Skip to content

Commit

Permalink
fix podman pod inspect to support multiple pods
Browse files Browse the repository at this point in the history
Just like the other inspect commands `podman pod inspect p1 p2` should
return the json for both.

To correctly implement this we follow the container inspect logic, this
allows use to reuse the global inspect command.
Note: This may be breaking since we now return a json array of pods and
no longer a single pod.

Fixes containers#15674

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
  • Loading branch information
Luap99 committed Sep 7, 2022
1 parent 7946628 commit b86b0dd
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 106 deletions.
46 changes: 13 additions & 33 deletions cmd/podman/inspect/inspect.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
52 changes: 9 additions & 43 deletions 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
Expand All @@ -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() {
Expand All @@ -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)
}
2 changes: 1 addition & 1 deletion pkg/domain/entities/engine_container.go
Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions pkg/domain/entities/pods.go
Expand Up @@ -441,9 +441,6 @@ type PodPSOptions struct {
type PodInspectOptions struct {
Latest bool

// Options for the API.
NameOrID string

Format string
}

Expand Down
58 changes: 42 additions & 16 deletions pkg/domain/infra/abi/pods.go
Expand Up @@ -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
}
27 changes: 20 additions & 7 deletions pkg/domain/infra/tunnel/pods.go
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/common_test.go
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions test/e2e/pod_inspect_test.go
Expand Up @@ -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))
})
})

0 comments on commit b86b0dd

Please sign in to comment.