This replaces the visitAll recursive function with a test that verifies that the option is set for all commands and subcommands, so that it doesn't have to be modified at runtime. We currently still have to loop over all functions for the setValidateArgs call, but that can be looked at separately. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package manifest
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/distribution/reference"
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/cli/cli/manifest/types"
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type inspectOptions struct {
|
|
ref string
|
|
list string
|
|
verbose bool
|
|
insecure bool
|
|
}
|
|
|
|
// NewInspectCommand creates a new `docker manifest inspect` command
|
|
func newInspectCommand(dockerCLI command.Cli) *cobra.Command {
|
|
var opts inspectOptions
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "inspect [OPTIONS] [MANIFEST_LIST] MANIFEST",
|
|
Short: "Display an image manifest, or manifest list",
|
|
Args: cli.RequiresRangeArgs(1, 2),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
switch len(args) {
|
|
case 1:
|
|
opts.ref = args[0]
|
|
case 2:
|
|
opts.list = args[0]
|
|
opts.ref = args[1]
|
|
}
|
|
return runInspect(cmd.Context(), dockerCLI, opts)
|
|
},
|
|
DisableFlagsInUseLine: true,
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
flags.BoolVar(&opts.insecure, "insecure", false, "Allow communication with an insecure registry")
|
|
flags.BoolVarP(&opts.verbose, "verbose", "v", false, "Output additional info including layers and platform")
|
|
return cmd
|
|
}
|
|
|
|
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
|
|
namedRef, err := normalizeReference(opts.ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If list reference is provided, display the local manifest in a list
|
|
if opts.list != "" {
|
|
listRef, err := normalizeReference(opts.list)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
imageManifest, err := newManifestStore(dockerCli).Get(listRef, namedRef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return printManifest(dockerCli, imageManifest, opts)
|
|
}
|
|
|
|
// Try a local manifest list first
|
|
localManifestList, err := newManifestStore(dockerCli).GetList(namedRef)
|
|
if err == nil {
|
|
return printManifestList(dockerCli, namedRef, localManifestList, opts)
|
|
}
|
|
|
|
// Next try a remote manifest
|
|
registryClient := newRegistryClient(dockerCli, opts.insecure)
|
|
imageManifest, err := registryClient.GetManifest(ctx, namedRef)
|
|
if err == nil {
|
|
return printManifest(dockerCli, imageManifest, opts)
|
|
}
|
|
|
|
// Finally try a remote manifest list
|
|
manifestList, err := registryClient.GetManifestList(ctx, namedRef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return printManifestList(dockerCli, namedRef, manifestList, opts)
|
|
}
|
|
|
|
func printManifest(dockerCli command.Cli, manifest types.ImageManifest, opts inspectOptions) error {
|
|
buffer := new(bytes.Buffer)
|
|
if !opts.verbose {
|
|
_, raw, err := manifest.Payload()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Indent(buffer, raw, "", "\t"); err != nil {
|
|
return err
|
|
}
|
|
_, _ = fmt.Fprintln(dockerCli.Out(), buffer.String())
|
|
return nil
|
|
}
|
|
jsonBytes, err := json.MarshalIndent(manifest, "", "\t")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = dockerCli.Out().Write(append(jsonBytes, '\n'))
|
|
return nil
|
|
}
|
|
|
|
func printManifestList(dockerCli command.Cli, namedRef reference.Named, list []types.ImageManifest, opts inspectOptions) error {
|
|
if !opts.verbose {
|
|
targetRepo := reference.TrimNamed(namedRef)
|
|
|
|
manifests := make([]manifestlist.ManifestDescriptor, 0, len(list))
|
|
// More than one response. This is a manifest list.
|
|
for _, img := range list {
|
|
mfd, err := buildManifestDescriptor(targetRepo, img)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to assemble ManifestDescriptor: %w", err)
|
|
}
|
|
manifests = append(manifests, mfd)
|
|
}
|
|
deserializedML, err := manifestlist.FromDescriptors(manifests)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
jsonBytes, err := deserializedML.MarshalJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = fmt.Fprintln(dockerCli.Out(), string(jsonBytes))
|
|
return nil
|
|
}
|
|
jsonBytes, err := json.MarshalIndent(list, "", "\t")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = dockerCli.Out().Write(append(jsonBytes, '\n'))
|
|
return nil
|
|
}
|