Skip to content

Commit

Permalink
Merge pull request #21559 from smarterclayton/info_manifestlist
Browse files Browse the repository at this point in the history
Ensure `oc image info` works with manifest lists
  • Loading branch information
openshift-merge-robot authored Dec 1, 2018
2 parents da6c897 + 2e81ea0 commit c7c40de
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 15 deletions.
19 changes: 18 additions & 1 deletion pkg/oc/cli/image/info/info.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package info

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand All @@ -11,6 +12,7 @@ import (
"time"

"github.com/docker/distribution"
"github.com/docker/distribution/manifest/manifestlist"
units "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -72,6 +74,9 @@ func (o *InfoOptions) Complete(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("info expects at least one argument, an image pull spec")
}
if err := o.FilterOptions.Validate(); err != nil {
return err
}
o.Images = args
return nil
}
Expand Down Expand Up @@ -107,11 +112,23 @@ func (o *InfoOptions) Run() error {
return err
}

srcManifest, srcDigest, _, err := imagemanifest.FirstManifest(ctx, src, repo, o.FilterOptions.Include)
srcManifest, srcDigest, _, err := imagemanifest.FirstManifest(ctx, src, repo, o.FilterOptions.IncludeAll)
if err != nil {
return fmt.Errorf("unable to read image %s: %v", location, err)
}

switch t := srcManifest.(type) {
case *manifestlist.DeserializedManifestList:
buf := &bytes.Buffer{}
w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0)
fmt.Fprintf(w, " OS\tDIGEST\n")
for _, manifest := range t.Manifests {
fmt.Fprintf(w, " %s\t%s\n", imagemanifest.PlatformSpecString(manifest.Platform), manifest.Digest)
}
w.Flush()
return fmt.Errorf("the image is a manifest list and contains multiple images - use --filter-by-os to select from:\n\n%s\n", buf.String())
}

imageConfig, layers, err := imagemanifest.ManifestToImageConfig(ctx, srcManifest, repo.Blobs(ctx), location)
if err != nil {
return fmt.Errorf("unable to parse image %s: %v", location, err)
Expand Down
37 changes: 23 additions & 14 deletions pkg/oc/cli/image/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,12 @@ type FilterOptions struct {

// Bind adds the options to the flag set.
func (o *FilterOptions) Bind(flags *pflag.FlagSet) {
flags.StringVar(&o.FilterByOS, "filter-by-os", o.FilterByOS, "A regular expression to control which images are mirrored. Images will be passed as '<platform>/<architecture>[/<variant>]'.")
flags.StringVar(&o.FilterByOS, "filter-by-os", o.FilterByOS, "A regular expression to control which images are considered when multiple variants are available. Images will be passed as '<platform>/<architecture>[/<variant>]'.")
}

// Complete checks whether the flags are ready for use.
func (o *FilterOptions) Complete(flags *pflag.FlagSet) error {
// Validate checks whether the flags are ready for use.
func (o *FilterOptions) Validate() error {
pattern := o.FilterByOS
if len(pattern) == 0 && !flags.Changed("filter-by-os") {
o.DefaultOSFilter = true
pattern = regexp.QuoteMeta(fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH))
}
if len(pattern) > 0 {
re, err := regexp.Compile(pattern)
if err != nil {
Expand All @@ -56,6 +52,16 @@ func (o *FilterOptions) Complete(flags *pflag.FlagSet) error {
return nil
}

// Complete performs defaulting by OS.
func (o *FilterOptions) Complete(flags *pflag.FlagSet) error {
pattern := o.FilterByOS
if len(pattern) == 0 && !flags.Changed("filter-by-os") {
o.DefaultOSFilter = true
o.FilterByOS = regexp.QuoteMeta(fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH))
}
return o.Validate()
}

// Include returns true if the provided manifest should be included, or the first image if the user didn't alter the
// default selection and there is only one image.
func (o *FilterOptions) Include(d *manifestlist.ManifestDescriptor, hasMultiple bool) bool {
Expand All @@ -65,21 +71,24 @@ func (o *FilterOptions) Include(d *manifestlist.ManifestDescriptor, hasMultiple
if o.DefaultOSFilter && !hasMultiple {
return true
}
if len(d.Platform.Variant) > 0 {
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s/%s", d.Platform.OS, d.Platform.Architecture, d.Platform.Variant))
s := PlatformSpecString(d.Platform)
return o.OSFilter.MatchString(s)
}

func PlatformSpecString(platform manifestlist.PlatformSpec) string {
if len(platform.Variant) > 0 {
return fmt.Sprintf("%s/%s/%s", platform.OS, platform.Architecture, platform.Variant)
}
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s", d.Platform.OS, d.Platform.Architecture))
return fmt.Sprintf("%s/%s", platform.OS, platform.Architecture)
}

// IncludeAll returns true if the provided manifest matches the filter, or all if there was no filter.
func (o *FilterOptions) IncludeAll(d *manifestlist.ManifestDescriptor, hasMultiple bool) bool {
if o.OSFilter == nil {
return true
}
if len(d.Platform.Variant) > 0 {
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s/%s", d.Platform.OS, d.Platform.Architecture, d.Platform.Variant))
}
return o.OSFilter.MatchString(fmt.Sprintf("%s/%s", d.Platform.OS, d.Platform.Architecture))
s := PlatformSpecString(d.Platform)
return o.OSFilter.MatchString(s)
}

type FilterFunc func(*manifestlist.ManifestDescriptor, bool) bool
Expand Down

0 comments on commit c7c40de

Please sign in to comment.