Merge pull request #15675 from Luap99/pod-inspect

fix podman pod inspect to support multiple pods
This commit is contained in:
OpenShift Merge Robot
2022-09-08 16:36:08 +02:00
committed by GitHub
9 changed files with 119 additions and 126 deletions

View File

@@ -11,6 +11,10 @@ const (
NetworkType = "network" NetworkType = "network"
// PodType is the pod type. // PodType is the pod type.
PodType = "pod" 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 is the volume type
VolumeType = "volume" VolumeType = "volume"
) )

View File

@@ -15,7 +15,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate" "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/containers/podman/v4/pkg/domain/entities"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -55,18 +54,10 @@ type inspector struct {
containerEngine entities.ContainerEngine containerEngine entities.ContainerEngine
imageEngine entities.ImageEngine imageEngine entities.ImageEngine
options entities.InspectOptions options entities.InspectOptions
podOptions entities.PodInspectOptions
} }
// newInspector creates a new inspector based on the specified options. // newInspector creates a new inspector based on the specified options.
func newInspector(options entities.InspectOptions) (*inspector, error) { 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.Type == common.ImageType {
if options.Latest { if options.Latest {
return nil, fmt.Errorf("latest is not supported for type %q", common.ImageType) return nil, fmt.Errorf("latest is not supported for type %q", common.ImageType)
@@ -78,15 +69,10 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
if options.Type == common.PodType && options.Size { if options.Type == common.PodType && options.Size {
return nil, fmt.Errorf("size is not supported for type %q", common.PodType) return nil, fmt.Errorf("size is not supported for type %q", common.PodType)
} }
podOpts := entities.PodInspectOptions{
Latest: options.Latest,
Format: options.Format,
}
return &inspector{ return &inspector{
containerEngine: registry.ContainerEngine(), containerEngine: registry.ContainerEngine(),
imageEngine: registry.ImageEngine(), imageEngine: registry.ImageEngine(),
options: options, options: options,
podOptions: podOpts,
}, nil }, nil
} }
@@ -140,34 +126,16 @@ func (i *inspector) inspect(namesOrIDs []string) error {
for i := range ctrData { for i := range ctrData {
data = append(data, ctrData[i]) data = append(data, ctrData[i])
} }
case common.PodType: case common.PodType, common.PodLegacyType:
for _, pod := range namesOrIDs { podData, allErrs, err := i.containerEngine.PodInspect(ctx, namesOrIDs, i.options)
i.podOptions.NameOrID = pod if err != nil {
podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) return err
if err != nil {
if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
errs = []error{err}
} else {
return err
}
} else {
errs = nil
data = append(data, podData)
}
} }
if i.podOptions.Latest { // latest means there are no names in the namesOrID array errs = allErrs
podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) for i := range podData {
if err != nil { data = append(data, podData[i])
if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) {
errs = []error{err}
} else {
return err
}
} else {
errs = nil
data = append(data, podData)
}
} }
case common.NetworkType: case common.NetworkType:
networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options)
if err != nil { if err != nil {
@@ -198,7 +166,14 @@ func (i *inspector) inspect(namesOrIDs []string) error {
var err error var err error
switch { switch {
case report.IsJSON(i.options.Format) || i.options.Format == "": 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: default:
// Landing here implies user has given a custom --format // Landing here implies user has given a custom --format
row := inspectNormalize(i.options.Format, tmpType) row := inspectNormalize(i.options.Format, tmpType)
@@ -221,7 +196,7 @@ func (i *inspector) inspect(namesOrIDs []string) error {
return nil return nil
} }
func printJSON(data []interface{}) error { func printJSON(data interface{}) error {
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
// by default, json marshallers will force utf=8 from // by default, json marshallers will force utf=8 from
// a string. this breaks healthchecks that use <,>, &&. // a string. this breaks healthchecks that use <,>, &&.
@@ -282,14 +257,13 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte
data = append(data, networkData[0]) data = append(data, networkData[0])
continue 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 err != nil {
if !strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { return nil, nil, err
return nil, nil, err }
} if len(errs) == 0 {
} else { data = append(data, podData[0])
data = append(data, podData)
continue continue
} }
if len(errs) > 0 { if len(errs) > 0 {

View File

@@ -1,23 +1,14 @@
package pods package pods
import ( 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/common"
"github.com/containers/podman/v4/cmd/podman/inspect"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate" "github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var (
inspectOptions = entities.PodInspectOptions{}
)
var ( var (
inspectDescription = `Display the configuration for a pod by name or id inspectDescription = `Display the configuration for a pod by name or id
@@ -27,10 +18,12 @@ var (
Use: "inspect [options] POD [POD...]", Use: "inspect [options] POD [POD...]",
Short: "Displays a pod configuration", Short: "Displays a pod configuration",
Long: inspectDescription, Long: inspectDescription,
RunE: inspect, RunE: inspectExec,
ValidArgsFunction: common.AutocompletePods, ValidArgsFunction: common.AutocompletePods,
Example: `podman pod inspect podID`, Example: `podman pod inspect podID`,
} }
inspectOpts = &entities.InspectOptions{}
) )
func init() { func init() {
@@ -41,40 +34,15 @@ func init() {
flags := inspectCmd.Flags() flags := inspectCmd.Flags()
formatFlagName := "format" 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{})) _ = 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 { func inspectExec(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !inspectOptions.Latest { // We need backwards compat with the old podman pod inspect behavior.
return errors.New("you must provide the name or id of a running pod") // https://github.com/containers/podman/pull/15675
} inspectOpts.Type = common.PodLegacyType
if len(args) > 0 && inspectOptions.Latest { return inspect.Inspect(args, *inspectOpts)
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)
} }

View File

@@ -75,7 +75,7 @@ type ContainerEngine interface {
PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error)
PodClone(ctx context.Context, podClone PodCloneOptions) (*PodCloneReport, error) PodClone(ctx context.Context, podClone PodCloneOptions) (*PodCloneReport, error)
PodExists(ctx context.Context, nameOrID string) (*BoolReport, 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) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
PodLogs(ctx context.Context, pod string, options PodLogsOptions) error PodLogs(ctx context.Context, pod string, options PodLogsOptions) error
PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)

View File

@@ -438,15 +438,6 @@ type PodPSOptions struct {
Sort string Sort string
} }
type PodInspectOptions struct {
Latest bool
// Options for the API.
NameOrID string
Format string
}
type PodInspectReport struct { type PodInspectReport struct {
*define.InspectPodData *define.InspectPodData
} }

View File

@@ -505,23 +505,49 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti
return reports, nil return reports, nil
} }
func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { func (ic *ContainerEngine) PodInspect(ctx context.Context, nameOrIDs []string, options entities.InspectOptions) ([]*entities.PodInspectReport, []error, error) {
var (
pod *libpod.Pod
err error
)
// Look up the pod.
if options.Latest { if options.Latest {
pod, err = ic.Libpod.GetLatestPod() pod, err := ic.Libpod.GetLatestPod()
} else { if err != nil {
pod, err = ic.Libpod.LookupPod(options.NameOrID) return nil, nil, err
}
inspect, err := pod.Inspect()
if err != nil {
return nil, nil, err
}
return []*entities.PodInspectReport{
{
InspectPodData: inspect,
},
}, nil, nil
} }
if err != nil {
return nil, fmt.Errorf("unable to look up requested container: %w", 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})
} }
inspect, err := pod.Inspect() return podReport, errs, nil
if err != nil {
return nil, err
}
return &entities.PodInspectReport{InspectPodData: inspect}, nil
} }

View File

@@ -3,10 +3,12 @@ package tunnel
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/bindings/pods" "github.com/containers/podman/v4/pkg/bindings/pods"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/util" "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) return pods.List(ic.ClientCtx, options)
} }
func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { func (ic *ContainerEngine) PodInspect(ctx context.Context, namesOrIDs []string, options entities.InspectOptions) ([]*entities.PodInspectReport, []error, error) {
switch { var errs []error
case options.Latest: podReport := make([]*entities.PodInspectReport, 0, len(namesOrIDs))
return nil, errors.New("latest is not supported") for _, name := range namesOrIDs {
case options.NameOrID == "": inspect, err := pods.Inspect(ic.ClientCtx, name, nil)
return nil, errors.New("NameOrID must be specified") 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) { func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, opts entities.PodStatsOptions) ([]*entities.PodStatsReport, error) {

View File

@@ -571,7 +571,7 @@ func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectCont
func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData { func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData {
var i define.InspectPodData var i define.InspectPodData
err := jsoniter.Unmarshal(s.Out.Contents(), &i) err := jsoniter.Unmarshal(s.Out.Contents(), &i)
Expect(err).To(BeNil()) Expect(err).ToNot(HaveOccurred())
return i return i
} }

View File

@@ -118,4 +118,21 @@ var _ = Describe("Podman pod inspect", func() {
Expect(inspectOut.OutputToString()).To(ContainSubstring(macAddr)) 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))
})
}) })