package main import ( "context" "strings" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) const ( inspectTypeContainer = "container" inspectTypeImage = "image" inspectAll = "all" ) var ( inspectCommand cliconfig.InspectValues inspectDescription = `This displays the low-level information on containers and images identified by name or ID. If given a name that matches both a container and an image, this command inspects the container. By default, this will render all results in a JSON array.` _inspectCommand = cobra.Command{ Use: "inspect [flags] CONTAINER | IMAGE", Short: "Display the configuration of a container or image", Long: inspectDescription, RunE: func(cmd *cobra.Command, args []string) error { inspectCommand.InputArgs = args inspectCommand.GlobalFlags = MainGlobalOpts inspectCommand.Remote = remoteclient return inspectCmd(&inspectCommand) }, Example: `podman inspect alpine podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, } ) func inspectInit(command *cliconfig.InspectValues) { command.SetHelpTemplate(HelpTemplate()) command.SetUsageTemplate(UsageTemplate()) flags := command.Flags() flags.StringVarP(&command.Format, "format", "f", "", "Change the output format to a Go template") // -t flag applicable only to 'podman inspect', not 'image/container inspect' ambiguous := strings.Contains(command.Use, "|") if ambiguous { flags.StringVarP(&command.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (image or container)") } if strings.Contains(command.Use, "CONTAINER") { containers_only := " (containers only)" if !ambiguous { containers_only = "" command.TypeObject = inspectTypeContainer } flags.BoolVarP(&command.Latest, "latest", "l", false, "Act on the latest container podman is aware of"+containers_only) flags.BoolVarP(&command.Size, "size", "s", false, "Display total file size"+containers_only) markFlagHiddenForRemoteClient("latest", flags) } else { command.TypeObject = inspectTypeImage } } func init() { inspectCommand.Command = &_inspectCommand inspectInit(&inspectCommand) } func inspectCmd(c *cliconfig.InspectValues) error { args := c.InputArgs inspectType := c.TypeObject latestContainer := c.Latest if len(args) == 0 && !latestContainer { return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name") } if len(args) > 0 && latestContainer { return errors.Errorf("you cannot provide additional arguments with --latest") } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.DeferredShutdown(false) if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) { return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll) } outputFormat := c.Format if strings.Contains(outputFormat, "{{.Id}}") { outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) } // These fields were renamed, so we need to provide backward compat for // the old names. if strings.Contains(outputFormat, ".Src") { outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1) } if strings.Contains(outputFormat, ".Dst") { outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1) } if strings.Contains(outputFormat, ".ImageID") { outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1) } if latestContainer { lc, err := runtime.GetLatestContainer() if err != nil { return err } args = append(args, lc.ID()) inspectType = inspectTypeContainer } inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType) if iterateErr != nil { return iterateErr } var out formats.Writer if outputFormat != "" && outputFormat != formats.JSONString { //template out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat} } else { // default is json output out = formats.JSONStructArray{Output: inspectedObjects} } return out.Out() } // func iterateInput iterates the images|containers the user has requested and returns the inspect data and error func iterateInput(ctx context.Context, size bool, args []string, runtime *adapter.LocalRuntime, inspectType string) ([]interface{}, error) { var ( data interface{} inspectedItems []interface{} inspectError error ) for _, input := range args { switch inspectType { case inspectTypeContainer: ctr, err := runtime.LookupContainer(input) if err != nil { inspectError = errors.Wrapf(err, "error looking up container %q", input) break } data, err = ctr.Inspect(size) if err != nil { inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID()) break } case inspectTypeImage: image, err := runtime.NewImageFromLocal(input) if err != nil { inspectError = errors.Wrapf(err, "error getting image %q", input) break } data, err = image.Inspect(ctx) if err != nil { inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID()) break } case inspectAll: ctr, err := runtime.LookupContainer(input) if err != nil { image, err := runtime.NewImageFromLocal(input) if err != nil { inspectError = errors.Wrapf(err, "error getting image %q", input) break } data, err = image.Inspect(ctx) if err != nil { inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID()) break } } else { data, err = ctr.Inspect(size) if err != nil { inspectError = errors.Wrapf(err, "error inspecting container %s", ctr.ID()) break } } } if inspectError == nil { inspectedItems = append(inspectedItems, data) } } return inspectedItems, inspectError }