Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix podman pod inspect to support multiple pods #15675

Merged
merged 1 commit into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/podman/common/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const (
NetworkType = "network"
// PodType is the pod type.
PodType = "pod"
// PodLegacyType is the pod type for backwards compatibility with the old pod inspect code.
// This allows us to use the shared inspect code but still provide the correct output format
// when podman pod inspect was called.
PodLegacyType = "pod-legacy"
// VolumeType is the volume type
VolumeType = "volume"
)
72 changes: 23 additions & 49 deletions cmd/podman/inspect/inspect.go
Original file line number Diff line number Diff line change
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 @@ -55,18 +54,10 @@ type inspector struct {
containerEngine entities.ContainerEngine
imageEngine entities.ImageEngine
options entities.InspectOptions
podOptions entities.PodInspectOptions
}

// newInspector creates a new inspector based on the specified options.
func newInspector(options entities.InspectOptions) (*inspector, error) {
switch options.Type {
case common.ImageType, common.ContainerType, common.AllType, common.PodType, common.NetworkType, common.VolumeType:
// Valid types.
default:
return nil, fmt.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type,
common.ImageType, common.ContainerType, common.PodType, common.NetworkType, common.VolumeType, common.AllType)
}
if options.Type == common.ImageType {
if options.Latest {
return nil, fmt.Errorf("latest is not supported for type %q", common.ImageType)
Expand All @@ -78,15 +69,10 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
if options.Type == common.PodType && options.Size {
return nil, fmt.Errorf("size is not supported for type %q", common.PodType)
}
podOpts := entities.PodInspectOptions{
Latest: options.Latest,
Format: options.Format,
}
return &inspector{
containerEngine: registry.ContainerEngine(),
imageEngine: registry.ImageEngine(),
options: options,
podOptions: podOpts,
}, nil
}

Expand Down Expand Up @@ -140,34 +126,16 @@ func (i *inspector) inspect(namesOrIDs []string) error {
for i := range ctrData {
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)
}
case common.PodType, common.PodLegacyType:
podData, allErrs, err := i.containerEngine.PodInspect(ctx, namesOrIDs, i.options)
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 @@ -198,7 +166,14 @@ func (i *inspector) inspect(namesOrIDs []string) error {
var err error
switch {
case report.IsJSON(i.options.Format) || i.options.Format == "":
err = printJSON(data)
if i.options.Type == common.PodLegacyType && len(data) == 1 {
// We need backwards compat with the old podman pod inspect behavior.
// https://github.com/containers/podman/pull/15675
// TODO (5.0): consider removing this to better match other commands.
err = printJSON(data[0])
} else {
err = printJSON(data)
}
default:
// Landing here implies user has given a custom --format
row := inspectNormalize(i.options.Format, tmpType)
Expand All @@ -221,7 +196,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
return nil
}

func printJSON(data []interface{}) error {
func printJSON(data interface{}) error {
enc := json.NewEncoder(os.Stdout)
// by default, json marshallers will force utf=8 from
// a string. this breaks healthchecks that use <,>, &&.
Expand Down Expand Up @@ -282,14 +257,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.options)
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
54 changes: 11 additions & 43 deletions cmd/podman/pods/inspect.go
Original file line number Diff line number Diff line change
@@ -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,15 @@ 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 {
// We need backwards compat with the old podman pod inspect behavior.
// https://github.com/containers/podman/pull/15675
inspectOpts.Type = common.PodLegacyType
return inspect.Inspect(args, *inspectOpts)
}
2 changes: 1 addition & 1 deletion pkg/domain/entities/engine_container.go
Original file line number Diff line number Diff line change
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 InspectOptions) ([]*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
9 changes: 0 additions & 9 deletions pkg/domain/entities/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,15 +438,6 @@ type PodPSOptions struct {
Sort string
}

type PodInspectOptions struct {
Latest bool

// Options for the API.
NameOrID string

Format string
}

type PodInspectReport struct {
*define.InspectPodData
}
Expand Down
58 changes: 42 additions & 16 deletions pkg/domain/infra/abi/pods.go
Original file line number Diff line number Diff line change
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.InspectOptions) ([]*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
Original file line number Diff line number Diff line change
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.InspectOptions) ([]*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
2 changes: 1 addition & 1 deletion test/e2e/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectCont
func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData {
var i define.InspectPodData
err := jsoniter.Unmarshal(s.Out.Contents(), &i)
Expect(err).To(BeNil())
Expect(err).ToNot(HaveOccurred())
return i
}

Expand Down
17 changes: 17 additions & 0 deletions test/e2e/pod_inspect_test.go
Original file line number Diff line number Diff line change
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))
})
})