diff options
author | Ashley Cui <acui@redhat.com> | 2020-10-15 09:23:26 -0400 |
---|---|---|
committer | Ashley Cui <acui@redhat.com> | 2020-10-27 14:42:54 -0400 |
commit | 61deec451f279cdc09b4415fe4988c2f8548e55a (patch) | |
tree | 091ff5d3f319d57cf788889a31d38d18e8e42cea | |
parent | 5c0849534d7bed20ec588d8ec0099a8b60df7e25 (diff) | |
download | podman-61deec451f279cdc09b4415fe4988c2f8548e55a.tar.gz podman-61deec451f279cdc09b4415fe4988c2f8548e55a.tar.bz2 podman-61deec451f279cdc09b4415fe4988c2f8548e55a.zip |
Add pod, volume, network to inspect package
podman inspect only had the capabilities to inspect containers and images. if a user wanted to inspect a pod, volume, or network, they would have to use `podman network inspect`, `podman pod inspect` etc. Docker's cli allowed users to inspect both volumes and networks using regular inspect, so this commit gives the user the functionality
If the inspect type is not specified using --type, the order of inspection is:
containers
images
volumes
networks
pods
meaning if container that has the same name as an image, podman inspect would return the container inspect.
To avoid duplicate code, podman network inspect and podman volume inspect now use the inspect package as well. Podman pod inspect does not because podman pod inspect returns a single json object while podman inspect can return multiple)
Signed-off-by: Ashley Cui <acui@redhat.com>
-rw-r--r-- | cmd/podman/inspect/inspect.go | 121 | ||||
-rw-r--r-- | cmd/podman/networks/inspect.go | 44 | ||||
-rw-r--r-- | cmd/podman/volumes/inspect.go | 40 | ||||
-rw-r--r-- | docs/source/markdown/podman-inspect.1.md | 25 | ||||
-rw-r--r-- | docs/source/markdown/podman.1.md | 2 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/networks.go | 14 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 4 | ||||
-rw-r--r-- | pkg/domain/entities/network.go | 5 | ||||
-rw-r--r-- | pkg/domain/entities/types.go | 2 | ||||
-rw-r--r-- | pkg/domain/entities/volumes.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/network.go | 15 | ||||
-rw-r--r-- | pkg/domain/infra/abi/volumes.go | 19 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/network.go | 20 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/volumes.go | 22 | ||||
-rw-r--r-- | test/e2e/common_test.go | 8 | ||||
-rw-r--r-- | test/e2e/inspect_test.go | 141 |
17 files changed, 360 insertions, 127 deletions
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index a62a68959..9c400d506 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -12,6 +12,7 @@ import ( "github.com/containers/common/pkg/report" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/cmd/podman/validate" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -19,12 +20,18 @@ import ( ) const ( - // ImageType is the image type. - ImageType = "image" - // ContainerType is the container type. - ContainerType = "container" // AllType can be of type ImageType or ContainerType. AllType = "all" + // ContainerType is the container type. + ContainerType = "container" + // ImageType is the image type. + ImageType = "image" + //NetworkType is the network type + NetworkType = "network" + //PodType is the pod type. + PodType = "pod" + //VolumeType is the volume type + VolumeType = "volume" ) // Pull in configured json library @@ -58,15 +65,16 @@ 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 ImageType, ContainerType, AllType: + case ImageType, ContainerType, AllType, PodType, NetworkType, VolumeType: // Valid types. default: - return nil, errors.Errorf("invalid type %q: must be %q, %q or %q", options.Type, ImageType, ContainerType, AllType) + return nil, errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } if options.Type == ImageType { if options.Latest { @@ -76,10 +84,18 @@ func newInspector(options entities.InspectOptions) (*inspector, error) { return nil, errors.Errorf("size is not supported for type %q", ImageType) } } + if options.Type == PodType && options.Size { + return nil, errors.Errorf("size is not supported for type %q", PodType) + } + podOpts := entities.PodInspectOptions{ + Latest: options.Latest, + Format: options.Format, + } return &inspector{ containerEngine: registry.ContainerEngine(), imageEngine: registry.ImageEngine(), options: options, + podOptions: podOpts, }, nil } @@ -91,17 +107,19 @@ func (i *inspector) inspect(namesOrIDs []string) error { ctx := context.Background() if len(namesOrIDs) == 0 { - if !i.options.Latest { - return errors.New("no containers or images specified") + if !i.options.Latest && !i.options.All { + return errors.New("no names or ids specified") } } tmpType := i.options.Type if i.options.Latest { if len(namesOrIDs) > 0 { - return errors.New("--latest and containers cannot be used together") + return errors.New("--latest and arguments cannot be used together") + } + if i.options.Type == AllType { + tmpType = ContainerType // -l works with --type=all, defaults to containertype } - tmpType = ContainerType // -l works with --type=all } // Inspect - note that AllType requires us to expensively query one-by-one. @@ -131,10 +149,57 @@ func (i *inspector) inspect(namesOrIDs []string) error { for i := range ctrData { data = append(data, ctrData[i]) } + case PodType: + for _, pod := range namesOrIDs { + i.podOptions.NameOrID = pod + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.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 + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + errs = []error{err} + } else { + return err + } + } else { + errs = nil + data = append(data, podData) + } + } + case NetworkType: + networkData, allErrs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range networkData { + data = append(data, networkData[i]) + } + case VolumeType: + volumeData, allErrs, err := i.containerEngine.VolumeInspect(ctx, namesOrIDs, i.options) + if err != nil { + return err + } + errs = allErrs + for i := range volumeData { + data = append(data, volumeData[i]) + } default: - return errors.Errorf("invalid type %q: must be %q, %q or %q", i.options.Type, ImageType, ContainerType, AllType) + return errors.Errorf("invalid type %q: must be %q, %q, %q, %q, %q, or %q", i.options.Type, ImageType, ContainerType, PodType, NetworkType, VolumeType, AllType) } - // Always print an empty array if data == nil { data = []interface{}{} @@ -195,11 +260,41 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte if err != nil { return nil, nil, err } + if len(errs) == 0 { + data = append(data, imgData[0]) + continue + } + volumeData, errs, err := i.containerEngine.VolumeInspect(ctx, []string{name}, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, volumeData[0]) + continue + } + networkData, errs, err := registry.ContainerEngine().NetworkInspect(ctx, namesOrIDs, i.options) + if err != nil { + return nil, nil, err + } + if len(errs) == 0 { + data = append(data, networkData[0]) + continue + } + i.podOptions.NameOrID = name + podData, err := i.containerEngine.PodInspect(ctx, i.podOptions) + if err != nil { + cause := errors.Cause(err) + if !strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()) { + return nil, nil, err + } + } else { + data = append(data, podData) + continue + } if len(errs) > 0 { allErrs = append(allErrs, errors.Errorf("no such object: %q", name)) continue } - data = append(data, imgData[0]) } return data, allErrs, nil } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index 47503fd4b..25ee7e574 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -1,13 +1,7 @@ package network import ( - "encoding/json" - "fmt" - "os" - "text/tabwriter" - "text/template" - - "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/spf13/cobra" @@ -23,10 +17,7 @@ var ( Example: `podman network inspect podman`, Args: cobra.MinimumNArgs(1), } -) - -var ( - networkInspectOptions entities.NetworkInspectOptions + inspectOpts *entities.InspectOptions ) func init() { @@ -35,36 +26,13 @@ func init() { Command: networkinspectCommand, Parent: networkCmd, }) + inspectOpts = new(entities.InspectOptions) flags := networkinspectCommand.Flags() - flags.StringVarP(&networkInspectOptions.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template") } func networkInspect(_ *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{}) - if err != nil { - return err - } - - switch { - case report.IsJSON(networkInspectOptions.Format) || networkInspectOptions.Format == "": - b, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return err - } - fmt.Println(string(b)) - default: - row := report.NormalizeFormat(networkInspectOptions.Format) - // There can be more than 1 in the inspect output. - row = "{{range . }}" + row + "{{end}}" - tmpl, err := template.New("inspectNetworks").Parse(row) - if err != nil { - return err - } + inspectOpts.Type = inspect.NetworkType + return inspect.Inspect(args, *inspectOpts) - w := tabwriter.NewWriter(os.Stdout, 8, 2, 0, ' ', 0) - defer w.Flush() - - return tmpl.Execute(w, responses) - } - return nil } diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index 732a67333..c6edcf809 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -1,16 +1,11 @@ package volumes import ( - "fmt" - "os" - "text/template" - - "github.com/containers/common/pkg/report" + "github.com/containers/podman/v2/cmd/podman/inspect" "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/net/context" ) var ( @@ -21,7 +16,7 @@ var ( Use: "inspect [options] VOLUME [VOLUME...]", Short: "Display detailed information on one or more volumes", Long: volumeInspectDescription, - RunE: inspect, + RunE: volumeInspect, Example: `podman volume inspect myvol podman volume inspect --all podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol`, @@ -29,8 +24,7 @@ var ( ) var ( - inspectOpts = entities.VolumeInspectOptions{} - inspectFormat string + inspectOpts *entities.InspectOptions ) func init() { @@ -39,34 +33,16 @@ func init() { Command: inspectCommand, Parent: volumeCmd, }) + inspectOpts = new(entities.InspectOptions) flags := inspectCommand.Flags() flags.BoolVarP(&inspectOpts.All, "all", "a", false, "Inspect all volumes") - flags.StringVarP(&inspectFormat, "format", "f", "json", "Format volume output using Go template") + flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format volume output using Go template") } -func inspect(cmd *cobra.Command, args []string) error { +func volumeInspect(cmd *cobra.Command, args []string) error { if (inspectOpts.All && len(args) > 0) || (!inspectOpts.All && len(args) < 1) { return errors.New("provide one or more volume names or use --all") } - responses, err := registry.ContainerEngine().VolumeInspect(context.Background(), args, inspectOpts) - if err != nil { - return err - } - - switch { - case report.IsJSON(inspectFormat), inspectFormat == "": - jsonOut, err := json.MarshalIndent(responses, "", " ") - if err != nil { - return errors.Wrapf(err, "error marshalling inspect JSON") - } - fmt.Println(string(jsonOut)) - default: - row := "{{range . }}" + report.NormalizeFormat(inspectFormat) + "{{end}}" - tmpl, err := template.New("volumeInspect").Parse(row) - if err != nil { - return err - } - return tmpl.Execute(os.Stdout, responses) - } - return nil + inspectOpts.Type = inspect.VolumeType + return inspect.Inspect(args, *inspectOpts) } diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md index a1dcd1a0e..eb7cf74c6 100644 --- a/docs/source/markdown/podman-inspect.1.md +++ b/docs/source/markdown/podman-inspect.1.md @@ -1,7 +1,7 @@ % podman-inspect(1) ## NAME -podman\-inspect - Display a container or image's configuration +podman\-inspect - Display a container, image, volume, network, or pod's configuration ## SYNOPSIS **podman inspect** [*options*] *name* [...] @@ -9,8 +9,9 @@ podman\-inspect - Display a container or image's configuration ## DESCRIPTION This displays the low-level information on containers and images identified by name or ID. By default, this will render -all results in a JSON array. If the container and image have the same name, this will return container JSON for -unspecified type. If a format is specified, the given template will be executed for each result. +all results in a JSON array. If the inspect type is all, the order of inspection is: containers, images, volumes, network, pods. + So, if a container has the same name as an image, then the container JSON will be returned, and so on. + If a format is specified, the given template will be executed for each result. For more inspection options, see: @@ -25,7 +26,7 @@ For more inspection options, see: **--type**, **-t**=*type* -Return JSON for the specified type. Type can be 'container', 'image' or 'all' (default: all) +Return JSON for the specified type. Type can be 'container', 'image', 'volume', 'network', 'pod', or 'all' (default: all) (Only meaningful when invoked as *podman inspect*) **--format**, **-f**=*format* @@ -38,6 +39,8 @@ The keys of the returned JSON can be used as the values for the --format flag (s Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. +This option can be used to inspect the latest pod created when used with --type pod + The latest option is not supported on the remote client or when invoked as *podman image inspect*. **--size**, **-s** @@ -148,6 +151,20 @@ podman container inspect --latest --format {{.EffectiveCaps}} [CAP_CHOWN CAP_DAC_OVERRIDE CAP_FSETID CAP_FOWNER CAP_MKNOD CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETFCAP CAP_SETPCAP CAP_NET_BIND_SERVICE CAP_SYS_CHROOT CAP_KILL CAP_AUDIT_WRITE] ``` +``` +# podman inspect myPod --type pod --format "{{.Name}}" +myPod +``` +``` +# podman inspect myVolume --type volume --format "{{.Name}}" +myVolume +``` + +``` +# podman inspect nyNetwork --type network --format "{{.name}}" +myNetwork +``` + ## SEE ALSO podman(1) diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 87337fa3c..2d5110ad9 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -220,7 +220,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | | [podman-info(1)](podman-info.1.md) | Displays Podman related system information. | | [podman-init(1)](podman-init.1.md) | Initialize one or more containers | -| [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. | +| [podman-inspect(1)](podman-inspect.1.md) | Display a container, image, volume, network, or pod's configuration. | | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | | [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. | | [podman-login(1)](podman-login.1.md) | Login to a container registry. | @@ -63,7 +63,6 @@ require ( github.com/vishvananda/netlink v1.1.0 go.etcd.io/bbolt v1.3.5 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 - golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go index b7e2b3988..9f6103c45 100644 --- a/pkg/api/handlers/libpod/networks.go +++ b/pkg/api/handlers/libpod/networks.go @@ -113,15 +113,15 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { return } name := utils.GetName(r) - options := entities.NetworkInspectOptions{} + options := entities.InspectOptions{} ic := abi.ContainerEngine{Libpod: runtime} - reports, err := ic.NetworkInspect(r.Context(), []string{name}, options) + reports, errs, err := ic.NetworkInspect(r.Context(), []string{name}, options) + // If the network cannot be found, we return a 404. + if len(errs) > 0 { + utils.Error(w, "Something went wrong", http.StatusNotFound, define.ErrNoSuchNetwork) + return + } if err != nil { - // If the network cannot be found, we return a 404. - if errors.Cause(err) == define.ErrNoSuchNetwork { - utils.Error(w, "Something went wrong", http.StatusNotFound, err) - return - } utils.InternalServerError(w, err) return } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index a20d3b404..8ab72dbd8 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -51,7 +51,7 @@ type ContainerEngine interface { HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) - NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error) + NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error) NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) @@ -76,7 +76,7 @@ type ContainerEngine interface { VarlinkService(ctx context.Context, opts ServiceOptions) error Version(ctx context.Context) (*SystemVersionReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) - VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) + VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context) ([]*VolumePruneReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index 9beeeb042..0bab672a7 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -18,11 +18,6 @@ type NetworkListReport struct { *libcni.NetworkConfigList } -// NetworkInspectOptions describes options for inspect networks -type NetworkInspectOptions struct { - Format string -} - // NetworkInspectReport describes the results from inspect networks type NetworkInspectReport map[string]interface{} diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 5fdd8afcb..d8ad2d891 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -56,6 +56,8 @@ type InspectOptions struct { Size bool `json:",omitempty"` // Type -- return JSON for specified type. Type string `json:",omitempty"` + // All -- inspect all + All bool `json:",omitempty"` } // All API and CLI diff commands and diff sub-commands use the same options diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index fb8466d04..1bc1e4301 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -105,10 +105,6 @@ type VolumeRmReport struct { Id string //nolint } -type VolumeInspectOptions struct { - All bool -} - type VolumeInspectReport struct { *VolumeConfigResponse } diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index f40df828a..4f572fb88 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -43,21 +43,26 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net return reports, nil } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { config, err := ic.Libpod.GetConfig() if err != nil { - return nil, err + return nil, nil, err } - + var errs []error rawCNINetworks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { rawList, err := network.InspectNetwork(config, name) if err != nil { - return nil, err + if errors.Cause(err) == define.ErrNoSuchNetwork { + errs = append(errs, errors.Errorf("no such network %s", name)) + continue + } else { + return nil, nil, errors.Wrapf(err, "error inspecting network %s", name) + } } rawCNINetworks = append(rawCNINetworks, rawList) } - return rawCNINetworks, nil + return rawCNINetworks, errs, nil } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 946f258af..a7262f61b 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" @@ -71,9 +72,10 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { +func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) { var ( err error + errs []error vols []*libpod.Volume ) @@ -82,13 +84,18 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin if opts.All { vols, err = ic.Libpod.GetAllVolumes() if err != nil { - return nil, err + return nil, nil, err } } else { for _, v := range namesOrIds { vol, err := ic.Libpod.LookupVolume(v) if err != nil { - return nil, errors.Wrapf(err, "error inspecting volume %s", v) + if errors.Cause(err) == define.ErrNoSuchVolume { + errs = append(errs, errors.Errorf("no such volume %s", v)) + continue + } else { + return nil, nil, errors.Wrapf(err, "error inspecting volume %s", v) + } } vols = append(vols, vol) } @@ -98,11 +105,11 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin var uid, gid int uid, err = v.UID() if err != nil { - return nil, err + return nil, nil, err } gid, err = v.GID() if err != nil { - return nil, err + return nil, nil, err } config := entities.VolumeConfigResponse{ Name: v.Name(), @@ -117,7 +124,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index d155fdd9e..15527e02c 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -5,22 +5,34 @@ import ( "github.com/containers/podman/v2/pkg/bindings/network" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" ) func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { return network.List(ic.ClientCxt, options) } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { - reports := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { + var ( + reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds)) + errs = []error{} + ) for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCxt, name) if err != nil { - return nil, err + errModel, ok := err.(entities.ErrorModel) + if !ok { + return nil, nil, err + } + if errModel.ResponseCode == 404 { + errs = append(errs, errors.Errorf("no such network %q", name)) + continue + } + return nil, nil, err } reports = append(reports, report...) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index e432d3292..c0df2bb7b 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" ) func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { @@ -35,25 +36,36 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { +func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.VolumeInspectReport, []error, error) { + var ( + reports = make([]*entities.VolumeInspectReport, 0, len(namesOrIds)) + errs = []error{} + ) if opts.All { vols, err := volumes.List(ic.ClientCxt, nil) if err != nil { - return nil, err + return nil, nil, err } for _, v := range vols { namesOrIds = append(namesOrIds, v.Name) } } - reports := make([]*entities.VolumeInspectReport, 0, len(namesOrIds)) for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCxt, id) if err != nil { - return nil, err + errModel, ok := err.(entities.ErrorModel) + if !ok { + return nil, nil, err + } + if errModel.ResponseCode == 404 { + errs = append(errs, errors.Errorf("no such volume %q", id)) + continue + } + return nil, nil, err } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: data}) } - return reports, nil + return reports, errs, nil } func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 206c66f9f..d8e23b118 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -515,6 +515,14 @@ func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData { return i } +// InspectPodToJSON takes the sessions output from an inspect and returns json +func (s *PodmanSessionIntegration) InspectPodArrToJSON() []define.InspectPodData { + var i []define.InspectPodData + err := jsoniter.Unmarshal(s.Out.Contents(), &i) + Expect(err).To(BeNil()) + return i +} + // CreatePod creates a pod with no infra container // it optionally takes a pod name func (p *PodmanTestIntegration) CreatePod(name string) (*PodmanSessionIntegration, int, string) { diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index d4de7a65c..e8a82f9a1 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -289,4 +289,145 @@ var _ = Describe("Podman inspect", func() { Expect(baseJSON[0].HostConfig.SecurityOpt).To(Equal([]string{"label=type:spc_t,label=level:s0", "seccomp=unconfined"})) }) + It("podman inspect pod", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + + It("podman inspect pod with type", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + + It("podman inspect latest pod", func() { + SkipIfRemote("--latest flag n/a") + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", "--latest"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodArrToJSON() + Expect(podData[0].Name).To(Equal(podName)) + }) + It("podman inspect latest defaults to latest container", func() { + SkipIfRemote("--latest flag n/a") + podName := "testpod" + pod := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + pod.WaitWithDefaultTimeout() + Expect(pod.ExitCode()).To(Equal(0)) + + inspect1 := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) + inspect1.WaitWithDefaultTimeout() + Expect(inspect1.ExitCode()).To(Equal(0)) + Expect(inspect1.IsJSONOutputValid()).To(BeTrue()) + podData := inspect1.InspectPodArrToJSON() + infra := podData[0].Containers[0].Name + + inspect := podmanTest.Podman([]string{"inspect", "--latest"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + containerData := inspect.InspectContainerToJSON() + Expect(containerData[0].Name).To(Equal(infra)) + }) + + It("podman inspect network", func() { + name, path := generateNetworkConfig(podmanTest) + defer removeConf(path) + + session := podmanTest.Podman([]string{"inspect", name, "--format", "{{.cniVersion}}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) + }) + + It("podman inspect a volume", func() { + session := podmanTest.Podman([]string{"volume", "create", "myvol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"inspect", volName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.IsJSONOutputValid()).To(BeTrue()) + }) + + It("podman inspect a volume with --format", func() { + session := podmanTest.Podman([]string{"volume", "create", "myvol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"inspect", "--format", "{{.Name}}", volName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal(volName)) + }) + It("podman inspect --type container on a pod should fail", func() { + podName := "testpod" + create := podmanTest.PodmanNoCache([]string{"pod", "create", "--name", podName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "container", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type network on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "network", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type pod on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + + It("podman inspect --type volume on a container should fail", func() { + ctrName := "testctr" + create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--type", "volume", ctrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(ExitWithError()) + }) + }) |