diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index d519bc7d9115..d65fd9a3085b 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -15,7 +15,6 @@ import ( "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" - "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -141,33 +140,15 @@ func (i *inspector) inspect(namesOrIDs []string) error { data = append(data, ctrData[i]) } case common.PodType: - for _, pod := range namesOrIDs { - i.podOptions.NameOrID = pod - podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) - if err != nil { - if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { - errs = []error{err} - } else { - return err - } - } else { - errs = nil - data = append(data, podData) - } + podData, allErrs, err := i.containerEngine.PodInspect(ctx, namesOrIDs, i.podOptions) + if err != nil { + return err } - if i.podOptions.Latest { // latest means there are no names in the namesOrID array - podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) - if err != nil { - if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { - errs = []error{err} - } else { - return err - } - } else { - errs = nil - data = append(data, podData) - } + errs = allErrs + for i := range podData { + data = append(data, podData[i]) } + case common.NetworkType: networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) if err != nil { @@ -282,14 +263,13 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte data = append(data, networkData[0]) continue } - i.podOptions.NameOrID = name - podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + + podData, errs, err := i.containerEngine.PodInspect(ctx, []string{name}, i.podOptions) if err != nil { - if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { - return nil, nil, err - } - } else { - data = append(data, podData) + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, podData[0]) continue } if len(errs) > 0 { diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 082e8d9a12b5..0dcfa4fbd010 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -1,23 +1,14 @@ package pods import ( - "context" - "errors" - "os" - "text/template" - - "github.com/containers/common/pkg/report" "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/inspect" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/spf13/cobra" ) -var ( - inspectOptions = entities.PodInspectOptions{} -) - var ( inspectDescription = `Display the configuration for a pod by name or id @@ -27,10 +18,12 @@ var ( Use: "inspect [options] POD [POD...]", Short: "Displays a pod configuration", Long: inspectDescription, - RunE: inspect, + RunE: inspectExec, ValidArgsFunction: common.AutocompletePods, Example: `podman pod inspect podID`, } + + inspectOpts = &entities.InspectOptions{} ) func init() { @@ -41,40 +34,13 @@ func init() { flags := inspectCmd.Flags() formatFlagName := "format" - flags.StringVarP(&inspectOptions.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") + flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.PodInspectReport{})) - validate.AddLatestFlag(inspectCmd, &inspectOptions.Latest) + validate.AddLatestFlag(inspectCmd, &inspectOpts.Latest) } -func inspect(cmd *cobra.Command, args []string) error { - if len(args) < 1 && !inspectOptions.Latest { - return errors.New("you must provide the name or id of a running pod") - } - if len(args) > 0 && inspectOptions.Latest { - return errors.New("--latest and containers cannot be used together") - } - - if !inspectOptions.Latest { - inspectOptions.NameOrID = args[0] - } - responses, err := registry.ContainerEngine().PodInspect(context.Background(), inspectOptions) - if err != nil { - return err - } - - if report.IsJSON(inspectOptions.Format) { - enc := json.NewEncoder(os.Stdout) - enc.SetIndent("", " ") - return enc.Encode(responses) - } - - // Cannot use report.New() as it enforces {{range .}} for OriginUser templates - tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs)) - format := report.NormalizeFormat(inspectOptions.Format) - tmpl, err = tmpl.Parse(format) - if err != nil { - return err - } - return tmpl.Execute(os.Stdout, *responses) +func inspectExec(cmd *cobra.Command, args []string) error { + inspectOpts.Type = common.PodType + return inspect.Inspect(args, *inspectOpts) } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 69adc97325bc..59ed3aa6164b 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -75,7 +75,7 @@ type ContainerEngine interface { PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) PodClone(ctx context.Context, podClone PodCloneOptions) (*PodCloneReport, error) PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) - PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) + PodInspect(ctx context.Context, namesOrID []string, options PodInspectOptions) ([]*PodInspectReport, []error, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) PodLogs(ctx context.Context, pod string, options PodLogsOptions) error PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index b672434d8e98..0921486d6143 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -441,9 +441,6 @@ type PodPSOptions struct { type PodInspectOptions struct { Latest bool - // Options for the API. - NameOrID string - Format string } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 03c8082c4903..7cc8a8a1880c 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -505,23 +505,49 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti return reports, nil } -func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { - var ( - pod *libpod.Pod - err error - ) - // Look up the pod. +func (ic *ContainerEngine) PodInspect(ctx context.Context, nameOrIDs []string, options entities.PodInspectOptions) ([]*entities.PodInspectReport, []error, error) { if options.Latest { - pod, err = ic.Libpod.GetLatestPod() - } else { - pod, err = ic.Libpod.LookupPod(options.NameOrID) - } - if err != nil { - return nil, fmt.Errorf("unable to look up requested container: %w", err) + pod, err := ic.Libpod.GetLatestPod() + if err != nil { + return nil, nil, err + } + inspect, err := pod.Inspect() + if err != nil { + return nil, nil, err + } + + return []*entities.PodInspectReport{ + { + InspectPodData: inspect, + }, + }, nil, nil } - inspect, err := pod.Inspect() - if err != nil { - return nil, err + + var errs []error + podReport := make([]*entities.PodInspectReport, 0, len(nameOrIDs)) + for _, name := range nameOrIDs { + pod, err := ic.Libpod.LookupPod(name) + if err != nil { + // ErrNoSuchPod is non-fatal, other errors will be + // treated as fatal. + if errors.Is(err, define.ErrNoSuchPod) { + errs = append(errs, fmt.Errorf("no such pod %s", name)) + continue + } + return nil, nil, err + } + + inspect, err := pod.Inspect() + if err != nil { + // ErrNoSuchPod is non-fatal, other errors will be + // treated as fatal. + if errors.Is(err, define.ErrNoSuchPod) { + errs = append(errs, fmt.Errorf("no such pod %s", name)) + continue + } + return nil, nil, err + } + podReport = append(podReport, &entities.PodInspectReport{InspectPodData: inspect}) } - return &entities.PodInspectReport{InspectPodData: inspect}, nil + return podReport, errs, nil } diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index bcbd32d1b8f9..1fa56b003565 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -3,10 +3,12 @@ package tunnel import ( "context" "errors" + "fmt" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings/pods" "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/util" ) @@ -223,14 +225,25 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, opts entities.PodPSOptions return pods.List(ic.ClientCtx, options) } -func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { - switch { - case options.Latest: - return nil, errors.New("latest is not supported") - case options.NameOrID == "": - return nil, errors.New("NameOrID must be specified") +func (ic *ContainerEngine) PodInspect(ctx context.Context, namesOrIDs []string, options entities.PodInspectOptions) ([]*entities.PodInspectReport, []error, error) { + var errs []error + podReport := make([]*entities.PodInspectReport, 0, len(namesOrIDs)) + for _, name := range namesOrIDs { + inspect, err := pods.Inspect(ic.ClientCtx, name, nil) + if err != nil { + errModel, ok := err.(*errorhandling.ErrorModel) + if !ok { + return nil, nil, err + } + if errModel.ResponseCode == 404 { + errs = append(errs, fmt.Errorf("no such pod %q", name)) + continue + } + return nil, nil, err + } + podReport = append(podReport, inspect) } - return pods.Inspect(ic.ClientCtx, options.NameOrID, nil) + return podReport, errs, nil } func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, opts entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 2d7c47a7fa18..d045b7620097 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -569,10 +569,11 @@ func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectCont // InspectPodToJSON takes the sessions output from a pod inspect and returns json func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData { - var i define.InspectPodData + var i []define.InspectPodData err := jsoniter.Unmarshal(s.Out.Contents(), &i) - Expect(err).To(BeNil()) - return i + Expect(err).ToNot(HaveOccurred()) + Expect(i).To(HaveLen(1)) + return i[0] } // InspectPodToJSON takes the sessions output from an inspect and returns json diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 351317cc521d..cefdee40ac38 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -118,4 +118,21 @@ var _ = Describe("Podman pod inspect", func() { Expect(inspectOut.OutputToString()).To(ContainSubstring(macAddr)) }) + + It("podman inspect two pods", func() { + _, ec, podid1 := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + _, ec, podid2 := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"pod", "inspect", podid1, podid2}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(BeValidJSON()) + podData := inspect.InspectPodArrToJSON() + Expect(podData).To(HaveLen(2)) + Expect(podData[0]).To(HaveField("ID", podid1)) + Expect(podData[1]).To(HaveField("ID", podid2)) + }) })