diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2020-03-26 21:36:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-26 21:36:36 +0100 |
commit | 6a46a87d08bd1e9ddda3dd3c9c30d21d2226a654 (patch) | |
tree | 6fc011ab6c7114ec215e0443e935396150c3dac1 | |
parent | 913426c70c37a87d425085f60af397f7b38bd65d (diff) | |
parent | c5ce210f7d091a4fa6d69abb9b4068c811a26129 (diff) | |
download | podman-6a46a87d08bd1e9ddda3dd3c9c30d21d2226a654.tar.gz podman-6a46a87d08bd1e9ddda3dd3c9c30d21d2226a654.tar.bz2 podman-6a46a87d08bd1e9ddda3dd3c9c30d21d2226a654.zip |
Merge pull request #5625 from baude/v2pods
podmanv2 pod subcommands
-rw-r--r-- | cmd/podmanV2/pods/kill.go | 68 | ||||
-rw-r--r-- | cmd/podmanV2/pods/pause.go | 66 | ||||
-rw-r--r-- | cmd/podmanV2/pods/restart.go | 68 | ||||
-rw-r--r-- | cmd/podmanV2/pods/rm.go | 71 | ||||
-rw-r--r-- | cmd/podmanV2/pods/start.go | 68 | ||||
-rw-r--r-- | cmd/podmanV2/pods/stop.go | 78 | ||||
-rw-r--r-- | cmd/podmanV2/pods/unpause.go | 66 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/pods.go | 112 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/swagger.go | 50 | ||||
-rw-r--r-- | pkg/api/handlers/swagger.go | 3 | ||||
-rw-r--r-- | pkg/api/handlers/utils/pods.go | 49 | ||||
-rw-r--r-- | pkg/api/server/register_pods.go | 28 | ||||
-rw-r--r-- | pkg/bindings/pods/pods.go | 82 | ||||
-rw-r--r-- | pkg/bindings/test/pods_test.go | 49 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 7 | ||||
-rw-r--r-- | pkg/domain/entities/pods.go | 94 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 6 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods.go | 222 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/helpers.go | 34 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/pods.go | 159 | ||||
-rw-r--r-- | test/apiv2/40-pods.at | 8 |
21 files changed, 1278 insertions, 110 deletions
diff --git a/cmd/podmanV2/pods/kill.go b/cmd/podmanV2/pods/kill.go new file mode 100644 index 000000000..06cca916c --- /dev/null +++ b/cmd/podmanV2/pods/kill.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podKillDescription = `Signals are sent to the main process of each container inside the specified pod. + + The default signal is SIGKILL, or any signal specified with option --signal.` + killCommand = &cobra.Command{ + Use: "kill [flags] POD [POD...]", + Short: "Send the specified signal or SIGKILL to containers in pod", + Long: podKillDescription, + RunE: kill, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod kill podID + podman pod kill --signal TERM mywebserver + podman pod kill --latest`, + } +) + +var ( + killOpts entities.PodKillOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: killCommand, + Parent: podCmd, + }) + flags := killCommand.Flags() + flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods") + flags.BoolVarP(&killOpts.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } + +} +func kill(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodKill(context.Background(), args, killOpts) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/pause.go b/cmd/podmanV2/pods/pause.go new file mode 100644 index 000000000..dc86e534d --- /dev/null +++ b/cmd/podmanV2/pods/pause.go @@ -0,0 +1,66 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podPauseDescription = `The pod name or ID can be used. + + All running containers within each specified pod will then be paused.` + pauseCommand = &cobra.Command{ + Use: "pause [flags] POD [POD...]", + Short: "Pause one or more pods", + Long: podPauseDescription, + RunE: pause, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod pause podID1 podID2 + podman pod pause --latest + podman pod pause --all`, + } +) + +var ( + pauseOptions entities.PodPauseOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pauseCommand, + Parent: podCmd, + }) + flags := pauseCommand.Flags() + flags.BoolVarP(&pauseOptions.All, "all", "a", false, "Pause all running pods") + flags.BoolVarP(&pauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} +func pause(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodPause(context.Background(), args, pauseOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/restart.go b/cmd/podmanV2/pods/restart.go new file mode 100644 index 000000000..1c8709704 --- /dev/null +++ b/cmd/podmanV2/pods/restart.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podRestartDescription = `The pod ID or name can be used. + + All of the containers within each of the specified pods will be restarted. If a container in a pod is not currently running it will be started.` + restartCommand = &cobra.Command{ + Use: "restart [flags] POD [POD...]", + Short: "Restart one or more pods", + Long: podRestartDescription, + RunE: restart, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod restart podID1 podID2 + podman pod restart --latest + podman pod restart --all`, + } +) + +var ( + restartOptions = entities.PodRestartOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: restartCommand, + Parent: podCmd, + }) + + flags := restartCommand.Flags() + flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func restart(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodRestart(context.Background(), args, restartOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/rm.go b/cmd/podmanV2/pods/rm.go new file mode 100644 index 000000000..b43dd2d6c --- /dev/null +++ b/cmd/podmanV2/pods/rm.go @@ -0,0 +1,71 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. + + The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) + rmCommand = &cobra.Command{ + Use: "rm [flags] POD [POD...]", + Short: "Remove one or more pods", + Long: podRmDescription, + RunE: rm, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod rm mywebserverpod + podman pod rm -f 860a4b23 + podman pod rm -f -a`, + } +) + +var ( + rmOptions = entities.PodRmOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: rmCommand, + Parent: podCmd, + }) + + flags := rmCommand.Flags() + flags.BoolVarP(&rmOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") + flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") + flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("ignore") + } +} + +func rm(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if r.Err == nil { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Err) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/start.go b/cmd/podmanV2/pods/start.go new file mode 100644 index 000000000..11ac312f9 --- /dev/null +++ b/cmd/podmanV2/pods/start.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podStartDescription = `The pod name or ID can be used. + + All containers defined in the pod will be started.` + startCommand = &cobra.Command{ + Use: "start [flags] POD [POD...]", + Short: "Start one or more pods", + Long: podStartDescription, + RunE: start, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod start podID + podman pod start --latest + podman pod start --all`, + } +) + +var ( + startOptions = entities.PodStartOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: startCommand, + Parent: podCmd, + }) + + flags := startCommand.Flags() + flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func start(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodStart(context.Background(), args, startOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/stop.go b/cmd/podmanV2/pods/stop.go new file mode 100644 index 000000000..2b61850e2 --- /dev/null +++ b/cmd/podmanV2/pods/stop.go @@ -0,0 +1,78 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podStopDescription = `The pod name or ID can be used. + + This command will stop all running containers in each of the specified pods.` + + stopCommand = &cobra.Command{ + Use: "stop [flags] POD [POD...]", + Short: "Stop one or more pods", + Long: podStopDescription, + RunE: stop, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod stop mywebserverpod + podman pod stop --latest + podman pod stop --timeout 0 490eb 3557fb`, + } +) + +var ( + stopOptions = entities.PodStopOptions{ + Timeout: -1, + } + timeout uint +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: stopCommand, + Parent: podCmd, + }) + flags := stopCommand.Flags() + flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods") + flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") + flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") + flags.UintVarP(&timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("ignore") + + } +} + +func stop(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + if cmd.Flag("timeout").Changed { + stopOptions.Timeout = int(timeout) + } + responses, err := registry.ContainerEngine().PodStop(context.Background(), args, stopOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/unpause.go b/cmd/podmanV2/pods/unpause.go new file mode 100644 index 000000000..2de7b964f --- /dev/null +++ b/cmd/podmanV2/pods/unpause.go @@ -0,0 +1,66 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podUnpauseDescription = `The podman unpause command will unpause all "paused" containers assigned to the pod. + + The pod name or ID can be used.` + unpauseCommand = &cobra.Command{ + Use: "unpause [flags] POD [POD...]", + Short: "Unpause one or more pods", + Long: podUnpauseDescription, + RunE: unpause, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod unpause podID1 podID2 + podman pod unpause --all + podman pod unpause --latest`, + } +) + +var ( + unpauseOptions entities.PodunpauseOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: unpauseCommand, + Parent: podCmd, + }) + flags := unpauseCommand.Flags() + flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods") + flags.BoolVarP(&unpauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} +func unpause(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodUnpause(context.Background(), args, unpauseOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 27ec64d89..5baf61ac9 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -102,9 +103,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { } func Pods(w http.ResponseWriter, r *http.Request) { - var ( - podInspectData []*libpod.PodInspect - ) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Filters map[string][]string `schema:"filters"` @@ -118,20 +116,11 @@ func Pods(w http.ResponseWriter, r *http.Request) { } pods, err := utils.GetPods(w, r) - if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - for _, pod := range pods { - data, err := pod.Inspect() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - podInspectData = append(podInspectData, data) - } - utils.WriteResponse(w, http.StatusOK, podInspectData) + utils.WriteResponse(w, http.StatusOK, pods) } func PodInspect(w http.ResponseWriter, r *http.Request) { @@ -155,6 +144,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) { stopError error runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) + responses map[string]error + errs []error ) query := struct { Timeout int `schema:"t"` @@ -185,18 +176,28 @@ func PodStop(w http.ResponseWriter, r *http.Request) { } if query.Timeout > 0 { - _, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout) + responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout) } else { - _, stopError = pod.Stop(r.Context(), false) + responses, stopError = pod.Stop(r.Context(), false) } if stopError != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, "") + for _, err := range responses { + errs = append(errs, err) + } + report := entities.PodStopReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodStart(w http.ResponseWriter, r *http.Request) { + var ( + errs []error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -213,11 +214,19 @@ func PodStart(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusNotModified, "") return } - if _, err := pod.Start(r.Context()); err != nil { + responses, err := pod.Start(r.Context()) + if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, "") + for _, err := range responses { + errs = append(errs, err) + } + report := entities.PodStartReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodDelete(w http.ResponseWriter, r *http.Request) { @@ -246,10 +255,16 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusNoContent, "") + report := entities.PodRmReport{ + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodRestart(w http.ResponseWriter, r *http.Request) { + var ( + errs []error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -257,12 +272,19 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } - _, err = pod.Restart(r.Context()) + responses, err := pod.Restart(r.Context()) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, "") + for _, err := range responses { + errs = append(errs, err) + } + report := entities.PodRestartReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodPrune(w http.ResponseWriter, r *http.Request) { @@ -278,6 +300,9 @@ func PodPrune(w http.ResponseWriter, r *http.Request) { } func PodPause(w http.ResponseWriter, r *http.Request) { + var ( + errs []error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -285,15 +310,25 @@ func PodPause(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } - _, err = pod.Pause() + responses, err := pod.Pause() if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusNoContent, "") + for _, v := range responses { + errs = append(errs, v) + } + report := entities.PodPauseReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodUnpause(w http.ResponseWriter, r *http.Request) { + var ( + errs []error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -301,12 +336,19 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } - _, err = pod.Unpause() + responses, err := pod.Unpause() if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, "") + for _, v := range responses { + errs = append(errs, v) + } + report := entities.PodUnpauseReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, &report) } func PodKill(w http.ResponseWriter, r *http.Request) { @@ -314,6 +356,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) { runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) signal = "SIGKILL" + errs []error ) query := struct { Signal string `schema:"signal"` @@ -356,12 +399,23 @@ func PodKill(w http.ResponseWriter, r *http.Request) { utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID())) return } - _, err = pod.Kill(uint(sig)) + + responses, err := pod.Kill(uint(sig)) if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, "") + + for _, v := range responses { + if v != nil { + errs = append(errs, v) + } + } + report := &entities.PodKillReport{ + Errs: errs, + Id: pod.ID(), + } + utils.WriteResponse(w, http.StatusOK, report) } func PodExists(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go index 149fa10dc..1fad2dd1a 100644 --- a/pkg/api/handlers/libpod/swagger.go +++ b/pkg/api/handlers/libpod/swagger.go @@ -6,6 +6,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" ) @@ -26,6 +27,55 @@ type swagInspectManifestResponse struct { Body manifest.List } +// Kill Pod +// swagger:response PodKillReport +type swagKillPodResponse struct { + // in:body + Body entities.PodKillReport +} + +// Pause pod +// swagger:response PodPauseReport +type swagPausePodResponse struct { + // in:body + Body entities.PodPauseReport +} + +// Unpause pod +// swagger:response PodUnpauseReport +type swagUnpausePodResponse struct { + // in:body + Body entities.PodUnpauseReport +} + +// Stop pod +// swagger:response PodStopReport +type swagStopPodResponse struct { + // in:body + Body entities.PodStopReport +} + +// Restart pod +// swagger:response PodRestartReport +type swagRestartPodResponse struct { + // in:body + Body entities.PodRestartReport +} + +// Start pod +// swagger:response PodStartReport +type swagStartPodResponse struct { + // in:body + Body entities.PodStartReport +} + +// Rm pod +// swagger:response PodRmReport +type swagRmPodResponse struct { + // in:body + Body entities.PodRmReport +} + func ServeSwagger(w http.ResponseWriter, r *http.Request) { path := DefaultPodmanSwaggerSpec if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found { diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go index 4ba123ba9..646ad45ea 100644 --- a/pkg/api/handlers/swagger.go +++ b/pkg/api/handlers/swagger.go @@ -3,6 +3,7 @@ package handlers import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/inspect" "github.com/docker/docker/api/types" ) @@ -116,7 +117,7 @@ type swagLibpodInspectContainerResponse struct { // swagger:response ListPodsResponse type swagListPodsResponse struct { // in:body - Body []libpod.PodInspect + Body []entities.ListPodsReport } // Inspect pod diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go index 266ad9a4b..79d1a5090 100644 --- a/pkg/api/handlers/utils/pods.go +++ b/pkg/api/handlers/utils/pods.go @@ -6,10 +6,16 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" "github.com/gorilla/schema" ) -func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) { +func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) { + var ( + lps []*entities.ListPodsReport + pods []*libpod.Pod + podErr error + ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) @@ -37,9 +43,42 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*libpod.Pod, error) { if err != nil { return nil, err } - return shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...) + pods, podErr = shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...) + } else { + pods, podErr = runtime.GetAllPods() } - - return runtime.GetAllPods() - + if podErr != nil { + return nil, podErr + } + for _, pod := range pods { + status, err := pod.GetPodStatus() + if err != nil { + return nil, err + } + ctrs, err := pod.AllContainers() + if err != nil { + return nil, err + } + lp := entities.ListPodsReport{ + Cgroup: pod.CgroupParent(), + Created: pod.CreatedTime(), + Id: pod.ID(), + Name: pod.Name(), + Namespace: pod.Namespace(), + Status: status, + } + for _, ctr := range ctrs { + state, err := ctr.State() + if err != nil { + return nil, err + } + lp.Containers = append(lp.Containers, &entities.ListPodContainer{ + Id: ctr.ID(), + Names: ctr.Name(), + Status: state.String(), + }) + } + lps = append(lps, &lp) + } + return lps, nil } diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index af2330665..87194fdd7 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -81,8 +81,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // type: boolean // description : force removal of a running pod by first stopping all containers, then removing all containers in the pod // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodRmReport' // 400: // $ref: "#/responses/BadParamError" // 404: @@ -146,8 +146,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // description: signal to be sent to pod // default: SIGKILL // responses: - // 204: - // description: no error + // 200: + // $ref: "#/responses/PodKillReport" // 400: // $ref: "#/responses/BadParamError" // 404: @@ -170,8 +170,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // required: true // description: the name or ID of the pod // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodPauseReport' // 404: // $ref: "#/responses/NoSuchPod" // 500: @@ -189,8 +189,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // required: true // description: the name or ID of the pod // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodRestartReport' // 404: // $ref: "#/responses/NoSuchPod" // 500: @@ -208,8 +208,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // required: true // description: the name or ID of the pod // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodStartReport' // 304: // $ref: "#/responses/PodAlreadyStartedError" // 404: @@ -233,8 +233,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // type: integer // description: timeout // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodStopReport' // 304: // $ref: "#/responses/PodAlreadyStoppedError" // 400: @@ -256,8 +256,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // required: true // description: the name or ID of the pod // responses: - // 204: - // description: no error + // 200: + // $ref: '#/responses/PodUnpauseReport' // 404: // $ref: "#/responses/NoSuchPod" // 500: diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index 1a8c31be1..49cce6e2b 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -8,6 +8,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/bindings" + "github.com/containers/libpod/pkg/domain/entities" ) func CreatePod() error { @@ -44,10 +45,13 @@ func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) { // Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter // can be used to override SIGTERM. -func Kill(ctx context.Context, nameOrID string, signal *string) error { +func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKillReport, error) { + var ( + report entities.PodKillReport + ) conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } params := url.Values{} if signal != nil { @@ -55,22 +59,23 @@ func Kill(ctx context.Context, nameOrID string, signal *string) error { } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + return &report, response.Process(&report) } // Pause pauses all running containers in a given pod. -func Pause(ctx context.Context, nameOrID string) error { +func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, error) { + var report entities.PodPauseReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + return &report, response.Process(&report) } // Prune removes all non-running pods in local storage. @@ -88,9 +93,9 @@ func Prune(ctx context.Context) error { // List returns all pods in local storage. The optional filters parameter can // be used to refine which pods should be listed. -func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspect, error) { +func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPodsReport, error) { var ( - inspect []*libpod.PodInspect + podsReports []*entities.ListPodsReport ) conn, err := bindings.GetClient(ctx) if err != nil { @@ -106,30 +111,32 @@ func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspec } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params) if err != nil { - return inspect, err + return podsReports, err } - return inspect, response.Process(&inspect) + return podsReports, response.Process(&podsReports) } // Restart restarts all containers in a pod. -func Restart(ctx context.Context, nameOrID string) error { +func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, error) { + var report entities.PodRestartReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + return &report, response.Process(&report) } // Remove deletes a Pod from from local storage. The optional force parameter denotes // that the Pod can be removed even if in a running state. -func Remove(ctx context.Context, nameOrID string, force *bool) error { +func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmReport, error) { + var report entities.PodRmReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } params := url.Values{} if force != nil { @@ -137,22 +144,27 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error { } response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + return &report, response.Process(&report) } // Start starts all containers in a pod. -func Start(ctx context.Context, nameOrID string) error { +func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, error) { + var report entities.PodStartReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + if response.StatusCode == http.StatusNotModified { + report.Id = nameOrID + return &report, nil + } + return &report, response.Process(&report) } func Stats() error { @@ -162,10 +174,11 @@ func Stats() error { // Stop stops all containers in a Pod. The optional timeout parameter can be // used to override the timeout before the container is killed. -func Stop(ctx context.Context, nameOrID string, timeout *int) error { +func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStopReport, error) { + var report entities.PodStopReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } params := url.Values{} if timeout != nil { @@ -173,9 +186,13 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) error { } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + if response.StatusCode == http.StatusNotModified { + report.Id = nameOrID + return &report, nil + } + return &report, response.Process(&report) } func Top() error { @@ -184,14 +201,15 @@ func Top() error { } // Unpause unpauses all paused containers in a Pod. -func Unpause(ctx context.Context, nameOrID string) error { +func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, error) { + var report entities.PodUnpauseReport conn, err := bindings.GetClient(ctx) if err != nil { - return err + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nameOrID) if err != nil { - return err + return nil, err } - return response.Process(nil) + return &report, response.Process(&report) } diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index a1d6ee184..c54170648 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -71,7 +71,7 @@ var _ = Describe("Podman pods", func() { Expect(len(podSummary)).To(Equal(2)) var names []string for _, i := range podSummary { - names = append(names, i.Config.Name) + names = append(names, i.Name) } Expect(StringInSlice(newpod, names)).To(BeTrue()) Expect(StringInSlice("newpod2", names)).To(BeTrue()) @@ -107,13 +107,14 @@ var _ = Describe("Podman pods", func() { Expect(len(filteredPods)).To(BeNumerically("==", 1)) var names []string for _, i := range filteredPods { - names = append(names, i.Config.Name) + names = append(names, i.Name) } Expect(StringInSlice("newpod2", names)).To(BeTrue()) // Validate list pod with id filter filters = make(map[string][]string) response, err := pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) id := response.Config.ID filters["id"] = []string{id} filteredPods, err = pods.List(bt.conn, filters) @@ -121,7 +122,7 @@ var _ = Describe("Podman pods", func() { Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] for _, i := range filteredPods { - names = append(names, i.Config.Name) + names = append(names, i.Name) } Expect(StringInSlice("newpod", names)).To(BeTrue()) @@ -132,7 +133,7 @@ var _ = Describe("Podman pods", func() { Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] for _, i := range filteredPods { - names = append(names, i.Config.Name) + names = append(names, i.Name) } Expect(StringInSlice("newpod", names)).To(BeTrue()) }) @@ -155,7 +156,7 @@ var _ = Describe("Podman pods", func() { // TODO fix this Skip("Pod behavior is jacked right now.") // Pause invalid container - err := pods.Pause(bt.conn, "dummyName") + _, err := pods.Pause(bt.conn, "dummyName") Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -167,9 +168,10 @@ var _ = Describe("Podman pods", func() { // Binding needs to be modified to inspect the pod state. // Since we don't have a pod state we inspect the states of the containers within the pod. // Pause a valid container - err = pods.Pause(bt.conn, newpod) + _, err = pods.Pause(bt.conn, newpod) Expect(err).To(BeNil()) response, err := pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStatePaused)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -177,9 +179,10 @@ var _ = Describe("Podman pods", func() { } // Unpause a valid container - err = pods.Unpause(bt.conn, newpod) + _, err = pods.Unpause(bt.conn, newpod) Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -189,28 +192,29 @@ var _ = Describe("Podman pods", func() { It("start stop restart pod", func() { // Start an invalid pod - err = pods.Start(bt.conn, "dummyName") + _, err = pods.Start(bt.conn, "dummyName") Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Stop an invalid pod - err = pods.Stop(bt.conn, "dummyName", nil) + _, err = pods.Stop(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Restart an invalid pod - err = pods.Restart(bt.conn, "dummyName") + _, err = pods.Restart(bt.conn, "dummyName") Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a valid pod and inspect status of each container - err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod) Expect(err).To(BeNil()) response, err := pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -218,11 +222,11 @@ var _ = Describe("Podman pods", func() { } // Start an already running pod - err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod) Expect(err).To(BeNil()) // Stop the running pods - err = pods.Stop(bt.conn, newpod, nil) + _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) response, _ = pods.Inspect(bt.conn, newpod) Expect(response.State.Status).To(Equal(define.PodStateExited)) @@ -232,10 +236,10 @@ var _ = Describe("Podman pods", func() { } // Stop an already stopped pod - err = pods.Stop(bt.conn, newpod, nil) + _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - err = pods.Restart(bt.conn, newpod) + _, err = pods.Restart(bt.conn, newpod) Expect(err).To(BeNil()) response, _ = pods.Inspect(bt.conn, newpod) Expect(response.State.Status).To(Equal(define.PodStateRunning)) @@ -260,11 +264,12 @@ var _ = Describe("Podman pods", func() { // Prune only one pod which is in exited state. // Start then stop a pod. // pod moves to exited state one pod should be pruned now. - err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod) Expect(err).To(BeNil()) - err = pods.Stop(bt.conn, newpod, nil) + _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) response, err := pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStateExited)) err = pods.Prune(bt.conn) Expect(err).To(BeNil()) @@ -274,21 +279,23 @@ var _ = Describe("Podman pods", func() { // Test prune all pods in exited state. bt.Podcreate(&newpod) - err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod) Expect(err).To(BeNil()) - err = pods.Start(bt.conn, newpod2) + _, err = pods.Start(bt.conn, newpod2) Expect(err).To(BeNil()) - err = pods.Stop(bt.conn, newpod, nil) + _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateStopped)) } - err = pods.Stop(bt.conn, newpod2, nil) + _, err = pods.Stop(bt.conn, newpod2, nil) Expect(err).To(BeNil()) response, err = pods.Inspect(bt.conn, newpod2) + Expect(err).To(BeNil()) Expect(response.State.Status).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 2efdbd602..958061437 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -14,6 +14,13 @@ type ContainerEngine interface { ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) + PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) + PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) + PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error) + PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error) + PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) + PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error) + PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go new file mode 100644 index 000000000..8d594620f --- /dev/null +++ b/pkg/domain/entities/pods.go @@ -0,0 +1,94 @@ +package entities + +import "time" + +type PodKillOptions struct { + All bool + Latest bool + Signal string +} + +type PodKillReport struct { + Errs []error + Id string +} + +type ListPodsReport struct { + Cgroup string + Containers []*ListPodContainer + Created time.Time + Id string + Name string + Namespace string + Status string +} + +type ListPodContainer struct { + Id string + Names string + Status string +} + +type PodPauseOptions struct { + All bool + Latest bool +} + +type PodPauseReport struct { + Errs []error + Id string +} + +type PodunpauseOptions struct { + All bool + Latest bool +} + +type PodUnpauseReport struct { + Errs []error + Id string +} + +type PodStopOptions struct { + All bool + Ignore bool + Latest bool + Timeout int +} + +type PodStopReport struct { + Errs []error + Id string +} + +type PodRestartOptions struct { + All bool + Latest bool +} + +type PodRestartReport struct { + Errs []error + Id string +} + +type PodStartOptions struct { + All bool + Latest bool +} + +type PodStartReport struct { + Errs []error + Id string +} + +type PodRmOptions struct { + All bool + Force bool + Ignore bool + Latest bool +} + +type PodRmReport struct { + Err error + Id string +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 203f14987..e3b3e310b 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -46,7 +46,7 @@ func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entit err = ir.deleteImage(ctx, img, opts, report) report.Errors = append(report.Errors, err) } - if len(targets) >= 0 || len(previousTargets) != 1 { + if len(previousTargets) != 1 { goto repeatRun } return &report, nil @@ -83,9 +83,7 @@ func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, } report.Deleted = append(report.Deleted, results.Deleted) - for _, e := range results.Untagged { - report.Untagged = append(report.Untagged, e) - } + report.Untagged = append(report.Untagged, results.Untagged...) return nil } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 8dd7b5a0c..4f68ad5f9 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -5,11 +5,45 @@ package abi import ( "context" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/signal" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) +// getPodsByContext returns a slice of pods. Note that all, latest and pods are +// mutually exclusive arguments. +func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) { + var outpods []*libpod.Pod + if all { + return runtime.GetAllPods() + } + if latest { + p, err := runtime.GetLatestPod() + if err != nil { + return nil, err + } + outpods = append(outpods, p) + return outpods, nil + } + var err error + for _, p := range pods { + pod, e := runtime.LookupPod(p) + if e != nil { + // Log all errors here, so callers don't need to. + logrus.Debugf("Error looking up pod %q: %v", p, e) + if err == nil { + err = e + } + } else { + outpods = append(outpods, pod) + } + } + return outpods, err +} + func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { _, err := ic.Libpod.LookupPod(nameOrId) if err != nil && errors.Cause(err) != define.ErrNoSuchPod { @@ -17,3 +51,191 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*ent } return &entities.BoolReport{Value: err == nil}, nil } + +func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { + var ( + reports []*entities.PodKillReport + ) + sig, err := signal.ParseSignalNameOrNumber(options.Signal) + if err != nil { + return nil, err + } + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + + for _, p := range pods { + report := entities.PodKillReport{Id: p.ID()} + conErrs, err := p.Kill(uint(sig)) + if err != nil { + report.Errs = []error{err} + reports = append(reports, &report) + continue + } + if len(conErrs) > 0 { + for _, err := range conErrs { + report.Errs = append(report.Errs, err) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { + var ( + reports []*entities.PodPauseReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodPauseReport{Id: p.ID()} + errs, err := p.Pause() + if err != nil { + report.Errs = []error{err} + continue + } + if len(errs) > 0 { + for _, v := range errs { + report.Errs = append(report.Errs, v) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) { + var ( + reports []*entities.PodUnpauseReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodUnpauseReport{Id: p.ID()} + errs, err := p.Unpause() + if err != nil { + report.Errs = []error{err} + continue + } + if len(errs) > 0 { + for _, v := range errs { + report.Errs = append(report.Errs, v) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { + var ( + reports []*entities.PodStopReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodStopReport{Id: p.ID()} + errs, err := p.StopWithTimeout(ctx, false, options.Timeout) + if err != nil { + report.Errs = []error{err} + continue + } + if len(errs) > 0 { + for _, v := range errs { + report.Errs = append(report.Errs, v) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) { + var ( + reports []*entities.PodRestartReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodRestartReport{Id: p.ID()} + errs, err := p.Restart(ctx) + if err != nil { + report.Errs = []error{err} + continue + } + if len(errs) > 0 { + for _, v := range errs { + report.Errs = append(report.Errs, v) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) { + var ( + reports []*entities.PodStartReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodStartReport{Id: p.ID()} + errs, err := p.Start(ctx) + if err != nil { + report.Errs = []error{err} + continue + } + if len(errs) > 0 { + for _, v := range errs { + report.Errs = append(report.Errs, v) + } + reports = append(reports, &report) + continue + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { + var ( + reports []*entities.PodRmReport + ) + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, p := range pods { + report := entities.PodRmReport{Id: p.ID()} + err := ic.Libpod.RemovePod(ctx, p, true, options.Force) + if err != nil { + report.Err = err + continue + } + reports = append(reports, &report) + } + return reports, nil +} diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 11fca5278..f9183c955 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -4,9 +4,12 @@ import ( "context" "strings" + "github.com/containers/libpod/libpod/define" "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/bindings/pods" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" ) @@ -40,3 +43,34 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam } return cons, nil } + +func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]*entities.ListPodsReport, error) { + var ( + sPods []*entities.ListPodsReport + ) + if all && len(namesOrIds) > 0 { + return nil, errors.New("cannot lookup specific pods and all") + } + + fPods, err := pods.List(contextWithConnection, nil) + if err != nil { + return nil, err + } + if all { + return fPods, nil + } + for _, nameOrId := range namesOrIds { + var found bool + for _, f := range fPods { + if f.Name == nameOrId || strings.HasPrefix(f.Id, nameOrId) { + sPods = append(sPods, f) + found = true + break + } + } + if !found { + return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrId) + } + } + return sPods, nil +} diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index 500069d51..53003da42 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -11,3 +11,162 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*ent exists, err := pods.Exists(ic.ClientCxt, nameOrId) return &entities.BoolReport{Value: exists}, err } + +func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { + var ( + reports []*entities.PodKillReport + ) + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Kill(ic.ClientCxt, p.Id, &options.Signal) + if err != nil { + report := entities.PodKillReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { + var ( + reports []*entities.PodPauseReport + ) + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Pause(ic.ClientCxt, p.Id) + if err != nil { + report := entities.PodPauseReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) { + var ( + reports []*entities.PodUnpauseReport + ) + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Unpause(ic.ClientCxt, p.Id) + if err != nil { + report := entities.PodUnpauseReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { + var ( + reports []*entities.PodStopReport + timeout int = -1 + ) + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + if options.Timeout != -1 { + timeout = options.Timeout + } + for _, p := range foundPods { + response, err := pods.Stop(ic.ClientCxt, p.Id, &timeout) + if err != nil { + report := entities.PodStopReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) { + var reports []*entities.PodRestartReport + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Restart(ic.ClientCxt, p.Id) + if err != nil { + report := entities.PodRestartReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) { + var reports []*entities.PodStartReport + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Start(ic.ClientCxt, p.Id) + if err != nil { + report := entities.PodStartReport{ + Errs: []error{err}, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} + +func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { + var reports []*entities.PodRmReport + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, p := range foundPods { + response, err := pods.Remove(ic.ClientCxt, p.Id, &options.Force) + if err != nil { + report := entities.PodRmReport{ + Err: err, + Id: p.Id, + } + reports = append(reports, &report) + continue + } + reports = append(reports, response) + } + return reports, nil +} diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index ab345b8f2..1dc094bd4 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -14,8 +14,8 @@ t GET libpod/pods/foo/json 200 \ .Config.id=$pod_id \ .Containers=null t GET libpod/pods/json 200 \ - .[0].Config.name=foo \ - .[0].Config.id=$pod_id \ + .[0].Name=foo \ + .[0].Id=$pod_id \ .[0].Containers=null # Cannot create a dup pod with the same name @@ -24,7 +24,7 @@ t POST libpod/pods/create name=foo 409 .cause="pod already exists" #t POST libpod/pods/create a=b 400 .cause='bad parameter' # FIXME: unimplemented if root || have_cgroupsv2; then - t POST libpod/pods/foo/pause '' 204 + t POST libpod/pods/foo/pause '' 200 else # Rootless cgroupsv1 : unsupported t POST libpod/pods/foo/pause '' 500 \ @@ -44,7 +44,7 @@ t POST libpod/pods/bar/restart '' 404 #t POST libpod/pods/prune 'a=b' 400 # FIXME: 2020-02-24 returns 200 # Clean up; and try twice, making sure that the second time fails -t DELETE libpod/pods/foo 204 +t DELETE libpod/pods/foo 200 t DELETE libpod/pods/foo 404 # vim: filetype=sh |