From 46e3b2efb84580cc12b0bc5ad0863957b88ae202 Mon Sep 17 00:00:00 2001
From: Jhon Honce <jhonce@redhat.com>
Date: Tue, 31 Mar 2020 17:02:10 -0700
Subject: V2 podman inspect

* Expose podman container inspect
* Expose podman image inspect

Signed-off-by: Jhon Honce <jhonce@redhat.com>
---
 cmd/podmanV2/common/inspect.go     |  18 +++++
 cmd/podmanV2/containers/inspect.go |  32 +++++----
 cmd/podmanV2/images/inspect.go     | 141 +++++++++++++++++--------------------
 cmd/podmanV2/images/list.go        |   2 +-
 cmd/podmanV2/inspect.go            |  62 ++++++++++++++++
 cmd/podmanV2/registry/registry.go  |  17 +++--
 cmd/podmanV2/root.go               |   5 +-
 cmd/podmanV2/system/system.go      |   2 +-
 8 files changed, 173 insertions(+), 106 deletions(-)
 create mode 100644 cmd/podmanV2/common/inspect.go
 create mode 100644 cmd/podmanV2/inspect.go

(limited to 'cmd')

diff --git a/cmd/podmanV2/common/inspect.go b/cmd/podmanV2/common/inspect.go
new file mode 100644
index 000000000..dfc6fe679
--- /dev/null
+++ b/cmd/podmanV2/common/inspect.go
@@ -0,0 +1,18 @@
+package common
+
+import (
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/spf13/cobra"
+)
+
+// AddInspectFlagSet takes a command and adds the inspect flags and returns an InspectOptions object
+// Since this cannot live in `package main` it lives here until a better home is found
+func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
+	opts := entities.InspectOptions{}
+
+	flags := cmd.Flags()
+	flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size")
+	flags.StringVarP(&opts.Format, "format", "f", "", "Change the output format to a Go template")
+
+	return &opts
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
index 648289f0b..3147426cb 100644
--- a/cmd/podmanV2/containers/inspect.go
+++ b/cmd/podmanV2/containers/inspect.go
@@ -7,9 +7,11 @@ import (
 	"strings"
 	"text/template"
 
+	"github.com/containers/libpod/cmd/podmanV2/common"
 	"github.com/containers/libpod/cmd/podmanV2/registry"
+
 	"github.com/containers/libpod/pkg/domain/entities"
-	jsoniter "github.com/json-iterator/go"
+	json "github.com/json-iterator/go"
 	"github.com/spf13/cobra"
 )
 
@@ -24,10 +26,7 @@ var (
 		Example: `podman container inspect myCtr
   podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
 	}
-)
-
-var (
-	inspectOptions entities.ContainerInspectOptions
+	inspectOpts *entities.InspectOptions
 )
 
 func init() {
@@ -36,29 +35,29 @@ func init() {
 		Command: inspectCmd,
 		Parent:  containerCmd,
 	})
+	inspectOpts = common.AddInspectFlagSet(inspectCmd)
 	flags := inspectCmd.Flags()
-	flags.StringVarP(&inspectOptions.Format, "format", "f", "", "Change the output format to a Go template")
-	flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
-	flags.BoolVarP(&inspectOptions.Size, "size", "s", false, "Display total file size")
-	if registry.IsRemote() {
-		_ = flags.MarkHidden("latest")
+
+	if !registry.IsRemote() {
+		flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
 	}
+
 }
 
 func inspect(cmd *cobra.Command, args []string) error {
-	responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, inspectOptions)
+	responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, *inspectOpts)
 	if err != nil {
 		return err
 	}
-	if inspectOptions.Format == "" {
-		b, err := jsoniter.MarshalIndent(responses, "", "  ")
+	if inspectOpts.Format == "" {
+		b, err := json.MarshalIndent(responses, "", "  ")
 		if err != nil {
 			return err
 		}
 		fmt.Println(string(b))
 		return nil
 	}
-	format := inspectOptions.Format
+	format := inspectOpts.Format
 	if !strings.HasSuffix(format, "\n") {
 		format += "\n"
 	}
@@ -73,3 +72,8 @@ func inspect(cmd *cobra.Command, args []string) error {
 	}
 	return nil
 }
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+	inspectOpts = options
+	return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
index f8fd44571..d7f6b0ee1 100644
--- a/cmd/podmanV2/images/inspect.go
+++ b/cmd/podmanV2/images/inspect.go
@@ -1,71 +1,44 @@
 package images
 
 import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"os"
 	"strings"
+	"text/tabwriter"
+	"text/template"
 
 	"github.com/containers/buildah/pkg/formats"
+	"github.com/containers/libpod/cmd/podmanV2/common"
 	"github.com/containers/libpod/cmd/podmanV2/registry"
 	"github.com/containers/libpod/pkg/domain/entities"
-	"github.com/containers/libpod/pkg/util"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 )
 
 var (
-	inspectOpts = entities.ImageInspectOptions{}
-
 	// Command: podman image _inspect_
 	inspectCmd = &cobra.Command{
 		Use:     "inspect [flags] IMAGE",
 		Short:   "Display the configuration of an image",
 		Long:    `Displays the low-level information on an image identified by name or ID.`,
-		PreRunE: populateEngines,
-		RunE:    imageInspect,
+		RunE:    inspect,
 		Example: `podman image inspect alpine`,
 	}
-
-	containerEngine entities.ContainerEngine
+	inspectOpts *entities.InspectOptions
 )
 
-// Inspect is unique in that it needs both an ImageEngine and a ContainerEngine
-func populateEngines(cmd *cobra.Command, args []string) (err error) {
-	// Populate registry.ImageEngine
-	err = preRunE(cmd, args)
-	if err != nil {
-		return
-	}
-
-	// Populate registry.ContainerEngine
-	containerEngine, err = registry.NewContainerEngine(cmd, args)
-	return
-}
-
 func init() {
 	registry.Commands = append(registry.Commands, registry.CliCommand{
 		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
 		Command: inspectCmd,
 		Parent:  imageCmd,
 	})
-
-	flags := inspectCmd.Flags()
-	flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
-	flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size")
-	flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template")
-
-	if registry.EngineOptions.EngineMode == entities.ABIMode {
-		// TODO: This is the same as V1.  We could skip creating the flag altogether in V2...
-		_ = flags.MarkHidden("latest")
-	}
+	inspectOpts = common.AddInspectFlagSet(inspectCmd)
 }
 
-const (
-	inspectTypeContainer = "container"
-	inspectTypeImage     = "image"
-	inspectAll           = "all"
-)
-
-func imageInspect(cmd *cobra.Command, args []string) error {
-	inspectType := inspectTypeImage
+func inspect(cmd *cobra.Command, args []string) error {
 	latestContainer := inspectOpts.Latest
 
 	if len(args) == 0 && !latestContainer {
@@ -76,49 +49,61 @@ func imageInspect(cmd *cobra.Command, args []string) error {
 		return errors.Errorf("you cannot provide additional arguments with --latest")
 	}
 
-	if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
-		return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
+	results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts)
+	if err != nil {
+		return err
 	}
 
-	outputFormat := inspectOpts.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 len(results.Images) > 0 {
+		if inspectOpts.Format == "" {
+			buf, err := json.MarshalIndent(results.Images, "", "  ")
+			if err != nil {
+				return err
+			}
+			fmt.Println(string(buf))
+
+			for id, e := range results.Errors {
+				fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
+			}
+			return nil
+		}
+
+		row := inspectFormat(inspectOpts.Format)
+		format := "{{range . }}" + row + "{{end}}"
+		tmpl, err := template.New("inspect").Parse(format)
+		if err != nil {
+			return err
+		}
+
+		w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+		defer func() { _ = w.Flush() }()
+		err = tmpl.Execute(w, results)
+		if err != nil {
+			return err
+		}
 	}
-	if strings.Contains(outputFormat, ".Dst") {
-		outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
-	}
-	if strings.Contains(outputFormat, ".ImageID") {
-		outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
+
+	for id, e := range results.Errors {
+		fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
 	}
-	_ = outputFormat
-	// if latestContainer {
-	// 	lc, err := ctnrRuntime.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()
 	return nil
 }
+
+func inspectFormat(row string) string {
+	r := strings.NewReplacer("{{.Id}}", formats.IDString,
+		".Src", ".Source",
+		".Dst", ".Destination",
+		".ImageID", ".Image",
+	)
+	row = r.Replace(row)
+
+	if !strings.HasSuffix(row, "\n") {
+		row += "\n"
+	}
+	return row
+}
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+	inspectOpts = options
+	return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
index 9a5b47299..2d6cb3596 100644
--- a/cmd/podmanV2/images/list.go
+++ b/cmd/podmanV2/images/list.go
@@ -152,7 +152,7 @@ func writeTemplate(imageS []*entities.ImageSummary, err error) error {
 	hdr, row := imageListFormat(listFlag)
 	format := hdr + "{{range . }}" + row + "{{end}}"
 
-	tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
+	tmpl := template.Must(template.New("list").Funcs(report.PodmanTemplateFuncs()).Parse(format))
 	w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
 	defer w.Flush()
 	return tmpl.Execute(w, imgs)
diff --git a/cmd/podmanV2/inspect.go b/cmd/podmanV2/inspect.go
new file mode 100644
index 000000000..4975cf632
--- /dev/null
+++ b/cmd/podmanV2/inspect.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/containers/libpod/cmd/podmanV2/common"
+	"github.com/containers/libpod/cmd/podmanV2/containers"
+	"github.com/containers/libpod/cmd/podmanV2/images"
+	"github.com/containers/libpod/cmd/podmanV2/registry"
+	"github.com/containers/libpod/pkg/domain/entities"
+	"github.com/spf13/cobra"
+)
+
+// Inspect is one of the out layer commands in that it operates on images/containers/...
+
+var (
+	inspectOpts *entities.InspectOptions
+
+	// Command: podman _inspect_ Object_ID
+	inspectCmd = &cobra.Command{
+		Use:              "inspect [flags] {CONTAINER_ID | IMAGE_ID}",
+		Args:             cobra.ExactArgs(1),
+		Short:            "Display the configuration of object denoted by ID",
+		Long:             "Displays the low-level information on an object identified by name or ID",
+		TraverseChildren: true,
+		RunE:             inspect,
+	}
+)
+
+func init() {
+	registry.Commands = append(registry.Commands, registry.CliCommand{
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+		Command: inspectCmd,
+	})
+	inspectOpts = common.AddInspectFlagSet(inspectCmd)
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+	ie, err := registry.NewImageEngine(cmd, args)
+	if err != nil {
+		return err
+	}
+
+	if found, err := ie.Exists(context.Background(), args[0]); err != nil {
+		return err
+	} else if found.Value {
+		return images.Inspect(cmd, args, inspectOpts)
+	}
+
+	ce, err := registry.NewContainerEngine(cmd, args)
+	if err != nil {
+		return err
+	}
+
+	if found, err := ce.ContainerExists(context.Background(), args[0]); err != nil {
+		return err
+	} else if found.Value {
+		return containers.Inspect(cmd, args, inspectOpts)
+	}
+	return fmt.Errorf("%s not found on system", args[0])
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index 5cdb8a840..401f82718 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -3,7 +3,6 @@ package registry
 import (
 	"context"
 
-	"github.com/containers/libpod/libpod/define"
 	"github.com/containers/libpod/pkg/domain/entities"
 	"github.com/containers/libpod/pkg/domain/infra"
 	"github.com/pkg/errors"
@@ -18,24 +17,24 @@ type CliCommand struct {
 	Parent  *cobra.Command
 }
 
-var (
-	Commands []CliCommand
+const ExecErrorCodeGeneric = 125
 
-	imageEngine     entities.ImageEngine
-	containerEngine entities.ContainerEngine
+var (
 	cliCtx          context.Context
+	containerEngine entities.ContainerEngine
+	exitCode        = ExecErrorCodeGeneric
+	imageEngine     entities.ImageEngine
 
+	Commands      []CliCommand
 	EngineOptions entities.EngineOptions
-
-	ExitCode = define.ExecErrorCodeGeneric
 )
 
 func SetExitCode(code int) {
-	ExitCode = code
+	exitCode = code
 }
 
 func GetExitCode() int {
-	return ExitCode
+	return exitCode
 }
 
 // HelpTemplate returns the help template for podman commands
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index cb4cb4e00..6fc12f57e 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -7,7 +7,6 @@ import (
 	"path"
 
 	"github.com/containers/libpod/cmd/podmanV2/registry"
-	"github.com/containers/libpod/libpod/define"
 	"github.com/containers/libpod/pkg/domain/entities"
 	"github.com/containers/libpod/version"
 	"github.com/sirupsen/logrus"
@@ -88,8 +87,8 @@ func Execute() {
 	o := registry.NewOptions(rootCmd.Context(), &registry.EngineOptions)
 	if err := rootCmd.ExecuteContext(o); err != nil {
 		fmt.Fprintln(os.Stderr, "Error:", err.Error())
-	} else if registry.GetExitCode() == define.ExecErrorCodeGeneric {
-		// The exitCode modified from define.ExecErrorCodeGeneric,
+	} else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
+		// The exitCode modified from registry.ExecErrorCodeGeneric,
 		// indicates an application
 		// running inside of a container failed, as opposed to the
 		// podman command failed.  Must exit with that exit code
diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go
index 30ed328e8..4e805c7bd 100644
--- a/cmd/podmanV2/system/system.go
+++ b/cmd/podmanV2/system/system.go
@@ -1,4 +1,4 @@
-package images
+package system
 
 import (
 	"github.com/containers/libpod/cmd/podmanV2/registry"
-- 
cgit v1.2.3-54-g00ecf