Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contrib/completions/bash/oc
Original file line number Diff line number Diff line change
Expand Up @@ -16045,6 +16045,10 @@ _oc_image_info()
two_word_flags+=("--filter-by-os")
local_nonpersistent_flags+=("--filter-by-os")
local_nonpersistent_flags+=("--filter-by-os=")
flags+=("--icsp-file=")
two_word_flags+=("--icsp-file")
local_nonpersistent_flags+=("--icsp-file")
local_nonpersistent_flags+=("--icsp-file=")
flags+=("--insecure")
local_nonpersistent_flags+=("--insecure")
flags+=("--output=")
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewCmdImage(f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra
{
Message: "View or copy images:",
Commands: []*cobra.Command{
info.NewInfo(streams),
info.NewInfo(f, streams),
mirror.NewCmdMirrorImage(streams),
},
},
Expand Down
38 changes: 24 additions & 14 deletions pkg/cli/image/info/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/openshift/library-go/pkg/image/registryclient"
"github.com/openshift/oc/pkg/cli/image/imagesource"
imagemanifest "github.com/openshift/oc/pkg/cli/image/manifest"
"github.com/openshift/oc/pkg/cli/image/strategy"
"github.com/openshift/oc/pkg/cli/image/workqueue"
)

Expand All @@ -38,7 +39,7 @@ func NewInfoOptions(streams genericclioptions.IOStreams) *InfoOptions {
}
}

func NewInfo(streams genericclioptions.IOStreams) *cobra.Command {
func NewInfo(f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewInfoOptions(streams)
cmd := &cobra.Command{
Use: "info IMAGE [...]",
Expand Down Expand Up @@ -68,8 +69,8 @@ func NewInfo(streams genericclioptions.IOStreams) *cobra.Command {

`),
Run: func(cmd *cobra.Command, args []string) {
kcmdutil.CheckErr(o.Complete(cmd, args))
kcmdutil.CheckErr(o.Validate())
kcmdutil.CheckErr(o.Complete(f, cmd, args))
kcmdutil.CheckErr(o.Validate(cmd))
kcmdutil.CheckErr(o.Run())
},
}
Expand All @@ -78,6 +79,8 @@ func NewInfo(streams genericclioptions.IOStreams) *cobra.Command {
o.SecurityOptions.Bind(flags)
flags.StringVarP(&o.Output, "output", "o", o.Output, "Print the image in an alternative format: json")
flags.StringVar(&o.FileDir, "dir", o.FileDir, "The directory on disk that file:// images will be read from.")
flags.StringVar(&o.ICSPFile, "icsp-file", o.ICSPFile, "Path to an ImageContentSourcePolicy file. If set, data from this file will be used to find alternative locations for images.")

return cmd
}

Expand All @@ -87,42 +90,45 @@ type InfoOptions struct {
SecurityOptions imagemanifest.SecurityOptions
FilterOptions imagemanifest.FilterOptions

Images []string

FileDir string

Output string
Images []string
FileDir string
Output string
ICSPFile string
}

func (o *InfoOptions) Complete(cmd *cobra.Command, args []string) error {
func (o *InfoOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("info expects at least one argument, an image pull spec")
}
o.Images = args
return nil
}

func (o *InfoOptions) Validate() error {
return o.FilterOptions.Validate()
return nil
}

func (o *InfoOptions) Run() error {
func (o *InfoOptions) Validate(cmd *cobra.Command) error {
if len(o.Images) == 0 {
return fmt.Errorf("must specify one or more images as arguments")
}
return o.FilterOptions.Validate()
}

func (o *InfoOptions) Run() error {
// cache the context
registryContext, err := o.SecurityOptions.Context()
if err != nil {
return err
}
if len(o.ICSPFile) > 0 {
registryContext = registryContext.WithAlternateBlobSourceStrategy(strategy.NewICSPOnErrorStrategy(o.ICSPFile))
}
opts := &imagesource.Options{
FileDir: o.FileDir,
Insecure: o.SecurityOptions.Insecure,
RegistryContext: registryContext,
}

hadError := false
icspWarned := false
for _, location := range o.Images {
sources, err := imagesource.ParseSourceReference(location, opts.ExpandWildcard)
if err != nil {
Expand All @@ -132,6 +138,10 @@ func (o *InfoOptions) Run() error {
if len(src.Ref.Tag) == 0 && len(src.Ref.ID) == 0 {
return fmt.Errorf("--from must point to an image ID or image tag")
}
if !icspWarned && len(o.ICSPFile) > 0 && len(src.Ref.Tag) > 0 {
fmt.Fprintf(o.ErrOut, "warning: --icsp-file only applies to images referenced by digest and will be ignored for tags\n")
icspWarned = true
}

var image *Image
retriever := &ImageRetriever{
Expand Down
80 changes: 80 additions & 0 deletions pkg/cli/image/strategy/explicit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package strategy

import (
"context"
"fmt"
"sync"

"k8s.io/klog/v2"

"github.com/openshift/library-go/pkg/image/reference"
"github.com/openshift/library-go/pkg/image/registryclient"
)

type explicitStrategy struct {
lock sync.Mutex

alternates map[reference.DockerImageReference][]reference.DockerImageReference
icspFile string
readICSPsFromFileFunc readICSPsFromFileFunc
}

var _ registryclient.AlternateBlobSourceStrategy = &explicitStrategy{}

// NewICSPExplicitStrategy returns ICSP alternate strategy which always reads
// alternate sources first rather than original requested.
func NewICSPExplicitStrategy(file string) registryclient.AlternateBlobSourceStrategy {
return &explicitStrategy{
icspFile: file,
alternates: make(map[reference.DockerImageReference][]reference.DockerImageReference),
readICSPsFromFileFunc: readICSPsFromFile,
}
}

func (s *explicitStrategy) FirstRequest(ctx context.Context, locator reference.DockerImageReference) (alternateRepositories []reference.DockerImageReference, err error) {
s.lock.Lock()
defer s.lock.Unlock()
if alternates, ok := s.alternates[locator]; ok {
return alternates, nil
}
alternates, err := s.resolve(ctx, locator)
if err != nil {
return nil, err
}
if len(alternates) == 0 {
return nil, fmt.Errorf("no alternative image references found for image: %s", locator.String())
}
s.alternates[locator] = alternates
return s.alternates[locator], nil

}

func (s *explicitStrategy) OnFailure(ctx context.Context, locator reference.DockerImageReference) (alternateRepositories []reference.DockerImageReference, err error) {
s.lock.Lock()
defer s.lock.Unlock()
if len(s.alternates) == 0 {
return nil, fmt.Errorf("no alternative image references found for image: %s", locator.String())
}
return s.alternates[locator], nil
}

// resolve gathers possible image sources for a given image
// gathered from ImageContentSourcePolicy objects and user-passed image.
// Will lookup from cluster or from ImageContentSourcePolicy file passed from user.
// Image reference of user-given image may be different from original in case of mirrored images.
func (s *explicitStrategy) resolve(ctx context.Context, imageRef reference.DockerImageReference) ([]reference.DockerImageReference, error) {
if len(s.icspFile) == 0 {
return nil, fmt.Errorf("no ImageContentSourceFile specified")
}
klog.V(5).Infof("Reading ICSP from file %s", s.icspFile)
icspList, err := s.readICSPsFromFileFunc(s.icspFile)
if err != nil {
return nil, err
}
// always add the original as the last reference
imageRefList, err := alternativeImageSources(imageRef, icspList, true)
if err != nil {
return nil, err
}
return imageRefList, nil
}
Loading