From 87293028e61d0c88c258ed9f4a82c4be7f0bc896 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Thu, 19 Mar 2020 16:50:15 -0500 Subject: podmanv2 container exists|wait enable container exists and wait for podmanv2 Signed-off-by: Brent Baude --- .gitignore | 1 + cmd/podmanV2/containers/exists.go | 40 +++++++++++++++++++ cmd/podmanV2/containers/inspect.go | 2 + cmd/podmanV2/containers/wait.go | 68 +++++++++++++++++++++++++++++++ cmd/podmanV2/images/inspect.go | 2 +- cmd/podmanV2/main.go | 13 +++--- cmd/podmanV2/registry/registry.go | 14 ++++--- cmd/podmanV2/root.go | 3 +- pkg/api/handlers/libpod/containers.go | 6 ++- pkg/bindings/connection.go | 7 +++- pkg/domain/entities/containers.go | 14 +++++++ pkg/domain/entities/engine.go | 9 ++--- pkg/domain/entities/engine_container.go | 14 +------ pkg/domain/infra/abi/containers.go | 71 +++++++++++++++++++++++++++++++++ pkg/domain/infra/abi/runtime.go | 2 - pkg/domain/infra/runtime_abi.go | 19 +++++---- pkg/domain/infra/runtime_image_proxy.go | 6 +-- pkg/domain/infra/runtime_libpod.go | 12 +++--- pkg/domain/infra/runtime_proxy.go | 39 ++---------------- pkg/domain/infra/runtime_tunnel.go | 16 ++++---- pkg/domain/infra/tunnel/containers.go | 24 +++++++++++ pkg/domain/infra/tunnel/runtime.go | 12 ------ 22 files changed, 284 insertions(+), 110 deletions(-) create mode 100644 cmd/podmanV2/containers/exists.go create mode 100644 cmd/podmanV2/containers/wait.go create mode 100644 pkg/domain/infra/abi/containers.go create mode 100644 pkg/domain/infra/tunnel/containers.go diff --git a/.gitignore b/.gitignore index 6ebb899cf..ea154fe5d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ podman*.tar.gz contrib/spec/podman.spec *.rpm *.coverprofile +/cmd/podmanV2/podmanV2 diff --git a/cmd/podmanV2/containers/exists.go b/cmd/podmanV2/containers/exists.go new file mode 100644 index 000000000..4f9e6c44a --- /dev/null +++ b/cmd/podmanV2/containers/exists.go @@ -0,0 +1,40 @@ +package containers + +import ( + "context" + "os" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + containerExistsCommand = &cobra.Command{ + Use: "exists CONTAINER", + Short: "Check if a container exists in local storage", + Long: containerExistsDescription, + Example: `podman container exists containerID + podman container exists myctr || podman run --name myctr [etc...]`, + RunE: exists, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: containerExistsCommand, + Parent: containerCmd, + }) +} + +func exists(cmd *cobra.Command, args []string) error { + exists, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]) + if err != nil { + return err + } + if !exists { + os.Exit(1) + } + return nil +} diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go index 635be4789..355924984 100644 --- a/cmd/podmanV2/containers/inspect.go +++ b/cmd/podmanV2/containers/inspect.go @@ -7,6 +7,8 @@ import ( ) var ( + containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` + // podman container _inspect_ inspectCmd = &cobra.Command{ Use: "inspect [flags] CONTAINER", diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go new file mode 100644 index 000000000..d18064c3c --- /dev/null +++ b/cmd/podmanV2/containers/wait.go @@ -0,0 +1,68 @@ +package containers + +import ( + "context" + "fmt" + "time" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + waitDescription = `Block until one or more containers stop and then print their exit codes. +` + waitCommand = &cobra.Command{ + Use: "wait [flags] CONTAINER [CONTAINER...]", + Short: "Block on one or more containers", + Long: waitDescription, + RunE: wait, + Example: `podman wait --latest + podman wait --interval 5000 ctrID + podman wait ctrID1 ctrID2`, + } +) + +var waitFlags = entities.WaitOptions{} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: waitCommand, + Parent: containerCmd, + }) + + waitCommand.SetHelpTemplate(registry.HelpTemplate()) + waitCommand.SetUsageTemplate(registry.UsageTemplate()) + flags := waitCommand.Flags() + flags.DurationVarP(&waitFlags.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") + flags.BoolVarP(&waitFlags.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.StringVar(&waitFlags.Condition, "condition", "stopped", "Condition to wait on") +} + +func wait(cmd *cobra.Command, args []string) error { + if waitFlags.Latest && len(args) > 0 { + return errors.New("cannot combine latest flag and arguments") + } + if waitFlags.Interval == 0 { + return errors.New("interval must be greater then 0") + } + + responses, err := registry.ContainerEngine().ContainerWait(context.Background(), args, waitFlags) + if err != nil { + return err + } + for _, r := range responses { + if r.Error == nil { + fmt.Println(r.Id) + } + } + for _, r := range responses { + if r.Error != nil { + fmt.Println(err) + } + } + return nil +} diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go index 9c44cea35..2ecbbb201 100644 --- a/cmd/podmanV2/images/inspect.go +++ b/cmd/podmanV2/images/inspect.go @@ -52,7 +52,7 @@ func init() { flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size") flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template") - if registry.GlobalFlags.EngineMode == entities.ABIMode { + if registry.EngineOpts.EngineMode == entities.ABIMode { // TODO: This is the same as V1. We could skip creating the flag altogether in V2... _ = flags.MarkHidden("latest") } diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go index 0df086352..24f21d804 100644 --- a/cmd/podmanV2/main.go +++ b/cmd/podmanV2/main.go @@ -5,6 +5,7 @@ import ( "os" "reflect" "runtime" + "strings" _ "github.com/containers/libpod/cmd/podmanV2/containers" _ "github.com/containers/libpod/cmd/podmanV2/images" @@ -31,17 +32,19 @@ func initCobra() { case "darwin": fallthrough case "windows": - registry.GlobalFlags.EngineMode = entities.TunnelMode + registry.EngineOpts.EngineMode = entities.TunnelMode case "linux": - registry.GlobalFlags.EngineMode = entities.ABIMode + registry.EngineOpts.EngineMode = entities.ABIMode default: logrus.Errorf("%s is not a supported OS", runtime.GOOS) os.Exit(1) } // TODO: Is there a Cobra way to "peek" at os.Args? - if ok := Contains("--remote", os.Args); ok { - registry.GlobalFlags.EngineMode = entities.TunnelMode + for _, v := range os.Args { + if strings.HasPrefix(v, "--remote") { + registry.EngineOpts.EngineMode = entities.TunnelMode + } } cobra.OnInitialize(func() {}) @@ -50,7 +53,7 @@ func initCobra() { func main() { fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands)) for _, c := range registry.Commands { - if Contains(registry.GlobalFlags.EngineMode, c.Mode) { + if Contains(registry.EngineOpts.EngineMode, c.Mode) { parent := rootCmd if c.Parent != nil { parent = c.Parent diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index fa51d6535..793d520a8 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -14,11 +14,13 @@ type CliCommand struct { } var ( - Commands []CliCommand - GlobalFlags entities.EngineFlags + Commands []CliCommand + imageEngine entities.ImageEngine containerEngine entities.ContainerEngine - PodmanTunnel bool + + EngineOpts entities.EngineOptions + GlobalFlags entities.EngineFlags ) // HelpTemplate returns the help template for podman commands @@ -63,7 +65,8 @@ func ImageEngine() entities.ImageEngine { // NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) { if imageEngine == nil { - engine, err := infra.NewImageEngine(GlobalFlags.EngineMode, entities.EngineOptions{}) + EngineOpts.FlagSet = cmd.Flags() + engine, err := infra.NewImageEngine(EngineOpts) if err != nil { return nil, err } @@ -79,7 +82,8 @@ func ContainerEngine() entities.ContainerEngine { // NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) { if containerEngine == nil { - engine, err := infra.NewContainerEngine(GlobalFlags.EngineMode, entities.EngineOptions{}) + EngineOpts.FlagSet = cmd.Flags() + engine, err := infra.NewContainerEngine(EngineOpts) if err != nil { return nil, err } diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go index 778184f28..92805ff30 100644 --- a/cmd/podmanV2/root.go +++ b/cmd/podmanV2/root.go @@ -24,7 +24,8 @@ func init() { // Override default --help information of `--version` global flag} var dummyVersion bool rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman") - rootCmd.PersistentFlags().BoolVarP(®istry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel") + rootCmd.PersistentFlags().StringVarP(®istry.EngineOpts.Uri, "remote", "r", "", "URL to access podman service") + rootCmd.PersistentFlags().StringSliceVarP(®istry.EngineOpts.Identities, "identity", "i", []string{}, "path to SSH identity file") } func Execute() { diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 8020c391d..cdc34004f 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -21,8 +21,12 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) { name := utils.GetName(r) _, err := runtime.LookupContainer(name) if err != nil { - utils.ContainerNotFound(w, name, err) + if errors.Cause(err) == define.ErrNoSuchCtr { + utils.ContainerNotFound(w, name, err) + } + utils.InternalServerError(w, err) return + } utils.WriteResponse(w, http.StatusNoContent, "") } diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 289debd8c..4fe4dd72d 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -165,8 +165,13 @@ func sshClient(_url *url.URL, identity string, secure bool) (*http.Client, error } } + port := _url.Port() + if port == "" { + port = "22" + } + bastion, err := ssh.Dial("tcp", - net.JoinHostPort(_url.Hostname(), _url.Port()), + net.JoinHostPort(_url.Hostname(), port), &ssh.ClientConfig{ User: _url.User.Username(), Auth: []ssh.AuthMethod{auth}, diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 1899b98f7..e688e4fd3 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -1 +1,15 @@ package entities + +import "time" + +type WaitOptions struct { + Condition string + Interval time.Duration + Latest bool +} + +type WaitReport struct { + Id string + Error error + ExitCode int32 +} diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index a1096f1f1..08ef1df92 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -1,7 +1,6 @@ package entities import ( - "net/url" "os/user" "path/filepath" @@ -20,11 +19,13 @@ func (m EngineMode) String() string { return string(m) } +// FIXME: merge EngineOptions and EngineFlags type EngineOptions struct { - Uri *url.URL + Uri string Identities []string - FlagSet pflag.FlagSet + FlagSet *pflag.FlagSet Flags EngineFlags + EngineMode EngineMode } type EngineFlags struct { @@ -58,8 +59,6 @@ type EngineFlags struct { Port int IdentityFile string IgnoreHosts bool - - EngineMode EngineMode } func NewEngineOptions() (EngineFlags, error) { diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index d08f37d44..53f8f88f5 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -5,22 +5,12 @@ import ( ) type ContainerEngine interface { - ContainerRuntime - PodRuntime - VolumeRuntime -} - -type ContainerRuntime interface { ContainerDelete(ctx context.Context, opts ContainerDeleteOptions) (*ContainerDeleteReport, error) ContainerPrune(ctx context.Context) (*ContainerPruneReport, error) -} - -type PodRuntime interface { + ContainerExists(ctx context.Context, nameOrId string) (bool, error) + ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error) PodPrune(ctx context.Context) (*PodPruneReport, error) -} - -type VolumeRuntime interface { VolumeDelete(ctx context.Context, opts VolumeDeleteOptions) (*VolumeDeleteReport, error) VolumePrune(ctx context.Context) (*VolumePruneReport, error) } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go new file mode 100644 index 000000000..618e9a111 --- /dev/null +++ b/pkg/domain/infra/abi/containers.go @@ -0,0 +1,71 @@ +// +build ABISupport + +package abi + +import ( + "context" + + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/adapter/shortcuts" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +// TODO: Should return *entities.ContainerExistsReport, error +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (bool, error) { + _, err := ic.Libpod.LookupContainer(nameOrId) + if err != nil && errors.Cause(err) != define.ErrNoSuchCtr { + return false, err + } + return err == nil, nil +} + +func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { + var ( + responses []entities.WaitReport + ) + condition, err := define.StringToContainerStatus(options.Condition) + if err != nil { + return nil, err + } + + ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, c := range ctrs { + response := entities.WaitReport{Id: c.ID()} + exitCode, err := c.WaitForConditionWithInterval(options.Interval, condition) + if err != nil { + response.Error = err + } else { + response.ExitCode = exitCode + } + responses = append(responses, response) + } + return responses, nil +} + +func (ic *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) { + panic("implement me") +} + +func (ic *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) { + panic("implement me") +} + +func (ic *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) { + panic("implement me") +} + +func (ic *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) { + panic("implement me") +} + +func (ic *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) { + panic("implement me") +} + +func (ic *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) { + panic("implement me") +} diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go index 479a69586..b53fb6d3a 100644 --- a/pkg/domain/infra/abi/runtime.go +++ b/pkg/domain/infra/abi/runtime.go @@ -4,7 +4,6 @@ package abi import ( "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/domain/entities" ) // Image-related runtime linked against libpod library @@ -14,6 +13,5 @@ type ImageEngine struct { // Container-related runtime linked against libpod library type ContainerEngine struct { - entities.ContainerEngine Libpod *libpod.Runtime } diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index de996f567..31f832423 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -8,32 +8,31 @@ import ( "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" - "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/containers/libpod/pkg/domain/infra/tunnel" ) // NewContainerEngine factory provides a libpod runtime for container-related operations -func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) { - switch mode { +func NewContainerEngine(opts entities.EngineOptions) (entities.ContainerEngine, error) { + switch opts.EngineMode { case entities.ABIMode: r, err := NewLibpodRuntime(opts.FlagSet, opts.Flags) - return &abi.ContainerEngine{ContainerEngine: r}, err + return r, err case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...) + ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...) return &tunnel.ContainerEngine{ClientCxt: ctx}, err } - return nil, fmt.Errorf("runtime mode '%v' is not supported", mode) + return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode) } // NewContainerEngine factory provides a libpod runtime for image-related operations -func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) { - switch mode { +func NewImageEngine(opts entities.EngineOptions) (entities.ImageEngine, error) { + switch opts.EngineMode { case entities.ABIMode: r, err := NewLibpodImageRuntime(opts.FlagSet, opts.Flags) return r, err case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...) + ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...) return &tunnel.ImageEngine{ClientCxt: ctx}, err } - return nil, fmt.Errorf("runtime mode '%v' is not supported", mode) + return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode) } diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go index 480e7c8d7..d2e66c08c 100644 --- a/pkg/domain/infra/runtime_image_proxy.go +++ b/pkg/domain/infra/runtime_image_proxy.go @@ -12,14 +12,10 @@ import ( // ContainerEngine Image Proxy will be EOL'ed after podmanV2 is separated from libpod repo -func NewLibpodImageRuntime(flags pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) { +func NewLibpodImageRuntime(flags *pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) { r, err := GetRuntime(context.Background(), flags, opts) if err != nil { return nil, err } return &abi.ImageEngine{Libpod: r}, nil } - -func (ir *runtime) ShutdownImageRuntime(force bool) error { - return ir.Libpod.Shutdown(force) -} diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index b42c52b6e..b835152bf 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -26,7 +26,7 @@ type engineOpts struct { } // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers -func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) { +func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ name: newRuntime, renumber: false, @@ -38,7 +38,7 @@ func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineF } // GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify -func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { +func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: false, migrate: false, @@ -49,7 +49,7 @@ func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.Engi } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber -func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { +func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: true, migrate: false, @@ -60,7 +60,7 @@ func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.Engine } // GetRuntime generates a new libpod runtime configured by command line options -func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { +func GetRuntime(ctx context.Context, flags *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { return getRuntime(ctx, flags, &engineOpts{ renumber: false, migrate: false, @@ -71,7 +71,7 @@ func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags } // GetRuntimeNoStore generates a new libpod runtime configured by command line options -func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { +func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: false, migrate: false, @@ -81,7 +81,7 @@ func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineF }) } -func getRuntime(ctx context.Context, fs flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) { +func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go index d17b8efa1..4095ae6e2 100644 --- a/pkg/domain/infra/runtime_proxy.go +++ b/pkg/domain/infra/runtime_proxy.go @@ -5,50 +5,17 @@ package infra import ( "context" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/domain/infra/abi" flag "github.com/spf13/pflag" ) // ContainerEngine Proxy will be EOL'ed after podmanV2 is separated from libpod repo -type runtime struct { - entities.ContainerEngine - Libpod *libpod.Runtime -} - -func NewLibpodRuntime(flags flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) { +func NewLibpodRuntime(flags *flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) { r, err := GetRuntime(context.Background(), flags, opts) if err != nil { return nil, err } - return &runtime{Libpod: r}, nil -} - -func (r *runtime) ShutdownRuntime(force bool) error { - return r.Libpod.Shutdown(force) -} - -func (r *runtime) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) { - panic("implement me") -} - -func (r *runtime) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) { - panic("implement me") -} - -func (r *runtime) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) { - panic("implement me") -} - -func (r *runtime) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) { - panic("implement me") -} - -func (r *runtime) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) { - panic("implement me") -} - -func (r *runtime) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) { - panic("implement me") + return &abi.ContainerEngine{Libpod: r}, nil } diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go index 8a606deaf..5816ef0c0 100644 --- a/pkg/domain/infra/runtime_tunnel.go +++ b/pkg/domain/infra/runtime_tunnel.go @@ -11,25 +11,25 @@ import ( "github.com/containers/libpod/pkg/domain/infra/tunnel" ) -func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) { - switch mode { +func NewContainerEngine(opts entities.EngineOptions) (entities.ContainerEngine, error) { + switch opts.EngineMode { case entities.ABIMode: return nil, fmt.Errorf("direct runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...) + ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...) return &tunnel.ContainerEngine{ClientCxt: ctx}, err } - return nil, fmt.Errorf("runtime mode '%v' is not supported", mode) + return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode) } // NewImageEngine factory provides a libpod runtime for image-related operations -func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) { - switch mode { +func NewImageEngine(opts entities.EngineOptions) (entities.ImageEngine, error) { + switch opts.EngineMode { case entities.ABIMode: return nil, fmt.Errorf("direct image runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...) + ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...) return &tunnel.ImageEngine{ClientCxt: ctx}, err } - return nil, fmt.Errorf("runtime mode '%v' is not supported", mode) + return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go new file mode 100644 index 000000000..84b6f2906 --- /dev/null +++ b/pkg/domain/infra/tunnel/containers.go @@ -0,0 +1,24 @@ +package tunnel + +import ( + "context" + + "github.com/containers/libpod/pkg/bindings/containers" + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (bool, error) { + return containers.Exists(ctx, nameOrId) +} + +func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { + return nil, nil +} + +func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) { + panic("implement me") +} + +func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) { + panic("implement me") +} diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go index af433a6d9..eb9b34e4a 100644 --- a/pkg/domain/infra/tunnel/runtime.go +++ b/pkg/domain/infra/tunnel/runtime.go @@ -16,18 +16,6 @@ type ContainerEngine struct { ClientCxt context.Context } -func (r *ContainerEngine) Shutdown(force bool) error { - return nil -} - -func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) { - panic("implement me") -} - -func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) { - panic("implement me") -} - func (r *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) { panic("implement me") } -- cgit v1.2.3-54-g00ecf From baf3a9b3a72e4d98ad30ee56a7dd81812fb1fd21 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 20 Mar 2020 13:00:37 -0500 Subject: use boolreport for containerexists response in the case of exists, use a boolreport structure so that responses can be consistent pointer and error Signed-off-by: Brent Baude --- cmd/podmanV2/containers/exists.go | 4 ++-- pkg/domain/entities/containers.go | 4 ++++ pkg/domain/entities/engine_container.go | 2 +- pkg/domain/infra/abi/containers.go | 6 +++--- pkg/domain/infra/tunnel/containers.go | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cmd/podmanV2/containers/exists.go b/cmd/podmanV2/containers/exists.go index 4f9e6c44a..93989b54a 100644 --- a/cmd/podmanV2/containers/exists.go +++ b/cmd/podmanV2/containers/exists.go @@ -29,11 +29,11 @@ func init() { } func exists(cmd *cobra.Command, args []string) error { - exists, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]) + response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]) if err != nil { return err } - if !exists { + if !response.Value { os.Exit(1) } return nil diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index e688e4fd3..45ce1f1fb 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -13,3 +13,7 @@ type WaitReport struct { Error error ExitCode int32 } + +type BoolReport struct { + Value bool +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 53f8f88f5..aa2ceb630 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -7,7 +7,7 @@ import ( type ContainerEngine interface { ContainerDelete(ctx context.Context, opts ContainerDeleteOptions) (*ContainerDeleteReport, error) ContainerPrune(ctx context.Context) (*ContainerPruneReport, error) - ContainerExists(ctx context.Context, nameOrId string) (bool, error) + ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error) PodPrune(ctx context.Context) (*PodPruneReport, error) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 618e9a111..2332c2874 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -12,12 +12,12 @@ import ( ) // TODO: Should return *entities.ContainerExistsReport, error -func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (bool, error) { +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { _, err := ic.Libpod.LookupContainer(nameOrId) if err != nil && errors.Cause(err) != define.ErrNoSuchCtr { - return false, err + return nil, err } - return err == nil, nil + return &entities.BoolReport{Value: err == nil}, nil } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 84b6f2906..c869ad2f3 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -7,8 +7,9 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (bool, error) { - return containers.Exists(ctx, nameOrId) +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { + exists, err := containers.Exists(ctx, nameOrId) + return &entities.BoolReport{Value: exists}, err } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { -- cgit v1.2.3-54-g00ecf From 4a00409bf8c88174cbdbc12760d3f8ddce9621de Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 20 Mar 2020 13:15:50 -0500 Subject: fix remote connection use of context Signed-off-by: Brent Baude --- pkg/domain/infra/tunnel/containers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index c869ad2f3..d2d93d51b 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -8,7 +8,7 @@ import ( ) func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { - exists, err := containers.Exists(ctx, nameOrId) + exists, err := containers.Exists(ic.ClientCxt, nameOrId) return &entities.BoolReport{Value: exists}, err } -- cgit v1.2.3-54-g00ecf From c81e065149da73ae904aa19ee46a23d1ab8ce55c Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 20 Mar 2020 14:00:05 -0500 Subject: podmanv2 enable remote wait enable remote container wait with condition Signed-off-by: Brent Baude --- cmd/podmanV2/containers/exists.go | 6 +++-- cmd/podmanV2/containers/inspect.go | 2 -- cmd/podmanV2/containers/wait.go | 22 +++++++++++++++---- cmd/podmanV2/root.go | 2 +- pkg/bindings/containers/containers.go | 5 +++-- pkg/bindings/test/common_test.go | 5 +++-- pkg/bindings/test/containers_test.go | 7 +++--- pkg/bindings/test/pods_test.go | 2 +- pkg/domain/entities/containers.go | 8 +++++-- pkg/domain/infra/abi/containers.go | 7 +----- pkg/domain/infra/tunnel/containers.go | 19 +++++++++++++++- pkg/domain/infra/tunnel/helpers.go | 41 +++++++++++++++++++++++++++++++++++ 12 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 pkg/domain/infra/tunnel/helpers.go diff --git a/cmd/podmanV2/containers/exists.go b/cmd/podmanV2/containers/exists.go index 93989b54a..3aff150be 100644 --- a/cmd/podmanV2/containers/exists.go +++ b/cmd/podmanV2/containers/exists.go @@ -10,7 +10,9 @@ import ( ) var ( - containerExistsCommand = &cobra.Command{ + containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` + + existsCommand = &cobra.Command{ Use: "exists CONTAINER", Short: "Check if a container exists in local storage", Long: containerExistsDescription, @@ -23,7 +25,7 @@ var ( func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: containerExistsCommand, + Command: existsCommand, Parent: containerCmd, }) } diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go index 355924984..635be4789 100644 --- a/cmd/podmanV2/containers/inspect.go +++ b/cmd/podmanV2/containers/inspect.go @@ -7,8 +7,6 @@ import ( ) var ( - containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` - // podman container _inspect_ inspectCmd = &cobra.Command{ Use: "inspect [flags] CONTAINER", diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go index d18064c3c..27acb3348 100644 --- a/cmd/podmanV2/containers/wait.go +++ b/cmd/podmanV2/containers/wait.go @@ -6,6 +6,7 @@ import ( "time" "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -25,7 +26,10 @@ var ( } ) -var waitFlags = entities.WaitOptions{} +var ( + waitFlags = entities.WaitOptions{} + waitCondition string +) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -34,15 +38,20 @@ func init() { Parent: containerCmd, }) - waitCommand.SetHelpTemplate(registry.HelpTemplate()) - waitCommand.SetUsageTemplate(registry.UsageTemplate()) flags := waitCommand.Flags() flags.DurationVarP(&waitFlags.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") flags.BoolVarP(&waitFlags.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.StringVar(&waitFlags.Condition, "condition", "stopped", "Condition to wait on") + flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") + if registry.EngineOpts.EngineMode == entities.ABIMode { + // TODO: This is the same as V1. We could skip creating the flag altogether in V2... + _ = flags.MarkHidden("latest") + } } func wait(cmd *cobra.Command, args []string) error { + var ( + err error + ) if waitFlags.Latest && len(args) > 0 { return errors.New("cannot combine latest flag and arguments") } @@ -50,6 +59,11 @@ func wait(cmd *cobra.Command, args []string) error { return errors.New("interval must be greater then 0") } + waitFlags.Condition, err = define.StringToContainerStatus(waitCondition) + if err != nil { + return err + } + responses, err := registry.ContainerEngine().ContainerWait(context.Background(), args, waitFlags) if err != nil { return err diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go index 92805ff30..24b083b9f 100644 --- a/cmd/podmanV2/root.go +++ b/cmd/podmanV2/root.go @@ -25,7 +25,7 @@ func init() { var dummyVersion bool rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman") rootCmd.PersistentFlags().StringVarP(®istry.EngineOpts.Uri, "remote", "r", "", "URL to access podman service") - rootCmd.PersistentFlags().StringSliceVarP(®istry.EngineOpts.Identities, "identity", "i", []string{}, "path to SSH identity file") + rootCmd.PersistentFlags().StringSliceVar(®istry.EngineOpts.Identities, "identity", []string{}, "path to SSH identity file") } func Execute() { diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index f298dbba1..534555a00 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod" "github.com/containers/libpod/pkg/bindings" ) @@ -212,7 +213,7 @@ func Unpause(ctx context.Context, nameOrID string) error { // Wait blocks until the given container reaches a condition. If not provided, the condition will // default to stopped. If the condition is stopped, an exit code for the container will be provided. The // nameOrID can be a container name or a partial/full ID. -func Wait(ctx context.Context, nameOrID string, condition *string) (int32, error) { +func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatus) (int32, error) { //nolint var exitCode int32 conn, err := bindings.GetClient(ctx) if err != nil { @@ -220,7 +221,7 @@ func Wait(ctx context.Context, nameOrID string, condition *string) (int32, error } params := url.Values{} if condition != nil { - params.Set("condition", *condition) + params.Set("condition", condition.String()) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nameOrID) if err != nil { diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 409e620a3..6b8d6788c 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -3,6 +3,7 @@ package test_bindings import ( "context" "fmt" + "github.com/containers/libpod/libpod/define" "io/ioutil" "os" "os/exec" @@ -205,8 +206,8 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po if err != nil { return "", err } - waiting := "running" - _, err = containers.Wait(b.conn, ctr.ID, &waiting) + wait := define.ContainerStateRunning + _, err = containers.Wait(b.conn, ctr.ID, &wait) return ctr.ID, err } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index afb0cc19b..f5465c803 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -1,6 +1,7 @@ package test_bindings import ( + "github.com/containers/libpod/libpod/define" "net/http" "strconv" "strings" @@ -282,8 +283,8 @@ var _ = Describe("Podman containers ", func() { var ( name = "top" exitCode int32 = -1 - pause = "paused" - unpause = "running" + pause = define.ContainerStatePaused + running = define.ContainerStateRunning ) errChan := make(chan error) _, err := bt.RunTopContainer(&name, nil, nil) @@ -301,7 +302,7 @@ var _ = Describe("Podman containers ", func() { errChan = make(chan error) go func() { - _, waitErr := containers.Wait(bt.conn, name, &unpause) + _, waitErr := containers.Wait(bt.conn, name, &running) errChan <- waitErr close(errChan) }() diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index fae1deca4..bcf8e69b8 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -81,7 +81,7 @@ var _ = Describe("Podman pods", func() { It("List pods with filters", func() { var newpod2 string = "newpod2" bt.Podcreate(&newpod2) - _, err = bt.RunTopContainer(nil, &trueFlag, &newpod) + _, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod) Expect(err).To(BeNil()) // Expected err with invalid filter params diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 45ce1f1fb..0e1208b3b 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -1,9 +1,13 @@ package entities -import "time" +import ( + "time" + + "github.com/containers/libpod/libpod/define" +) type WaitOptions struct { - Condition string + Condition define.ContainerStatus Interval time.Duration Latest bool } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 2332c2874..cdcd77246 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -24,18 +24,13 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin var ( responses []entities.WaitReport ) - condition, err := define.StringToContainerStatus(options.Condition) - if err != nil { - return nil, err - } - ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } for _, c := range ctrs { response := entities.WaitReport{Id: c.ID()} - exitCode, err := c.WaitForConditionWithInterval(options.Interval, condition) + exitCode, err := c.WaitForConditionWithInterval(options.Interval, options.Condition) if err != nil { response.Error = err } else { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index d2d93d51b..8bf74126d 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -13,7 +13,24 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { - return nil, nil + var ( + responses []entities.WaitReport + ) + cons, err := getContainersByContext(ic.ClientCxt, false, namesOrIds) + if err != nil { + return nil, err + } + for _, c := range cons { + response := entities.WaitReport{Id: c.ID} + exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition) + if err != nil { + response.Error = err + } else { + response.ExitCode = exitCode + } + responses = append(responses, response) + } + return responses, nil } func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) { diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go new file mode 100644 index 000000000..d5a3224c2 --- /dev/null +++ b/pkg/domain/infra/tunnel/helpers.go @@ -0,0 +1,41 @@ +package tunnel + +import ( + "context" + + "github.com/containers/libpod/pkg/api/handlers/libpod" + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/libpod/pkg/bindings/containers" + "github.com/containers/libpod/pkg/util" + "github.com/pkg/errors" +) + +func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]libpod.ListContainer, error) { + var ( + cons []libpod.ListContainer + ) + if all && len(namesOrIds) > 0 { + return nil, errors.New("cannot lookup containers and all") + } + c, err := containers.List(contextWithConnection, nil, &bindings.PTrue, nil, nil, nil, &bindings.PTrue) + if err != nil { + return nil, err + } + if all { + return c, err + } + for _, id := range namesOrIds { + var found bool + for _, con := range c { + if id == con.ID || util.StringInSlice(id, con.Names) { + cons = append(cons, con) + found = true + break + } + } + if !found { + return nil, errors.Errorf("unable to find container %q", id) + } + } + return cons, nil +} -- cgit v1.2.3-54-g00ecf