github.com/cspotcode/docker-cli@v20.10.0-rc1.0.20201201121459-3faad7acc5b8+incompatible/cli/command/manifest/annotate.go (about) 1 package manifest 2 3 import ( 4 "fmt" 5 6 "github.com/docker/cli/cli" 7 "github.com/docker/cli/cli/command" 8 "github.com/docker/cli/cli/manifest/store" 9 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 10 "github.com/pkg/errors" 11 "github.com/spf13/cobra" 12 ) 13 14 type annotateOptions struct { 15 target string // the target manifest list name (also transaction ID) 16 image string // the manifest to annotate within the list 17 variant string // an architecture variant 18 os string 19 arch string 20 osFeatures []string 21 osVersion string 22 } 23 24 // NewAnnotateCommand creates a new `docker manifest annotate` command 25 func newAnnotateCommand(dockerCli command.Cli) *cobra.Command { 26 var opts annotateOptions 27 28 cmd := &cobra.Command{ 29 Use: "annotate [OPTIONS] MANIFEST_LIST MANIFEST", 30 Short: "Add additional information to a local image manifest", 31 Args: cli.ExactArgs(2), 32 RunE: func(cmd *cobra.Command, args []string) error { 33 opts.target = args[0] 34 opts.image = args[1] 35 return runManifestAnnotate(dockerCli, opts) 36 }, 37 } 38 39 flags := cmd.Flags() 40 41 flags.StringVar(&opts.os, "os", "", "Set operating system") 42 flags.StringVar(&opts.arch, "arch", "", "Set architecture") 43 flags.StringVar(&opts.osVersion, "os-version", "", "Set operating system version") 44 flags.StringSliceVar(&opts.osFeatures, "os-features", []string{}, "Set operating system feature") 45 flags.StringVar(&opts.variant, "variant", "", "Set architecture variant") 46 47 return cmd 48 } 49 50 func runManifestAnnotate(dockerCli command.Cli, opts annotateOptions) error { 51 targetRef, err := normalizeReference(opts.target) 52 if err != nil { 53 return errors.Wrapf(err, "annotate: error parsing name for manifest list %s", opts.target) 54 } 55 imgRef, err := normalizeReference(opts.image) 56 if err != nil { 57 return errors.Wrapf(err, "annotate: error parsing name for manifest %s", opts.image) 58 } 59 60 manifestStore := dockerCli.ManifestStore() 61 imageManifest, err := manifestStore.Get(targetRef, imgRef) 62 switch { 63 case store.IsNotFound(err): 64 return fmt.Errorf("manifest for image %s does not exist in %s", opts.image, opts.target) 65 case err != nil: 66 return err 67 } 68 69 // Update the mf 70 if imageManifest.Descriptor.Platform == nil { 71 imageManifest.Descriptor.Platform = new(ocispec.Platform) 72 } 73 if opts.os != "" { 74 imageManifest.Descriptor.Platform.OS = opts.os 75 } 76 if opts.arch != "" { 77 imageManifest.Descriptor.Platform.Architecture = opts.arch 78 } 79 for _, osFeature := range opts.osFeatures { 80 imageManifest.Descriptor.Platform.OSFeatures = appendIfUnique(imageManifest.Descriptor.Platform.OSFeatures, osFeature) 81 } 82 if opts.variant != "" { 83 imageManifest.Descriptor.Platform.Variant = opts.variant 84 } 85 if opts.osVersion != "" { 86 imageManifest.Descriptor.Platform.OSVersion = opts.osVersion 87 } 88 89 if !isValidOSArch(imageManifest.Descriptor.Platform.OS, imageManifest.Descriptor.Platform.Architecture) { 90 return errors.Errorf("manifest entry for image has unsupported os/arch combination: %s/%s", opts.os, opts.arch) 91 } 92 return manifestStore.Save(targetRef, imgRef, imageManifest) 93 } 94 95 func appendIfUnique(list []string, str string) []string { 96 for _, s := range list { 97 if s == str { 98 return list 99 } 100 } 101 return append(list, str) 102 }