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  }