summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.golangci.yml1
-rw-r--r--cmd/podman/shared/pod.go18
-rw-r--r--cmd/podmanV2/Makefile2
-rw-r--r--cmd/podmanV2/common/netflags.go108
-rw-r--r--cmd/podmanV2/common/types.go3
-rw-r--r--cmd/podmanV2/common/util.go43
-rw-r--r--cmd/podmanV2/containers/inspect.go53
-rw-r--r--cmd/podmanV2/containers/kill.go9
-rw-r--r--cmd/podmanV2/containers/pause.go9
-rw-r--r--cmd/podmanV2/containers/restart.go9
-rw-r--r--cmd/podmanV2/containers/rm.go9
-rw-r--r--cmd/podmanV2/containers/stop.go9
-rw-r--r--cmd/podmanV2/containers/top.go91
-rw-r--r--cmd/podmanV2/containers/unpause.go9
-rw-r--r--cmd/podmanV2/containers/wait.go9
-rw-r--r--cmd/podmanV2/images/exists.go40
-rw-r--r--cmd/podmanV2/images/image.go6
-rw-r--r--cmd/podmanV2/images/images.go14
-rw-r--r--cmd/podmanV2/images/list.go2
-rw-r--r--cmd/podmanV2/images/prune.go86
-rw-r--r--cmd/podmanV2/images/rm.go70
-rw-r--r--cmd/podmanV2/images/rmi.go30
-rw-r--r--cmd/podmanV2/main.go8
-rw-r--r--cmd/podmanV2/pods/create.go132
-rw-r--r--cmd/podmanV2/pods/kill.go68
-rw-r--r--cmd/podmanV2/pods/pause.go66
-rw-r--r--cmd/podmanV2/pods/restart.go68
-rw-r--r--cmd/podmanV2/pods/rm.go71
-rw-r--r--cmd/podmanV2/pods/start.go68
-rw-r--r--cmd/podmanV2/pods/stop.go78
-rw-r--r--cmd/podmanV2/pods/unpause.go66
-rw-r--r--cmd/podmanV2/registry/registry.go2
-rw-r--r--cmd/podmanV2/root.go72
-rw-r--r--docs/source/markdown/podman-create.1.md2
-rw-r--r--libpod.conf2
-rw-r--r--libpod/container_exec.go79
-rw-r--r--libpod/container_inspect.go652
-rw-r--r--libpod/define/config.go2
-rw-r--r--libpod/define/container_inspect.go624
-rw-r--r--libpod/define/inspect.go54
-rw-r--r--libpod/healthcheck.go32
-rw-r--r--libpod/networking_linux.go45
-rw-r--r--libpod/networking_unsupported.go2
-rw-r--r--libpod/oci_conmon_linux.go1
-rw-r--r--libpod/options.go2
-rw-r--r--libpod/runtime_ctr.go18
-rw-r--r--libpod/runtime_volume.go11
-rw-r--r--pkg/adapter/containers_remote.go4
-rw-r--r--pkg/adapter/runtime.go8
-rw-r--r--pkg/api/handlers/compat/exec.go107
-rw-r--r--pkg/api/handlers/compat/images.go3
-rw-r--r--pkg/api/handlers/compat/images_remove.go22
-rw-r--r--pkg/api/handlers/libpod/images.go7
-rw-r--r--pkg/api/handlers/libpod/pods.go186
-rw-r--r--pkg/api/handlers/libpod/swagger.go50
-rw-r--r--pkg/api/handlers/libpod/volumes.go9
-rw-r--r--pkg/api/handlers/swagger.go6
-rw-r--r--pkg/api/handlers/types.go21
-rw-r--r--pkg/api/handlers/utils/pods.go49
-rw-r--r--pkg/api/server/register_exec.go16
-rw-r--r--pkg/api/server/register_pods.go30
-rw-r--r--pkg/api/server/swagger.go3
-rw-r--r--pkg/bindings/containers/containers.go42
-rw-r--r--pkg/bindings/containers/exec.go71
-rw-r--r--pkg/bindings/containers/healthcheck.go6
-rw-r--r--pkg/bindings/images/images.go7
-rw-r--r--pkg/bindings/pods/pods.go106
-rw-r--r--pkg/bindings/test/containers_test.go34
-rw-r--r--pkg/bindings/test/exec_test.go77
-rw-r--r--pkg/bindings/test/images_test.go40
-rw-r--r--pkg/bindings/test/pods_test.go65
-rw-r--r--pkg/domain/entities/containers.go25
-rw-r--r--pkg/domain/entities/engine_container.go11
-rw-r--r--pkg/domain/entities/engine_image.go3
-rw-r--r--pkg/domain/entities/images.go19
-rw-r--r--pkg/domain/entities/pods.go143
-rw-r--r--pkg/domain/entities/types.go27
-rw-r--r--pkg/domain/infra/abi/containers.go38
-rw-r--r--pkg/domain/infra/abi/images.go92
-rw-r--r--pkg/domain/infra/abi/pods.go235
-rw-r--r--pkg/domain/infra/abi/volumes.go19
-rw-r--r--pkg/domain/infra/tunnel/containers.go34
-rw-r--r--pkg/domain/infra/tunnel/helpers.go34
-rw-r--r--pkg/domain/infra/tunnel/images.go45
-rw-r--r--pkg/domain/infra/tunnel/pods.go166
-rw-r--r--pkg/specgen/container_create.go (renamed from pkg/specgen/create.go)2
-rw-r--r--pkg/specgen/container_validate.go (renamed from pkg/specgen/validate.go)18
-rw-r--r--pkg/specgen/pod_create.go83
-rw-r--r--pkg/specgen/pod_validate.go104
-rw-r--r--pkg/specgen/podspecgen.go (renamed from pkg/specgen/pod.go)13
-rw-r--r--pkg/specgen/specgen.go6
-rw-r--r--pkg/varlinkapi/volumes.go10
-rw-r--r--test/apiv2/40-pods.at14
-rw-r--r--test/e2e/common_test.go22
-rw-r--r--test/e2e/config.go2
-rw-r--r--test/e2e/pod_ps_test.go23
-rw-r--r--test/endpoint/endpoint.go2
97 files changed, 3893 insertions, 1121 deletions
diff --git a/.golangci.yml b/.golangci.yml
index 8b1062b4c..8af8aea91 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -4,6 +4,7 @@ run:
- apparmor
- seccomp
- selinux
+ - ABISupport
concurrency: 6
deadline: 5m
skip-dirs-use-default: true
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 3046953b5..50bd88e08 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -162,7 +162,7 @@ func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter)
func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) {
var filterFuncs []libpod.PodFilter
for _, f := range filters {
- filterSplit := strings.Split(f, "=")
+ filterSplit := strings.SplitN(f, "=", 2)
if len(filterSplit) < 2 {
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
@@ -256,6 +256,22 @@ func generatePodFilterFuncs(filter, filterValue string) (
}
return false
}, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(p *libpod.Pod) bool {
+ for labelKey, labelValue := range p.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile
new file mode 100644
index 000000000..147a78d9c
--- /dev/null
+++ b/cmd/podmanV2/Makefile
@@ -0,0 +1,2 @@
+all:
+ GO111MODULE=off go build -tags 'ABISupport'
diff --git a/cmd/podmanV2/common/netflags.go b/cmd/podmanV2/common/netflags.go
new file mode 100644
index 000000000..758f155c8
--- /dev/null
+++ b/cmd/podmanV2/common/netflags.go
@@ -0,0 +1,108 @@
+package common
+
+import (
+ "net"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+func getDefaultNetwork() string {
+ if rootless.IsRootless() {
+ return "slirp4netns"
+ }
+ return "bridge"
+}
+
+func GetNetFlags() *pflag.FlagSet {
+ netFlags := pflag.FlagSet{}
+ netFlags.StringSlice(
+ "add-host", []string{},
+ "Add a custom host-to-IP mapping (host:ip) (default [])",
+ )
+ netFlags.StringSlice(
+ "dns", []string{},
+ "Set custom DNS servers",
+ )
+ netFlags.StringSlice(
+ "dns-opt", []string{},
+ "Set custom DNS options",
+ )
+ netFlags.StringSlice(
+ "dns-search", []string{},
+ "Set custom DNS search domains",
+ )
+ netFlags.String(
+ "ip", "",
+ "Specify a static IPv4 address for the container",
+ )
+ netFlags.String(
+ "mac-address", "",
+ "Container MAC address (e.g. 92:d0:c6:0a:29:33)",
+ )
+ netFlags.String(
+ "network", getDefaultNetwork(),
+ "Connect a container to a network",
+ )
+ netFlags.StringSliceP(
+ "publish", "p", []string{},
+ "Publish a container's port, or a range of ports, to the host (default [])",
+ )
+ netFlags.Bool(
+ "no-hosts", false,
+ "Do not create /etc/hosts within the container, instead use the version from the image",
+ )
+ return &netFlags
+}
+
+func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
+ var (
+ err error
+ )
+ opts := entities.NetOptions{}
+ opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host")
+ if err != nil {
+ return nil, err
+ }
+ servers, err := cmd.Flags().GetStringSlice("dns")
+ if err != nil {
+ return nil, err
+ }
+ for _, d := range servers {
+ if d == "none" {
+ opts.DNSHost = true
+ break
+ }
+ opts.DNSServers = append(opts.DNSServers, net.ParseIP(d))
+ }
+ opts.DNSSearch, err = cmd.Flags().GetStringSlice("dns-search")
+ if err != nil {
+ return nil, err
+ }
+
+ m, err := cmd.Flags().GetString("mac-address")
+ if err != nil {
+ return nil, err
+ }
+ if len(m) > 0 {
+ mac, err := net.ParseMAC(m)
+ if err != nil {
+ return nil, err
+ }
+ opts.StaticMAC = &mac
+ }
+ inputPorts, err := cmd.Flags().GetStringSlice("publish")
+ if err != nil {
+ return nil, err
+ }
+ if len(inputPorts) > 0 {
+ opts.PublishPorts, err = createPortBindings(inputPorts)
+ if err != nil {
+ return nil, err
+ }
+ }
+ opts.NoHosts, err = cmd.Flags().GetBool("no-hosts")
+ return &opts, err
+}
diff --git a/cmd/podmanV2/common/types.go b/cmd/podmanV2/common/types.go
new file mode 100644
index 000000000..2427ae975
--- /dev/null
+++ b/cmd/podmanV2/common/types.go
@@ -0,0 +1,3 @@
+package common
+
+var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
diff --git a/cmd/podmanV2/common/util.go b/cmd/podmanV2/common/util.go
new file mode 100644
index 000000000..47bbe12fa
--- /dev/null
+++ b/cmd/podmanV2/common/util.go
@@ -0,0 +1,43 @@
+package common
+
+import (
+ "strconv"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ "github.com/pkg/errors"
+)
+
+// createPortBindings iterates ports mappings and exposed ports into a format CNI understands
+func createPortBindings(ports []string) ([]ocicni.PortMapping, error) {
+ // TODO wants someone to rewrite this code in the future
+ var portBindings []ocicni.PortMapping
+ // The conversion from []string to natBindings is temporary while mheon reworks the port
+ // deduplication code. Eventually that step will not be required.
+ _, natBindings, err := nat.ParsePortSpecs(ports)
+ if err != nil {
+ return nil, err
+ }
+ for containerPb, hostPb := range natBindings {
+ var pm ocicni.PortMapping
+ pm.ContainerPort = int32(containerPb.Int())
+ for _, i := range hostPb {
+ var hostPort int
+ var err error
+ pm.HostIP = i.HostIP
+ if i.HostPort == "" {
+ hostPort = containerPb.Int()
+ } else {
+ hostPort, err = strconv.Atoi(i.HostPort)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert host port to integer")
+ }
+ }
+
+ pm.HostPort = int32(hostPort)
+ pm.Protocol = containerPb.Proto()
+ portBindings = append(portBindings, pm)
+ }
+ }
+ return portBindings, nil
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
index 635be4789..648289f0b 100644
--- a/cmd/podmanV2/containers/inspect.go
+++ b/cmd/podmanV2/containers/inspect.go
@@ -1,8 +1,15 @@
package containers
import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/template"
+
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ jsoniter "github.com/json-iterator/go"
"github.com/spf13/cobra"
)
@@ -12,31 +19,57 @@ var (
Use: "inspect [flags] CONTAINER",
Short: "Display the configuration of a container",
Long: `Displays the low-level information on a container identified by name or ID.`,
- PreRunE: inspectPreRunE,
+ PreRunE: preRunE,
RunE: inspect,
Example: `podman container inspect myCtr
podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
}
)
+var (
+ inspectOptions entities.ContainerInspectOptions
+)
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: inspectCmd,
Parent: containerCmd,
})
-}
-
-func inspectPreRunE(cmd *cobra.Command, args []string) (err error) {
- err = preRunE(cmd, args)
- if err != nil {
- return
+ flags := inspectCmd.Flags()
+ flags.StringVarP(&inspectOptions.Format, "format", "f", "", "Change the output format to a Go template")
+ flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVarP(&inspectOptions.Size, "size", "s", false, "Display total file size")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
}
-
- _, err = registry.NewImageEngine(cmd, args)
- return err
}
func inspect(cmd *cobra.Command, args []string) error {
+ responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, inspectOptions)
+ if err != nil {
+ return err
+ }
+ if inspectOptions.Format == "" {
+ b, err := jsoniter.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ format := inspectOptions.Format
+ if !strings.HasSuffix(format, "\n") {
+ format += "\n"
+ }
+ tmpl, err := template.New("inspect").Parse(format)
+ if err != nil {
+ return err
+ }
+ for _, i := range responses {
+ if err := tmpl.Execute(os.Stdout, i); err != nil {
+ return err
+ }
+ }
return nil
}
diff --git a/cmd/podmanV2/containers/kill.go b/cmd/podmanV2/containers/kill.go
index 36c4f2528..6e6debfec 100644
--- a/cmd/podmanV2/containers/kill.go
+++ b/cmd/podmanV2/containers/kill.go
@@ -15,10 +15,11 @@ import (
var (
killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal."
killCommand = &cobra.Command{
- Use: "kill [flags] CONTAINER [CONTAINER...]",
- Short: "Kill one or more running containers with a specific signal",
- Long: killDescription,
- RunE: kill,
+ Use: "kill [flags] CONTAINER [CONTAINER...]",
+ Short: "Kill one or more running containers with a specific signal",
+ Long: killDescription,
+ RunE: kill,
+ PersistentPreRunE: preRunE,
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
},
diff --git a/cmd/podmanV2/containers/pause.go b/cmd/podmanV2/containers/pause.go
index 99ca34d5c..a9b91b68f 100644
--- a/cmd/podmanV2/containers/pause.go
+++ b/cmd/podmanV2/containers/pause.go
@@ -15,10 +15,11 @@ import (
var (
pauseDescription = `Pauses one or more running containers. The container name or ID can be used.`
pauseCommand = &cobra.Command{
- Use: "pause [flags] CONTAINER [CONTAINER...]",
- Short: "Pause all the processes in one or more containers",
- Long: pauseDescription,
- RunE: pause,
+ Use: "pause [flags] CONTAINER [CONTAINER...]",
+ Short: "Pause all the processes in one or more containers",
+ Long: pauseDescription,
+ RunE: pause,
+ PersistentPreRunE: preRunE,
Example: `podman pause mywebserver
podman pause 860a4b23
podman pause -a`,
diff --git a/cmd/podmanV2/containers/restart.go b/cmd/podmanV2/containers/restart.go
index 216824389..053891f79 100644
--- a/cmd/podmanV2/containers/restart.go
+++ b/cmd/podmanV2/containers/restart.go
@@ -18,10 +18,11 @@ var (
A timeout before forcibly stopping can be set, but defaults to 10 seconds.`
restartCommand = &cobra.Command{
- Use: "restart [flags] CONTAINER [CONTAINER...]",
- Short: "Restart one or more containers",
- Long: restartDescription,
- RunE: restart,
+ Use: "restart [flags] CONTAINER [CONTAINER...]",
+ Short: "Restart one or more containers",
+ Long: restartDescription,
+ RunE: restart,
+ PersistentPreRunE: preRunE,
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
},
diff --git a/cmd/podmanV2/containers/rm.go b/cmd/podmanV2/containers/rm.go
index 916dd2577..75655e4cd 100644
--- a/cmd/podmanV2/containers/rm.go
+++ b/cmd/podmanV2/containers/rm.go
@@ -19,10 +19,11 @@ var (
Command does not remove images. Running or unusable containers will not be removed without the -f option.`
rmCommand = &cobra.Command{
- Use: "rm [flags] CONTAINER [CONTAINER...]",
- Short: "Remove one or more containers",
- Long: rmDescription,
- RunE: rm,
+ Use: "rm [flags] CONTAINER [CONTAINER...]",
+ Short: "Remove one or more containers",
+ Long: rmDescription,
+ RunE: rm,
+ PersistentPreRunE: preRunE,
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, false, true)
},
diff --git a/cmd/podmanV2/containers/stop.go b/cmd/podmanV2/containers/stop.go
index b6d70784d..58d47fd52 100644
--- a/cmd/podmanV2/containers/stop.go
+++ b/cmd/podmanV2/containers/stop.go
@@ -18,10 +18,11 @@ var (
A timeout to forcibly stop the container can also be set but defaults to 10 seconds otherwise.`
stopCommand = &cobra.Command{
- Use: "stop [flags] CONTAINER [CONTAINER...]",
- Short: "Stop one or more containers",
- Long: stopDescription,
- RunE: stop,
+ Use: "stop [flags] CONTAINER [CONTAINER...]",
+ Short: "Stop one or more containers",
+ Long: stopDescription,
+ RunE: stop,
+ PersistentPreRunE: preRunE,
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, false, true)
},
diff --git a/cmd/podmanV2/containers/top.go b/cmd/podmanV2/containers/top.go
new file mode 100644
index 000000000..a86c12e2a
--- /dev/null
+++ b/cmd/podmanV2/containers/top.go
@@ -0,0 +1,91 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ topDescription = fmt.Sprintf(`Similar to system "top" command.
+
+ Specify format descriptors to alter the output.
+
+ Running "podman top -l pid pcpu seccomp" will print the process ID, the CPU percentage and the seccomp mode of each process of the latest container.
+ Format Descriptors:
+ %s`, strings.Join(psgo.ListDescriptors(), ","))
+
+ topOptions = entities.TopOptions{}
+
+ topCommand = &cobra.Command{
+ Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS]",
+ Short: "Display the running processes of a container",
+ Long: topDescription,
+ PersistentPreRunE: preRunE,
+ RunE: top,
+ Args: cobra.ArbitraryArgs,
+ Example: `podman top ctrID
+podman top --latest
+podman top ctrID pid seccomp args %C
+podman top ctrID -eo user,pid,comm`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: topCommand,
+ })
+
+ topCommand.SetHelpTemplate(registry.HelpTemplate())
+ topCommand.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := topCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "")
+ flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+
+ _ = flags.MarkHidden("list-descriptors") // meant only for bash completion
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func top(cmd *cobra.Command, args []string) error {
+ if topOptions.ListDescriptors {
+ fmt.Println(strings.Join(psgo.ListDescriptors(), "\n"))
+ return nil
+ }
+
+ if len(args) < 1 && !topOptions.Latest {
+ return errors.Errorf("you must provide the name or id of a running container")
+ }
+
+ if topOptions.Latest {
+ topOptions.Descriptors = args
+ } else {
+ topOptions.NameOrID = args[0]
+ topOptions.Descriptors = args[1:]
+ }
+
+ topResponse, err := registry.ContainerEngine().ContainerTop(context.Background(), topOptions)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
+ for _, proc := range topResponse.Value {
+ if _, err := fmt.Fprintln(w, proc); err != nil {
+ return err
+ }
+ }
+ return w.Flush()
+}
diff --git a/cmd/podmanV2/containers/unpause.go b/cmd/podmanV2/containers/unpause.go
index 8b8cde9b2..6a3179f10 100644
--- a/cmd/podmanV2/containers/unpause.go
+++ b/cmd/podmanV2/containers/unpause.go
@@ -15,10 +15,11 @@ import (
var (
unpauseDescription = `Unpauses one or more previously paused containers. The container name or ID can be used.`
unpauseCommand = &cobra.Command{
- Use: "unpause [flags] CONTAINER [CONTAINER...]",
- Short: "Unpause the processes in one or more containers",
- Long: unpauseDescription,
- RunE: unpause,
+ Use: "unpause [flags] CONTAINER [CONTAINER...]",
+ Short: "Unpause the processes in one or more containers",
+ Long: unpauseDescription,
+ RunE: unpause,
+ PersistentPreRunE: preRunE,
Example: `podman unpause ctrID
podman unpause --all`,
}
diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go
index 4367fd9d9..bf3c86200 100644
--- a/cmd/podmanV2/containers/wait.go
+++ b/cmd/podmanV2/containers/wait.go
@@ -18,10 +18,11 @@ 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,
+ Use: "wait [flags] CONTAINER [CONTAINER...]",
+ Short: "Block on one or more containers",
+ Long: waitDescription,
+ RunE: wait,
+ PersistentPreRunE: preRunE,
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
},
diff --git a/cmd/podmanV2/images/exists.go b/cmd/podmanV2/images/exists.go
new file mode 100644
index 000000000..d35d6825e
--- /dev/null
+++ b/cmd/podmanV2/images/exists.go
@@ -0,0 +1,40 @@
+package images
+
+import (
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ existsCmd = &cobra.Command{
+ Use: "exists IMAGE",
+ Short: "Check if an image exists in local storage",
+ Long: `If the named image exists in local storage, podman image exists exits with 0, otherwise the exit code will be 1.`,
+ Args: cobra.ExactArgs(1),
+ RunE: exists,
+ Example: `podman image exists ID
+ podman image exists IMAGE && podman pull IMAGE`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCmd,
+ Parent: imageCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0])
+ if err != nil {
+ return err
+ }
+ if !found.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go
index a15c3e826..9fc7b21d1 100644
--- a/cmd/podmanV2/images/image.go
+++ b/cmd/podmanV2/images/image.go
@@ -28,6 +28,8 @@ func init() {
}
func preRunE(cmd *cobra.Command, args []string) error {
- _, err := registry.NewImageEngine(cmd, args)
- return err
+ if _, err := registry.NewImageEngine(cmd, args); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go
index 719846b4c..d00f0996e 100644
--- a/cmd/podmanV2/images/images.go
+++ b/cmd/podmanV2/images/images.go
@@ -11,13 +11,13 @@ import (
var (
// podman _images_ Alias for podman image _list_
imagesCmd = &cobra.Command{
- Use: strings.Replace(listCmd.Use, "list", "images", 1),
- Args: listCmd.Args,
- Short: listCmd.Short,
- Long: listCmd.Long,
- PersistentPreRunE: preRunE,
- RunE: listCmd.RunE,
- Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ Use: strings.Replace(listCmd.Use, "list", "images", 1),
+ Args: listCmd.Args,
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PreRunE: preRunE,
+ RunE: listCmd.RunE,
+ Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
}
)
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
index 4714af3e4..9a5b47299 100644
--- a/cmd/podmanV2/images/list.go
+++ b/cmd/podmanV2/images/list.go
@@ -212,7 +212,7 @@ func imageListFormat(flags listFlagType) (string, string) {
row += "\t{{.Digest}}"
}
- hdr += "\tID"
+ hdr += "\tIMAGE ID"
if flags.noTrunc {
row += "\tsha256:{{.ID}}"
} else {
diff --git a/cmd/podmanV2/images/prune.go b/cmd/podmanV2/images/prune.go
new file mode 100644
index 000000000..6577c458e
--- /dev/null
+++ b/cmd/podmanV2/images/prune.go
@@ -0,0 +1,86 @@
+package images
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ pruneDescription = `Removes all unnamed images from local storage.
+
+ If an image is not being used by a container, it will be removed from the system.`
+ pruneCmd = &cobra.Command{
+ Use: "prune",
+ Args: cobra.NoArgs,
+ Short: "Remove unused images",
+ Long: pruneDescription,
+ RunE: prune,
+ Example: `podman image prune`,
+ }
+
+ pruneOpts = entities.ImagePruneOptions{}
+ force bool
+ filter = []string{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pruneCmd,
+ Parent: imageCmd,
+ })
+
+ flags := pruneCmd.Flags()
+ flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all unused images, not just dangling ones")
+ flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")
+ flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
+
+}
+
+func prune(cmd *cobra.Command, args []string) error {
+ if !force {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Printf(`
+WARNING! This will remove all dangling images.
+Are you sure you want to continue? [y/N] `)
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ return errors.Wrapf(err, "error reading input")
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ return nil
+ }
+ }
+
+ // TODO Remove once filter refactor is finished and url.Values rules :)
+ for _, f := range filter {
+ t := strings.SplitN(f, "=", 2)
+ pruneOpts.Filters.Add(t[0], t[1])
+ }
+
+ results, err := registry.ImageEngine().Prune(registry.GetContext(), pruneOpts)
+ if err != nil {
+ return err
+ }
+
+ for _, i := range results.Report.Id {
+ fmt.Println(i)
+ }
+
+ for _, e := range results.Report.Err {
+ fmt.Fprint(os.Stderr, e.Error()+"\n")
+ }
+
+ if results.Size > 0 {
+ fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size)
+ }
+
+ return nil
+}
diff --git a/cmd/podmanV2/images/rm.go b/cmd/podmanV2/images/rm.go
new file mode 100644
index 000000000..bb5880de3
--- /dev/null
+++ b/cmd/podmanV2/images/rm.go
@@ -0,0 +1,70 @@
+package images
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ rmDescription = "Removes one or more previously pulled or locally created images."
+ rmCmd = &cobra.Command{
+ Use: "rm [flags] IMAGE [IMAGE...]",
+ Short: "Removes one or more images from local storage",
+ Long: rmDescription,
+ PreRunE: preRunE,
+ RunE: rm,
+ Example: `podman image rm imageID
+ podman image rm --force alpine
+ podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
+ }
+
+ imageOpts = entities.ImageDeleteOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: rmCmd,
+ Parent: imageCmd,
+ })
+
+ flags := rmCmd.Flags()
+ flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images")
+ flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image")
+}
+
+func rm(cmd *cobra.Command, args []string) error {
+
+ if len(args) < 1 && !imageOpts.All {
+ return errors.Errorf("image name or ID must be specified")
+ }
+ if len(args) > 0 && imageOpts.All {
+ return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
+ }
+
+ report, err := registry.ImageEngine().Delete(registry.GetContext(), args, imageOpts)
+ if err != nil {
+ switch {
+ case report != nil && report.ImageNotFound != nil:
+ fmt.Fprintln(os.Stderr, err.Error())
+ registry.SetExitCode(2)
+ case report != nil && report.ImageInUse != nil:
+ fmt.Fprintln(os.Stderr, err.Error())
+ default:
+ return err
+ }
+ }
+
+ for _, u := range report.Untagged {
+ fmt.Println("Untagged: " + u)
+ }
+ for _, d := range report.Deleted {
+ fmt.Println("Deleted: " + d)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/rmi.go b/cmd/podmanV2/images/rmi.go
new file mode 100644
index 000000000..7f9297bc9
--- /dev/null
+++ b/cmd/podmanV2/images/rmi.go
@@ -0,0 +1,30 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ rmiCmd = &cobra.Command{
+ Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1),
+ Args: rmCmd.Args,
+ Short: rmCmd.Short,
+ Long: rmCmd.Long,
+ PreRunE: rmCmd.PreRunE,
+ RunE: rmCmd.RunE,
+ Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: rmiCmd,
+ })
+ rmiCmd.SetHelpTemplate(registry.HelpTemplate())
+ rmiCmd.SetUsageTemplate(registry.UsageTemplate())
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index dc96c26d0..bd9fbb25e 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -1,7 +1,6 @@
package main
import (
- "fmt"
"os"
"reflect"
"runtime"
@@ -16,7 +15,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
)
func init() {
@@ -24,10 +22,7 @@ func init() {
logrus.Errorf(err.Error())
os.Exit(1)
}
- initCobra()
-}
-func initCobra() {
switch runtime.GOOS {
case "darwin":
fallthrough
@@ -46,12 +41,9 @@ func initCobra() {
registry.EngineOptions.EngineMode = entities.TunnelMode
}
}
-
- cobra.OnInitialize(func() {})
}
func main() {
- fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
for _, c := range registry.Commands {
if Contains(registry.EngineOptions.EngineMode, c.Mode) {
parent := rootCmd
diff --git a/cmd/podmanV2/pods/create.go b/cmd/podmanV2/pods/create.go
new file mode 100644
index 000000000..ab8957ee3
--- /dev/null
+++ b/cmd/podmanV2/pods/create.go
@@ -0,0 +1,132 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/errorhandling"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ podCreateDescription = `After creating the pod, the pod ID is printed to stdout.
+
+ You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
+
+ createCommand = &cobra.Command{
+ Use: "create",
+ Args: cobra.NoArgs,
+ Short: "Create a new empty pod",
+ Long: podCreateDescription,
+ RunE: create,
+ }
+)
+
+var (
+ createOptions entities.PodCreateOptions
+ labels, labelFile []string
+ podIDFile string
+ share string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ Parent: podCmd,
+ })
+ flags := createCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.AddFlagSet(common.GetNetFlags())
+ flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod")
+ flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with")
+ flags.StringVar(&createOptions.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod")
+ flags.StringVar(&createOptions.InfraCommand, "infra-command", define.DefaultInfraCommand, "The command to run on the infra container when the pod is started")
+ flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels")
+ flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])")
+ flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod")
+ flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod")
+ flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file")
+ flags.StringVar(&share, "share", common.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ podIdFile *os.File
+ )
+ createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+
+ if !createOptions.Infra && cmd.Flag("share").Changed && share != "none" && share != "" {
+ return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
+ }
+ createOptions.Share = strings.Split(share, ",")
+ if cmd.Flag("pod-id-file").Changed {
+ podIdFile, err = util.OpenExclusiveFile(podIDFile)
+ if err != nil && os.IsExist(err) {
+ return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile)
+ }
+ if err != nil {
+ return errors.Errorf("error opening pod-id-file %s", podIDFile)
+ }
+ defer errorhandling.CloseQuiet(podIdFile)
+ defer errorhandling.SyncQuiet(podIdFile)
+ }
+
+ createOptions.Net, err = common.NetFlagsToNetOptions(cmd)
+ if err != nil {
+ return err
+ }
+ netInput, err := cmd.Flags().GetString("network")
+ if err != nil {
+ return err
+ }
+ n := specgen.Namespace{}
+ switch netInput {
+ case "bridge":
+ n.NSMode = specgen.Bridge
+ case "host":
+ n.NSMode = specgen.Host
+ case "slip4netns":
+ n.NSMode = specgen.Slirp
+ default:
+ if strings.HasPrefix(netInput, "container:") { //nolint
+ split := strings.Split(netInput, ":")
+ if len(split) != 2 {
+ return errors.Errorf("invalid network paramater: %q", netInput)
+ }
+ n.NSMode = specgen.FromContainer
+ n.Value = split[1]
+ } else if strings.HasPrefix(netInput, "ns:") {
+ return errors.New("the ns: network option is not supported for pods")
+ } else {
+ n.NSMode = specgen.Bridge
+ createOptions.Net.CNINetworks = strings.Split(netInput, ",")
+ }
+ }
+ if len(createOptions.Net.PublishPorts) > 0 {
+ if !createOptions.Infra {
+ return errors.Errorf("you must have an infra container to publish port bindings to the host")
+ }
+ }
+
+ response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Id)
+ return nil
+}
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/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index f0650a7cf..5cdb8a840 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -10,6 +10,8 @@ import (
"github.com/spf13/cobra"
)
+type CobraFuncs func(cmd *cobra.Command, args []string) error
+
type CliCommand struct {
Mode []entities.EngineMode
Command *cobra.Command
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index 68e8b4531..cb4cb4e00 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -2,24 +2,35 @@ package main
import (
"fmt"
+ "log/syslog"
"os"
"path"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/version"
+ "github.com/sirupsen/logrus"
+ logrusSyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/spf13/cobra"
)
-var rootCmd = &cobra.Command{
- Use: path.Base(os.Args[0]),
- Long: "Manage pods, containers and images",
- SilenceUsage: true,
- SilenceErrors: true,
- TraverseChildren: true,
- RunE: registry.SubCommandExists,
- Version: version.Version,
-}
+var (
+ rootCmd = &cobra.Command{
+ Use: path.Base(os.Args[0]),
+ Long: "Manage pods, containers and images",
+ SilenceUsage: true,
+ SilenceErrors: true,
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ Version: version.Version,
+ }
+
+ logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic")
+ logLevel = "error"
+ useSyslog bool
+)
func init() {
// Override default --help information of `--version` global flag}
@@ -28,6 +39,49 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version of Podman")
rootCmd.PersistentFlags().StringVarP(&registry.EngineOptions.Uri, "remote", "r", "", "URL to access Podman service")
rootCmd.PersistentFlags().StringSliceVar(&registry.EngineOptions.Identities, "identity", []string{}, "path to SSH identity file")
+ rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "error", fmt.Sprintf("Log messages above specified level (%s)", logLevels.String()))
+ rootCmd.PersistentFlags().BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
+
+ cobra.OnInitialize(
+ logging,
+ syslogHook,
+ )
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+ return nil
+}
+
+func logging() {
+ if !logLevels.Contains(logLevel) {
+ fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String())
+ os.Exit(1)
+ }
+
+ level, err := logrus.ParseLevel(logLevel)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ logrus.SetLevel(level)
+
+ if logrus.IsLevelEnabled(logrus.InfoLevel) {
+ logrus.Infof("%s filtering at log level %s", os.Args[0], logrus.GetLevel())
+ }
+}
+
+func syslogHook() {
+ if useSyslog {
+ hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+ if err != nil {
+ logrus.WithError(err).Error("Failed to initialize syslog hook")
+ }
+ if err == nil {
+ logrus.AddHook(hook)
+ }
+ }
}
func Execute() {
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index aa2456836..63c63dcd2 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -44,7 +44,7 @@ each of stdin, stdout, and stderr.
**--authfile**=*path*
-Path of the authentication file. Default is ${XDG_\RUNTIME\_DIR}/containers/auth.json
+Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
environment variable. `export REGISTRY_AUTH_FILE=path` (Not available for remote commands)
diff --git a/libpod.conf b/libpod.conf
index 7e0228c19..1bc31eb4c 100644
--- a/libpod.conf
+++ b/libpod.conf
@@ -74,7 +74,7 @@ cni_default_network = "podman"
#namespace = ""
# Default infra (pause) image name for pod infra containers
-infra_image = "k8s.gcr.io/pause:3.1"
+infra_image = "k8s.gcr.io/pause:3.2"
# Default command to run the infra container
infra_command = "/pause"
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index 7ed7a3343..5469462f8 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -94,67 +94,14 @@ func (e *ExecSession) ContainerID() string {
return e.ContainerId
}
-// InspectExecSession contains information about a given exec session.
-type InspectExecSession struct {
- // CanRemove is legacy and used purely for compatibility reasons.
- // Will always be set to true, unless the exec session is running.
- CanRemove bool `json:"CanRemove"`
- // ContainerID is the ID of the container this exec session is attached
- // to.
- ContainerID string `json:"ContainerID"`
- // DetachKeys are the detach keys used by the exec session.
- // If set to "" the default keys are being used.
- // Will show "<none>" if no detach keys are set.
- DetachKeys string `json:"DetachKeys"`
- // ExitCode is the exit code of the exec session. Will be set to 0 if
- // the exec session has not yet exited.
- ExitCode int `json:"ExitCode"`
- // ID is the ID of the exec session.
- ID string `json:"ID"`
- // OpenStderr is whether the container's STDERR stream will be attached.
- // Always set to true if the exec session created a TTY.
- OpenStderr bool `json:"OpenStderr"`
- // OpenStdin is whether the container's STDIN stream will be attached
- // to.
- OpenStdin bool `json:"OpenStdin"`
- // OpenStdout is whether the container's STDOUT stream will be attached.
- // Always set to true if the exec session created a TTY.
- OpenStdout bool `json:"OpenStdout"`
- // Running is whether the exec session is running.
- Running bool `json:"Running"`
- // Pid is the PID of the exec session's process.
- // Will be set to 0 if the exec session is not running.
- Pid int `json:"Pid"`
- // ProcessConfig contains information about the exec session's process.
- ProcessConfig *InspectExecProcess `json:"ProcessConfig"`
-}
-
-// InspectExecProcess contains information about the process in a given exec
-// session.
-type InspectExecProcess struct {
- // Arguments are the arguments to the entrypoint command of the exec
- // session.
- Arguments []string `json:"arguments"`
- // Entrypoint is the entrypoint for the exec session (the command that
- // will be executed in the container).
- Entrypoint string `json:"entrypoint"`
- // Privileged is whether the exec session will be started with elevated
- // privileges.
- Privileged bool `json:"privileged"`
- // Tty is whether the exec session created a terminal.
- Tty bool `json:"tty"`
- // User is the user the exec session was started as.
- User string `json:"user"`
-}
-
// Inspect inspects the given exec session and produces detailed output on its
// configuration and current state.
-func (e *ExecSession) Inspect() (*InspectExecSession, error) {
+func (e *ExecSession) Inspect() (*define.InspectExecSession, error) {
if e.Config == nil {
return nil, errors.Wrapf(define.ErrInternal, "given exec session does not have a configuration block")
}
- output := new(InspectExecSession)
+ output := new(define.InspectExecSession)
output.CanRemove = e.State != define.ExecStateRunning
output.ContainerID = e.ContainerId
if e.Config.DetachKeys != nil {
@@ -167,7 +114,7 @@ func (e *ExecSession) Inspect() (*InspectExecSession, error) {
output.OpenStdout = e.Config.AttachStdout
output.Running = e.State == define.ExecStateRunning
output.Pid = e.PID
- output.ProcessConfig = new(InspectExecProcess)
+ output.ProcessConfig = new(define.InspectExecProcess)
if len(e.Config.Command) > 0 {
output.ProcessConfig.Entrypoint = e.Config.Command[0]
if len(e.Config.Command) > 1 {
@@ -213,6 +160,11 @@ func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
return "", errors.Wrapf(define.ErrInvalidArg, "cannot specify streams to attach to when exec session has a pseudoterminal")
}
+ // Verify that we are in a good state to continue
+ if !c.ensureState(define.ContainerStateRunning) {
+ return "", errors.Wrapf(define.ErrCtrStateInvalid, "can only create exec sessions on running containers")
+ }
+
// Generate an ID for our new exec session
sessionID := stringid.GenerateNonCryptoID()
found := true
@@ -279,6 +231,11 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *AttachStreams)
}
}
+ // Verify that we are in a good state to continue
+ if !c.ensureState(define.ContainerStateRunning) {
+ return errors.Wrapf(define.ErrCtrStateInvalid, "can only start exec sessions when their container is running")
+ }
+
session, ok := c.state.ExecSessions[sessionID]
if !ok {
return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
@@ -575,7 +532,7 @@ func (c *Container) ExecResize(sessionID string, newSize remotecommand.TerminalS
return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
}
- logrus.Infof("Removing container %s exec session %s", c.ID(), session.ID())
+ logrus.Infof("Resizing container %s exec session %s to %+v", c.ID(), session.ID(), newSize)
if session.State != define.ExecStateRunning {
return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it is not running", c.ID(), session.ID())
@@ -592,9 +549,6 @@ func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-ch
if err != nil {
return -1, err
}
- if err := c.ExecStartAndAttach(sessionID, streams); err != nil {
- return -1, err
- }
// Start resizing if we have a resize channel.
// This goroutine may likely leak, given that we cannot close it here.
@@ -605,6 +559,7 @@ func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-ch
// session.
if resize != nil {
go func() {
+ logrus.Debugf("Sending resize events to exec session %s", sessionID)
for resizeRequest := range resize {
if err := c.ExecResize(sessionID, resizeRequest); err != nil {
// Assume the exec session went down.
@@ -615,6 +570,10 @@ func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-ch
}()
}
+ if err := c.ExecStartAndAttach(sessionID, streams); err != nil {
+ return -1, err
+ }
+
session, err := c.ExecSession(sessionID)
if err != nil {
return -1, err
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 50ae72499..7552eb483 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -3,13 +3,10 @@ package libpod
import (
"fmt"
"strings"
- "time"
- "github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/driver"
"github.com/containers/libpod/pkg/util"
- "github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/runtime-tools/validate"
@@ -85,602 +82,9 @@ const (
InspectResponseFalse = "FALSE"
)
-// InspectContainerData provides a detailed record of a container's configuration
-// and state as viewed by Libpod.
-// Large portions of this structure are defined such that the output is
-// compatible with `docker inspect` JSON, but additional fields have been added
-// as required to share information not in the original output.
-type InspectContainerData struct {
- ID string `json:"Id"`
- Created time.Time `json:"Created"`
- Path string `json:"Path"`
- Args []string `json:"Args"`
- State *InspectContainerState `json:"State"`
- Image string `json:"Image"`
- ImageName string `json:"ImageName"`
- Rootfs string `json:"Rootfs"`
- Pod string `json:"Pod"`
- ResolvConfPath string `json:"ResolvConfPath"`
- HostnamePath string `json:"HostnamePath"`
- HostsPath string `json:"HostsPath"`
- StaticDir string `json:"StaticDir"`
- OCIConfigPath string `json:"OCIConfigPath,omitempty"`
- OCIRuntime string `json:"OCIRuntime,omitempty"`
- LogPath string `json:"LogPath"`
- LogTag string `json:"LogTag"`
- ConmonPidFile string `json:"ConmonPidFile"`
- Name string `json:"Name"`
- RestartCount int32 `json:"RestartCount"`
- Driver string `json:"Driver"`
- MountLabel string `json:"MountLabel"`
- ProcessLabel string `json:"ProcessLabel"`
- AppArmorProfile string `json:"AppArmorProfile"`
- EffectiveCaps []string `json:"EffectiveCaps"`
- BoundingCaps []string `json:"BoundingCaps"`
- ExecIDs []string `json:"ExecIDs"`
- GraphDriver *driver.Data `json:"GraphDriver"`
- SizeRw *int64 `json:"SizeRw,omitempty"`
- SizeRootFs int64 `json:"SizeRootFs,omitempty"`
- Mounts []InspectMount `json:"Mounts"`
- Dependencies []string `json:"Dependencies"`
- NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
- ExitCommand []string `json:"ExitCommand"`
- Namespace string `json:"Namespace"`
- IsInfra bool `json:"IsInfra"`
- Config *InspectContainerConfig `json:"Config"`
- HostConfig *InspectContainerHostConfig `json:"HostConfig"`
-}
-
-// InspectContainerConfig holds further data about how a container was initially
-// configured.
-type InspectContainerConfig struct {
- // Container hostname
- Hostname string `json:"Hostname"`
- // Container domain name - unused at present
- DomainName string `json:"Domainname"`
- // User the container was launched with
- User string `json:"User"`
- // Unused, at present
- AttachStdin bool `json:"AttachStdin"`
- // Unused, at present
- AttachStdout bool `json:"AttachStdout"`
- // Unused, at present
- AttachStderr bool `json:"AttachStderr"`
- // Whether the container creates a TTY
- Tty bool `json:"Tty"`
- // Whether the container leaves STDIN open
- OpenStdin bool `json:"OpenStdin"`
- // Whether STDIN is only left open once.
- // Presently not supported by Podman, unused.
- StdinOnce bool `json:"StdinOnce"`
- // Container environment variables
- Env []string `json:"Env"`
- // Container command
- Cmd []string `json:"Cmd"`
- // Container image
- Image string `json:"Image"`
- // Unused, at present. I've never seen this field populated.
- Volumes map[string]struct{} `json:"Volumes"`
- // Container working directory
- WorkingDir string `json:"WorkingDir"`
- // Container entrypoint
- Entrypoint string `json:"Entrypoint"`
- // On-build arguments - presently unused. More of Buildah's domain.
- OnBuild *string `json:"OnBuild"`
- // Container labels
- Labels map[string]string `json:"Labels"`
- // Container annotations
- Annotations map[string]string `json:"Annotations"`
- // Container stop signal
- StopSignal uint `json:"StopSignal"`
- // Configured healthcheck for the container
- Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
- // CreateCommand is the full command plus arguments of the process the
- // container has been created with.
- CreateCommand []string `json:"CreateCommand,omitempty"`
-}
-
-// InspectContainerHostConfig holds information used when the container was
-// created.
-// It's very much a Docker-specific struct, retained (mostly) as-is for
-// compatibility. We fill individual fields as best as we can, inferring as much
-// as possible from the spec and container config.
-// Some things cannot be inferred. These will be populated by spec annotations
-// (if available).
-// Field names are fixed for compatibility and cannot be changed.
-// As such, silence lint warnings about them.
-//nolint
-type InspectContainerHostConfig struct {
- // Binds contains an array of user-added mounts.
- // Both volume mounts and named volumes are included.
- // Tmpfs mounts are NOT included.
- // In 'docker inspect' this is separated into 'Binds' and 'Mounts' based
- // on how a mount was added. We do not make this distinction and do not
- // include a Mounts field in inspect.
- // Format: <src>:<destination>[:<comma-separated options>]
- Binds []string `json:"Binds"`
- // ContainerIDFile is a file created during container creation to hold
- // the ID of the created container.
- // This is not handled within libpod and is stored in an annotation.
- ContainerIDFile string `json:"ContainerIDFile"`
- // LogConfig contains information on the container's logging backend
- LogConfig *InspectLogConfig `json:"LogConfig"`
- // NetworkMode is the configuration of the container's network
- // namespace.
- // Populated as follows:
- // default - A network namespace is being created and configured via CNI
- // none - A network namespace is being created, not configured via CNI
- // host - No network namespace created
- // container:<id> - Using another container's network namespace
- // ns:<path> - A path to a network namespace has been specified
- NetworkMode string `json:"NetworkMode"`
- // PortBindings contains the container's port bindings.
- // It is formatted as map[string][]InspectHostPort.
- // The string key here is formatted as <integer port number>/<protocol>
- // and represents the container port. A single container port may be
- // bound to multiple host ports (on different IPs).
- PortBindings map[string][]InspectHostPort `json:"PortBindings"`
- // RestartPolicy contains the container's restart policy.
- RestartPolicy *InspectRestartPolicy `json:"RestartPolicy"`
- // AutoRemove is whether the container will be automatically removed on
- // exiting.
- // It is not handled directly within libpod and is stored in an
- // annotation.
- AutoRemove bool `json:"AutoRemove"`
- // VolumeDriver is presently unused and is retained for Docker
- // compatibility.
- VolumeDriver string `json:"VolumeDriver"`
- // VolumesFrom is a list of containers which this container uses volumes
- // from. This is not handled directly within libpod and is stored in an
- // annotation.
- // It is formatted as an array of container names and IDs.
- VolumesFrom []string `json:"VolumesFrom"`
- // CapAdd is a list of capabilities added to the container.
- // It is not directly stored by Libpod, and instead computed from the
- // capabilities listed in the container's spec, compared against a set
- // of default capabilities.
- CapAdd []string `json:"CapAdd"`
- // CapDrop is a list of capabilities removed from the container.
- // It is not directly stored by libpod, and instead computed from the
- // capabilities listed in the container's spec, compared against a set
- // of default capabilities.
- CapDrop []string `json:"CapDrop"`
- // Dns is a list of DNS nameservers that will be added to the
- // container's resolv.conf
- Dns []string `json:"Dns"`
- // DnsOptions is a list of DNS options that will be set in the
- // container's resolv.conf
- DnsOptions []string `json:"DnsOptions"`
- // DnsSearch is a list of DNS search domains that will be set in the
- // container's resolv.conf
- DnsSearch []string `json:"DnsSearch"`
- // ExtraHosts contains hosts that will be aded to the container's
- // /etc/hosts.
- ExtraHosts []string `json:"ExtraHosts"`
- // GroupAdd contains groups that the user inside the container will be
- // added to.
- GroupAdd []string `json:"GroupAdd"`
- // IpcMode represents the configuration of the container's IPC
- // namespace.
- // Populated as follows:
- // "" (empty string) - Default, an IPC namespace will be created
- // host - No IPC namespace created
- // container:<id> - Using another container's IPC namespace
- // ns:<path> - A path to an IPC namespace has been specified
- IpcMode string `json:"IpcMode"`
- // Cgroup contains the container's cgroup. It is presently not
- // populated.
- // TODO.
- Cgroup string `json:"Cgroup"`
- // Cgroups contains the container's CGroup mode.
- // Allowed values are "default" (container is creating CGroups) and
- // "disabled" (container is not creating CGroups).
- // This is Libpod-specific and not included in `docker inspect`.
- Cgroups string `json:"Cgroups"`
- // Links is unused, and provided purely for Docker compatibility.
- Links []string `json:"Links"`
- // OOMScoreAdj is an adjustment that will be made to the container's OOM
- // score.
- OomScoreAdj int `json:"OomScoreAdj"`
- // PidMode represents the configuration of the container's PID
- // namespace.
- // Populated as follows:
- // "" (empty string) - Default, a PID namespace will be created
- // host - No PID namespace created
- // container:<id> - Using another container's PID namespace
- // ns:<path> - A path to a PID namespace has been specified
- PidMode string `json:"PidMode"`
- // Privileged indicates whether the container is running with elevated
- // privileges.
- // This has a very specific meaning in the Docker sense, so it's very
- // difficult to decode from the spec and config, and so is stored as an
- // annotation.
- Privileged bool `json:"Privileged"`
- // PublishAllPorts indicates whether image ports are being published.
- // This is not directly stored in libpod and is saved as an annotation.
- PublishAllPorts bool `json:"PublishAllPorts"`
- // ReadonlyRootfs is whether the container will be mounted read-only.
- ReadonlyRootfs bool `json:"ReadonlyRootfs"`
- // SecurityOpt is a list of security-related options that are set in the
- // container.
- SecurityOpt []string `json:"SecurityOpt"`
- // Tmpfs is a list of tmpfs filesystems that will be mounted into the
- // container.
- // It is a map of destination path to options for the mount.
- Tmpfs map[string]string `json:"Tmpfs"`
- // UTSMode represents the configuration of the container's UID
- // namespace.
- // Populated as follows:
- // "" (empty string) - Default, a UTS namespace will be created
- // host - no UTS namespace created
- // container:<id> - Using another container's UTS namespace
- // ns:<path> - A path to a UTS namespace has been specified
- UTSMode string `json:"UTSMode"`
- // UsernsMode represents the configuration of the container's user
- // namespace.
- // When running rootless, a user namespace is created outside of libpod
- // to allow some privileged operations. This will not be reflected here.
- // Populated as follows:
- // "" (empty string) - No user namespace will be created
- // private - The container will be run in a user namespace
- // container:<id> - Using another container's user namespace
- // ns:<path> - A path to a user namespace has been specified
- // TODO Rootless has an additional 'keep-id' option, presently not
- // reflected here.
- UsernsMode string `json:"UsernsMode"`
- // ShmSize is the size of the container's SHM device.
- ShmSize int64 `json:"ShmSize"`
- // Runtime is provided purely for Docker compatibility.
- // It is set unconditionally to "oci" as Podman does not presently
- // support non-OCI runtimes.
- Runtime string `json:"Runtime"`
- // ConsoleSize is an array of 2 integers showing the size of the
- // container's console.
- // It is only set if the container is creating a terminal.
- // TODO.
- ConsoleSize []uint `json:"ConsoleSize"`
- // Isolation is presently unused and provided solely for Docker
- // compatibility.
- Isolation string `json:"Isolation"`
- // CpuShares indicates the CPU resources allocated to the container.
- // It is a relative weight in the scheduler for assigning CPU time
- // versus other CGroups.
- CpuShares uint64 `json:"CpuShares"`
- // Memory indicates the memory resources allocated to the container.
- // This is the limit (in bytes) of RAM the container may use.
- Memory int64 `json:"Memory"`
- // NanoCpus indicates number of CPUs allocated to the container.
- // It is an integer where one full CPU is indicated by 1000000000 (one
- // billion).
- // Thus, 2.5 CPUs (fractional portions of CPUs are allowed) would be
- // 2500000000 (2.5 billion).
- // In 'docker inspect' this is set exclusively of two further options in
- // the output (CpuPeriod and CpuQuota) which are both used to implement
- // this functionality.
- // We can't distinguish here, so if CpuQuota is set to the default of
- // 100000, we will set both CpuQuota, CpuPeriod, and NanoCpus. If
- // CpuQuota is not the default, we will not set NanoCpus.
- NanoCpus int64 `json:"NanoCpus"`
- // CgroupParent is the CGroup parent of the container.
- // Only set if not default.
- CgroupParent string `json:"CgroupParent"`
- // BlkioWeight indicates the I/O resources allocated to the container.
- // It is a relative weight in the scheduler for assigning I/O time
- // versus other CGroups.
- BlkioWeight uint16 `json:"BlkioWeight"`
- // BlkioWeightDevice is an array of I/O resource priorities for
- // individual device nodes.
- // Unfortunately, the spec only stores the device's Major/Minor numbers
- // and not the path, which is used here.
- // Fortunately, the kernel provides an interface for retrieving the path
- // of a given node by major:minor at /sys/dev/. However, the exact path
- // in use may not be what was used in the original CLI invocation -
- // though it is guaranteed that the device node will be the same, and
- // using the given path will be functionally identical.
- BlkioWeightDevice []InspectBlkioWeightDevice `json:"BlkioWeightDevice"`
- // BlkioDeviceReadBps is an array of I/O throttle parameters for
- // individual device nodes.
- // This specifically sets read rate cap in bytes per second for device
- // nodes.
- // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
- // don't guarantee the path will be identical to the original (though
- // the node will be).
- BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadBps"`
- // BlkioDeviceWriteBps is an array of I/O throttle parameters for
- // individual device nodes.
- // this specifically sets write rate cap in bytes per second for device
- // nodes.
- // as with BlkioWeightDevice, we pull the path from /sys/dev, and we
- // don't guarantee the path will be identical to the original (though
- // the node will be).
- BlkioDeviceWriteBps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteBps"`
- // BlkioDeviceReadIOps is an array of I/O throttle parameters for
- // individual device nodes.
- // This specifically sets the read rate cap in iops per second for
- // device nodes.
- // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
- // don't guarantee the path will be identical to the original (though
- // the node will be).
- BlkioDeviceReadIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadIOps"`
- // BlkioDeviceWriteIOps is an array of I/O throttle parameters for
- // individual device nodes.
- // This specifically sets the write rate cap in iops per second for
- // device nodes.
- // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
- // don't guarantee the path will be identical to the original (though
- // the node will be).
- BlkioDeviceWriteIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteIOps"`
- // CpuPeriod is the length of a CPU period in microseconds.
- // It relates directly to CpuQuota.
- CpuPeriod uint64 `json:"CpuPeriod"`
- // CpuPeriod is the amount of time (in microseconds) that a container
- // can use the CPU in every CpuPeriod.
- CpuQuota int64 `json:"CpuQuota"`
- // CpuRealtimePeriod is the length of time (in microseconds) of the CPU
- // realtime period. If set to 0, no time will be allocated to realtime
- // tasks.
- CpuRealtimePeriod uint64 `json:"CpuRealtimePeriod"`
- // CpuRealtimeRuntime is the length of time (in microseconds) allocated
- // for realtime tasks within every CpuRealtimePeriod.
- CpuRealtimeRuntime int64 `json:"CpuRealtimeRuntime"`
- // CpusetCpus is the is the set of CPUs that the container will execute
- // on. Formatted as `0-3` or `0,2`. Default (if unset) is all CPUs.
- CpusetCpus string `json:"CpusetCpus"`
- // CpusetMems is the set of memory nodes the container will use.
- // Formatted as `0-3` or `0,2`. Default (if unset) is all memory nodes.
- CpusetMems string `json:"CpusetMems"`
- // Devices is a list of device nodes that will be added to the
- // container.
- // These are stored in the OCI spec only as type, major, minor while we
- // display the host path. We convert this with /sys/dev, but we cannot
- // guarantee that the host path will be identical - only that the actual
- // device will be.
- Devices []InspectDevice `json:"Devices"`
- // DiskQuota is the maximum amount of disk space the container may use
- // (in bytes).
- // Presently not populated.
- // TODO.
- DiskQuota uint64 `json:"DiskQuota"`
- // KernelMemory is the maximum amount of memory the kernel will devote
- // to the container.
- KernelMemory int64 `json:"KernelMemory"`
- // MemoryReservation is the reservation (soft limit) of memory available
- // to the container. Soft limits are warnings only and can be exceeded.
- MemoryReservation int64 `json:"MemoryReservation"`
- // MemorySwap is the total limit for all memory available to the
- // container, including swap. 0 indicates that there is no limit to the
- // amount of memory available.
- MemorySwap int64 `json:"MemorySwap"`
- // MemorySwappiness is the willingness of the kernel to page container
- // memory to swap. It is an integer from 0 to 100, with low numbers
- // being more likely to be put into swap.
- // -1, the default, will not set swappiness and use the system defaults.
- MemorySwappiness int64 `json:"MemorySwappiness"`
- // OomKillDisable indicates whether the kernel OOM killer is disabled
- // for the container.
- OomKillDisable bool `json:"OomKillDisable"`
- // Init indicates whether the container has an init mounted into it.
- Init bool `json:"Init,omitempty"`
- // PidsLimit is the maximum number of PIDs what may be created within
- // the container. 0, the default, indicates no limit.
- PidsLimit int64 `json:"PidsLimit"`
- // Ulimits is a set of ulimits that will be set within the container.
- Ulimits []InspectUlimit `json:"Ulimits"`
- // CpuCount is Windows-only and not presently implemented.
- CpuCount uint64 `json:"CpuCount"`
- // CpuPercent is Windows-only and not presently implemented.
- CpuPercent uint64 `json:"CpuPercent"`
- // IOMaximumIOps is Windows-only and not presently implemented.
- IOMaximumIOps uint64 `json:"IOMaximumIOps"`
- // IOMaximumBandwidth is Windows-only and not presently implemented.
- IOMaximumBandwidth uint64 `json:"IOMaximumBandwidth"`
-}
-
-// InspectLogConfig holds information about a container's configured log driver
-// and is presently unused. It is retained for Docker compatibility.
-type InspectLogConfig struct {
- Type string `json:"Type"`
- Config map[string]string `json:"Config"` //idk type, TODO
-}
-
-// InspectRestartPolicy holds information about the container's restart policy.
-type InspectRestartPolicy struct {
- // Name contains the container's restart policy.
- // Allowable values are "no" or "" (take no action),
- // "on-failure" (restart on non-zero exit code, with an optional max
- // retry count), and "always" (always restart on container stop, unless
- // explicitly requested by API).
- // Note that this is NOT actually a name of any sort - the poor naming
- // is for Docker compatibility.
- Name string `json:"Name"`
- // MaximumRetryCount is the maximum number of retries allowed if the
- // "on-failure" restart policy is in use. Not used if "on-failure" is
- // not set.
- MaximumRetryCount uint `json:"MaximumRetryCount"`
-}
-
-// InspectBlkioWeightDevice holds information about the relative weight
-// of an individual device node. Weights are used in the I/O scheduler to give
-// relative priority to some accesses.
-type InspectBlkioWeightDevice struct {
- // Path is the path to the device this applies to.
- Path string `json:"Path"`
- // Weight is the relative weight the scheduler will use when scheduling
- // I/O.
- Weight uint16 `json:"Weight"`
-}
-
-// InspectBlkioThrottleDevice holds information about a speed cap for a device
-// node. This cap applies to a specific operation (read, write, etc) on the given
-// node.
-type InspectBlkioThrottleDevice struct {
- // Path is the path to the device this applies to.
- Path string `json:"Path"`
- // Rate is the maximum rate. It is in either bytes per second or iops
- // per second, determined by where it is used - documentation will
- // indicate which is appropriate.
- Rate uint64 `json:"Rate"`
-}
-
-// InspectUlimit is a ulimit that will be applied to the container.
-type InspectUlimit struct {
- // Name is the name (type) of the ulimit.
- Name string `json:"Name"`
- // Soft is the soft limit that will be applied.
- Soft uint64 `json:"Soft"`
- // Hard is the hard limit that will be applied.
- Hard uint64 `json:"Hard"`
-}
-
-// InspectMount provides a record of a single mount in a container. It contains
-// fields for both named and normal volumes. Only user-specified volumes will be
-// included, and tmpfs volumes are not included even if the user specified them.
-type InspectMount struct {
- // Whether the mount is a volume or bind mount. Allowed values are
- // "volume" and "bind".
- Type string `json:"Type"`
- // The name of the volume. Empty for bind mounts.
- Name string `json:"Name,omptempty"`
- // The source directory for the volume.
- Source string `json:"Source"`
- // The destination directory for the volume. Specified as a path within
- // the container, as it would be passed into the OCI runtime.
- Destination string `json:"Destination"`
- // The driver used for the named volume. Empty for bind mounts.
- Driver string `json:"Driver"`
- // Contains SELinux :z/:Z mount options. Unclear what, if anything, else
- // goes in here.
- Mode string `json:"Mode"`
- // All remaining mount options. Additional data, not present in the
- // original output.
- Options []string `json:"Options"`
- // Whether the volume is read-write
- RW bool `json:"RW"`
- // Mount propagation for the mount. Can be empty if not specified, but
- // is always printed - no omitempty.
- Propagation string `json:"Propagation"`
-}
-
-// InspectDevice is a single device that will be mounted into the container.
-type InspectDevice struct {
- // PathOnHost is the path of the device on the host.
- PathOnHost string `json:"PathOnHost"`
- // PathInContainer is the path of the device within the container.
- PathInContainer string `json:"PathInContainer"`
- // CgroupPermissions is the permissions of the mounted device.
- // Presently not populated.
- // TODO.
- CgroupPermissions string `json:"CgroupPermissions"`
-}
-
-// InspectHostPort provides information on a port on the host that a container's
-// port is bound to.
-type InspectHostPort struct {
- // IP on the host we are bound to. "" if not specified (binding to all
- // IPs).
- HostIP string `json:"HostIp"`
- // Port on the host we are bound to. No special formatting - just an
- // integer stuffed into a string.
- HostPort string `json:"HostPort"`
-}
-
-// InspectContainerState provides a detailed record of a container's current
-// state. It is returned as part of InspectContainerData.
-// As with InspectContainerData, many portions of this struct are matched to
-// Docker, but here we see more fields that are unused (nonsensical in the
-// context of Libpod).
-type InspectContainerState struct {
- OciVersion string `json:"OciVersion"`
- Status string `json:"Status"`
- Running bool `json:"Running"`
- Paused bool `json:"Paused"`
- Restarting bool `json:"Restarting"` // TODO
- OOMKilled bool `json:"OOMKilled"`
- Dead bool `json:"Dead"`
- Pid int `json:"Pid"`
- ConmonPid int `json:"ConmonPid,omitempty"`
- ExitCode int32 `json:"ExitCode"`
- Error string `json:"Error"` // TODO
- StartedAt time.Time `json:"StartedAt"`
- FinishedAt time.Time `json:"FinishedAt"`
- Healthcheck HealthCheckResults `json:"Healthcheck,omitempty"`
-}
-
-// InspectBasicNetworkConfig holds basic configuration information (e.g. IP
-// addresses, MAC address, subnet masks, etc) that are common for all networks
-// (both additional and main).
-type InspectBasicNetworkConfig struct {
- // EndpointID is unused, maintained exclusively for compatibility.
- EndpointID string `json:"EndpointID"`
- // Gateway is the IP address of the gateway this network will use.
- Gateway string `json:"Gateway"`
- // IPAddress is the IP address for this network.
- IPAddress string `json:"IPAddress"`
- // IPPrefixLen is the length of the subnet mask of this network.
- IPPrefixLen int `json:"IPPrefixLen"`
- // SecondaryIPAddresses is a list of extra IP Addresses that the
- // container has been assigned in this network.
- SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty"`
- // IPv6Gateway is the IPv6 gateway this network will use.
- IPv6Gateway string `json:"IPv6Gateway"`
- // GlobalIPv6Address is the global-scope IPv6 Address for this network.
- GlobalIPv6Address string `json:"GlobalIPv6Address"`
- // GlobalIPv6PrefixLen is the length of the subnet mask of this network.
- GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
- // SecondaryIPv6Addresses is a list of extra IPv6 Addresses that the
- // container has been assigned in this networ.
- SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty"`
- // MacAddress is the MAC address for the interface in this network.
- MacAddress string `json:"MacAddress"`
- // AdditionalMacAddresses is a set of additional MAC Addresses beyond
- // the first. CNI may configure more than one interface for a single
- // network, which can cause this.
- AdditionalMacAddresses []string `json:"AdditionalMACAddresses,omitempty"`
-}
-
-// InspectNetworkSettings holds information about the network settings of the
-// container.
-// Many fields are maintained only for compatibility with `docker inspect` and
-// are unused within Libpod.
-type InspectNetworkSettings struct {
- InspectBasicNetworkConfig
-
- Bridge string `json:"Bridge"`
- SandboxID string `json:"SandboxID"`
- HairpinMode bool `json:"HairpinMode"`
- LinkLocalIPv6Address string `json:"LinkLocalIPv6Address"`
- LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen"`
- Ports []ocicni.PortMapping `json:"Ports"`
- SandboxKey string `json:"SandboxKey"`
- // Networks contains information on non-default CNI networks this
- // container has joined.
- // It is a map of network name to network information.
- Networks map[string]*InspectAdditionalNetwork `json:"Networks,omitempty"`
-}
-
-// InspectAdditionalNetwork holds information about non-default CNI networks the
-// container has been connected to.
-// As with InspectNetworkSettings, many fields are unused and maintained only
-// for compatibility with Docker.
-type InspectAdditionalNetwork struct {
- InspectBasicNetworkConfig
-
- // Name of the network we're connecting to.
- NetworkID string `json:"NetworkID,omitempty"`
- // DriverOpts is presently unused and maintained exclusively for
- // compatibility.
- DriverOpts map[string]string `json:"DriverOpts"`
- // IPAMConfig is presently unused and maintained exclusively for
- // compatibility.
- IPAMConfig map[string]string `json:"IPAMConfig"`
- // Links is presently unused and maintained exclusively for
- // compatibility.
- Links []string `json:"Links"`
-}
-
// inspectLocked inspects a container for low-level information.
// The caller must held c.lock.
-func (c *Container) inspectLocked(size bool) (*InspectContainerData, error) {
+func (c *Container) inspectLocked(size bool) (*define.InspectContainerData, error) {
storeCtr, err := c.runtime.store.Container(c.ID())
if err != nil {
return nil, errors.Wrapf(err, "error getting container from store %q", c.ID())
@@ -697,7 +101,7 @@ func (c *Container) inspectLocked(size bool) (*InspectContainerData, error) {
}
// Inspect a container for low-level information
-func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
+func (c *Container) Inspect(size bool) (*define.InspectContainerData, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -710,7 +114,7 @@ func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
return c.inspectLocked(size)
}
-func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*InspectContainerData, error) {
+func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*define.InspectContainerData, error) {
config := c.config
runtimeInfo := c.state
ctrSpec, err := c.specFromState()
@@ -757,12 +161,12 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
return nil, err
}
- data := &InspectContainerData{
+ data := &define.InspectContainerData{
ID: config.ID,
Created: config.CreatedTime,
Path: path,
Args: args,
- State: &InspectContainerState{
+ State: &define.InspectContainerState{
OciVersion: ctrSpec.Version,
Status: runtimeInfo.State.String(),
Running: runtimeInfo.State == define.ContainerStateRunning,
@@ -857,8 +261,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// Get inspect-formatted mounts list.
// Only includes user-specified mounts. Only includes bind mounts and named
// volumes, not tmpfs volumes.
-func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]InspectMount, error) {
- inspectMounts := []InspectMount{}
+func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
+ inspectMounts := []define.InspectMount{}
// No mounts, return early
if len(c.config.UserVolumes) == 0 {
@@ -866,7 +270,7 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*Contain
}
for _, volume := range namedVolumes {
- mountStruct := InspectMount{}
+ mountStruct := define.InspectMount{}
mountStruct.Type = "volume"
mountStruct.Destination = volume.Dest
mountStruct.Name = volume.Name
@@ -891,7 +295,7 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*Contain
continue
}
- mountStruct := InspectMount{}
+ mountStruct := define.InspectMount{}
mountStruct.Type = "bind"
mountStruct.Source = mount.Source
mountStruct.Destination = mount.Destination
@@ -906,7 +310,7 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*Contain
// Parse mount options so we can populate them in the mount structure.
// The mount passed in will be modified.
-func parseMountOptionsForInspect(options []string, mount *InspectMount) {
+func parseMountOptionsForInspect(options []string, mount *define.InspectMount) {
isRW := true
mountProp := ""
zZ := ""
@@ -940,8 +344,8 @@ func parseMountOptionsForInspect(options []string, mount *InspectMount) {
}
// Generate the InspectContainerConfig struct for the Config field of Inspect.
-func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectContainerConfig, error) {
- ctrConfig := new(InspectContainerConfig)
+func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*define.InspectContainerConfig, error) {
+ ctrConfig := new(define.InspectContainerConfig)
ctrConfig.Hostname = c.Hostname()
ctrConfig.User = c.config.User
@@ -992,14 +396,14 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*InspectCon
// Generate the InspectContainerHostConfig struct for the HostConfig field of
// Inspect.
-func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) (*InspectContainerHostConfig, error) {
- hostConfig := new(InspectContainerHostConfig)
+func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) (*define.InspectContainerHostConfig, error) {
+ hostConfig := new(define.InspectContainerHostConfig)
- logConfig := new(InspectLogConfig)
+ logConfig := new(define.InspectLogConfig)
logConfig.Type = c.config.LogDriver
hostConfig.LogConfig = logConfig
- restartPolicy := new(InspectRestartPolicy)
+ restartPolicy := new(define.InspectRestartPolicy)
restartPolicy.Name = c.config.RestartPolicy
restartPolicy.MaximumRetryCount = c.config.RestartRetries
hostConfig.RestartPolicy = restartPolicy
@@ -1126,7 +530,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Linux.Resources.BlockIO.Weight != nil {
hostConfig.BlkioWeight = *ctrSpec.Linux.Resources.BlockIO.Weight
}
- hostConfig.BlkioWeightDevice = []InspectBlkioWeightDevice{}
+ hostConfig.BlkioWeightDevice = []define.InspectBlkioWeightDevice{}
for _, dev := range ctrSpec.Linux.Resources.BlockIO.WeightDevice {
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
// TODO: how do we handle LeafWeight vs
@@ -1148,14 +552,14 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
logrus.Warnf("Could not locate weight device %s in system devices", key)
continue
}
- weightDev := InspectBlkioWeightDevice{}
+ weightDev := define.InspectBlkioWeightDevice{}
weightDev.Path = path
weightDev.Weight = *dev.Weight
hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
}
- handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]InspectBlkioThrottleDevice, error) {
- out := []InspectBlkioThrottleDevice{}
+ handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]define.InspectBlkioThrottleDevice, error) {
+ out := []define.InspectBlkioThrottleDevice{}
for _, dev := range devs {
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
if deviceNodes == nil {
@@ -1170,7 +574,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
logrus.Warnf("Could not locate throttle device %s in system devices", key)
continue
}
- throttleDev := InspectBlkioThrottleDevice{}
+ throttleDev := define.InspectBlkioThrottleDevice{}
throttleDev.Path = path
throttleDev.Rate = dev.Rate
out = append(out, throttleDev)
@@ -1272,15 +676,15 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Port bindings.
// Only populate if we're using CNI to configure the network.
- portBindings := make(map[string][]InspectHostPort)
+ portBindings := make(map[string][]define.InspectHostPort)
if c.config.CreateNetNS {
for _, port := range c.config.PortMappings {
key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
hostPorts := portBindings[key]
if hostPorts == nil {
- hostPorts = []InspectHostPort{}
+ hostPorts = []define.InspectHostPort{}
}
- hostPorts = append(hostPorts, InspectHostPort{
+ hostPorts = append(hostPorts, define.InspectHostPort{
HostIP: port.HostIP,
HostPort: fmt.Sprintf("%d", port.HostPort),
})
@@ -1449,7 +853,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Devices
// Do not include if privileged - assumed that all devices will be
// included.
- hostConfig.Devices = []InspectDevice{}
+ hostConfig.Devices = []define.InspectDevice{}
if ctrSpec.Linux != nil && !hostConfig.Privileged {
for _, dev := range ctrSpec.Linux.Devices {
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
@@ -1465,7 +869,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
logrus.Warnf("Could not locate device %s on host", key)
continue
}
- newDev := InspectDevice{}
+ newDev := define.InspectDevice{}
newDev.PathOnHost = path
newDev.PathInContainer = dev.Path
hostConfig.Devices = append(hostConfig.Devices, newDev)
@@ -1473,10 +877,10 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
}
// Ulimits
- hostConfig.Ulimits = []InspectUlimit{}
+ hostConfig.Ulimits = []define.InspectUlimit{}
if ctrSpec.Process != nil {
for _, limit := range ctrSpec.Process.Rlimits {
- newLimit := InspectUlimit{}
+ newLimit := define.InspectUlimit{}
newLimit.Name = limit.Type
newLimit.Soft = limit.Soft
newLimit.Hard = limit.Hard
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 8bd59be75..c06c32471 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -4,7 +4,7 @@ var (
// DefaultInitPath is the default path to the container-init binary
DefaultInitPath = "/usr/libexec/podman/catatonit"
// DefaultInfraImage to use for infra container
- DefaultInfraImage = "k8s.gcr.io/pause:3.1"
+ DefaultInfraImage = "k8s.gcr.io/pause:3.2"
// DefaultInfraCommand to be run in an infra container
DefaultInfraCommand = "/pause"
// DefaultSHMLockPath is the default path for SHM locks
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
new file mode 100644
index 000000000..e6a19e5b4
--- /dev/null
+++ b/libpod/define/container_inspect.go
@@ -0,0 +1,624 @@
+package define
+
+import (
+ "time"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/libpod/driver"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
+// InspectContainerConfig holds further data about how a container was initially
+// configured.
+type InspectContainerConfig struct {
+ // Container hostname
+ Hostname string `json:"Hostname"`
+ // Container domain name - unused at present
+ DomainName string `json:"Domainname"`
+ // User the container was launched with
+ User string `json:"User"`
+ // Unused, at present
+ AttachStdin bool `json:"AttachStdin"`
+ // Unused, at present
+ AttachStdout bool `json:"AttachStdout"`
+ // Unused, at present
+ AttachStderr bool `json:"AttachStderr"`
+ // Whether the container creates a TTY
+ Tty bool `json:"Tty"`
+ // Whether the container leaves STDIN open
+ OpenStdin bool `json:"OpenStdin"`
+ // Whether STDIN is only left open once.
+ // Presently not supported by Podman, unused.
+ StdinOnce bool `json:"StdinOnce"`
+ // Container environment variables
+ Env []string `json:"Env"`
+ // Container command
+ Cmd []string `json:"Cmd"`
+ // Container image
+ Image string `json:"Image"`
+ // Unused, at present. I've never seen this field populated.
+ Volumes map[string]struct{} `json:"Volumes"`
+ // Container working directory
+ WorkingDir string `json:"WorkingDir"`
+ // Container entrypoint
+ Entrypoint string `json:"Entrypoint"`
+ // On-build arguments - presently unused. More of Buildah's domain.
+ OnBuild *string `json:"OnBuild"`
+ // Container labels
+ Labels map[string]string `json:"Labels"`
+ // Container annotations
+ Annotations map[string]string `json:"Annotations"`
+ // Container stop signal
+ StopSignal uint `json:"StopSignal"`
+ // Configured healthcheck for the container
+ Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
+ // CreateCommand is the full command plus arguments of the process the
+ // container has been created with.
+ CreateCommand []string `json:"CreateCommand,omitempty"`
+}
+
+// InspectRestartPolicy holds information about the container's restart policy.
+type InspectRestartPolicy struct {
+ // Name contains the container's restart policy.
+ // Allowable values are "no" or "" (take no action),
+ // "on-failure" (restart on non-zero exit code, with an optional max
+ // retry count), and "always" (always restart on container stop, unless
+ // explicitly requested by API).
+ // Note that this is NOT actually a name of any sort - the poor naming
+ // is for Docker compatibility.
+ Name string `json:"Name"`
+ // MaximumRetryCount is the maximum number of retries allowed if the
+ // "on-failure" restart policy is in use. Not used if "on-failure" is
+ // not set.
+ MaximumRetryCount uint `json:"MaximumRetryCount"`
+}
+
+// InspectLogConfig holds information about a container's configured log driver
+// and is presently unused. It is retained for Docker compatibility.
+type InspectLogConfig struct {
+ Type string `json:"Type"`
+ Config map[string]string `json:"Config"` //idk type, TODO
+}
+
+// InspectBlkioWeightDevice holds information about the relative weight
+// of an individual device node. Weights are used in the I/O scheduler to give
+// relative priority to some accesses.
+type InspectBlkioWeightDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Weight is the relative weight the scheduler will use when scheduling
+ // I/O.
+ Weight uint16 `json:"Weight"`
+}
+
+// InspectBlkioThrottleDevice holds information about a speed cap for a device
+// node. This cap applies to a specific operation (read, write, etc) on the given
+// node.
+type InspectBlkioThrottleDevice struct {
+ // Path is the path to the device this applies to.
+ Path string `json:"Path"`
+ // Rate is the maximum rate. It is in either bytes per second or iops
+ // per second, determined by where it is used - documentation will
+ // indicate which is appropriate.
+ Rate uint64 `json:"Rate"`
+}
+
+// InspectUlimit is a ulimit that will be applied to the container.
+type InspectUlimit struct {
+ // Name is the name (type) of the ulimit.
+ Name string `json:"Name"`
+ // Soft is the soft limit that will be applied.
+ Soft uint64 `json:"Soft"`
+ // Hard is the hard limit that will be applied.
+ Hard uint64 `json:"Hard"`
+}
+
+// InspectDevice is a single device that will be mounted into the container.
+type InspectDevice struct {
+ // PathOnHost is the path of the device on the host.
+ PathOnHost string `json:"PathOnHost"`
+ // PathInContainer is the path of the device within the container.
+ PathInContainer string `json:"PathInContainer"`
+ // CgroupPermissions is the permissions of the mounted device.
+ // Presently not populated.
+ // TODO.
+ CgroupPermissions string `json:"CgroupPermissions"`
+}
+
+// InspectHostPort provides information on a port on the host that a container's
+// port is bound to.
+type InspectHostPort struct {
+ // IP on the host we are bound to. "" if not specified (binding to all
+ // IPs).
+ HostIP string `json:"HostIp"`
+ // Port on the host we are bound to. No special formatting - just an
+ // integer stuffed into a string.
+ HostPort string `json:"HostPort"`
+}
+
+// InspectMount provides a record of a single mount in a container. It contains
+// fields for both named and normal volumes. Only user-specified volumes will be
+// included, and tmpfs volumes are not included even if the user specified them.
+type InspectMount struct {
+ // Whether the mount is a volume or bind mount. Allowed values are
+ // "volume" and "bind".
+ Type string `json:"Type"`
+ // The name of the volume. Empty for bind mounts.
+ Name string `json:"Name,omptempty"`
+ // The source directory for the volume.
+ Source string `json:"Source"`
+ // The destination directory for the volume. Specified as a path within
+ // the container, as it would be passed into the OCI runtime.
+ Destination string `json:"Destination"`
+ // The driver used for the named volume. Empty for bind mounts.
+ Driver string `json:"Driver"`
+ // Contains SELinux :z/:Z mount options. Unclear what, if anything, else
+ // goes in here.
+ Mode string `json:"Mode"`
+ // All remaining mount options. Additional data, not present in the
+ // original output.
+ Options []string `json:"Options"`
+ // Whether the volume is read-write
+ RW bool `json:"RW"`
+ // Mount propagation for the mount. Can be empty if not specified, but
+ // is always printed - no omitempty.
+ Propagation string `json:"Propagation"`
+}
+
+// InspectContainerState provides a detailed record of a container's current
+// state. It is returned as part of InspectContainerData.
+// As with InspectContainerData, many portions of this struct are matched to
+// Docker, but here we see more fields that are unused (nonsensical in the
+// context of Libpod).
+type InspectContainerState struct {
+ OciVersion string `json:"OciVersion"`
+ Status string `json:"Status"`
+ Running bool `json:"Running"`
+ Paused bool `json:"Paused"`
+ Restarting bool `json:"Restarting"` // TODO
+ OOMKilled bool `json:"OOMKilled"`
+ Dead bool `json:"Dead"`
+ Pid int `json:"Pid"`
+ ConmonPid int `json:"ConmonPid,omitempty"`
+ ExitCode int32 `json:"ExitCode"`
+ Error string `json:"Error"` // TODO
+ StartedAt time.Time `json:"StartedAt"`
+ FinishedAt time.Time `json:"FinishedAt"`
+ Healthcheck HealthCheckResults `json:"Healthcheck,omitempty"`
+}
+
+// HealthCheckResults describes the results/logs from a healthcheck
+type HealthCheckResults struct {
+ // Status healthy or unhealthy
+ Status string `json:"Status"`
+ // FailingStreak is the number of consecutive failed healthchecks
+ FailingStreak int `json:"FailingStreak"`
+ // Log describes healthcheck attempts and results
+ Log []HealthCheckLog `json:"Log"`
+}
+
+// HealthCheckLog describes the results of a single healthcheck
+type HealthCheckLog struct {
+ // Start time as string
+ Start string `json:"Start"`
+ // End time as a string
+ End string `json:"End"`
+ // Exitcode is 0 or 1
+ ExitCode int `json:"ExitCode"`
+ // Output is the stdout/stderr from the healthcheck command
+ Output string `json:"Output"`
+}
+
+// InspectContainerHostConfig holds information used when the container was
+// created.
+// It's very much a Docker-specific struct, retained (mostly) as-is for
+// compatibility. We fill individual fields as best as we can, inferring as much
+// as possible from the spec and container config.
+// Some things cannot be inferred. These will be populated by spec annotations
+// (if available).
+// Field names are fixed for compatibility and cannot be changed.
+// As such, silence lint warnings about them.
+//nolint
+type InspectContainerHostConfig struct {
+ // Binds contains an array of user-added mounts.
+ // Both volume mounts and named volumes are included.
+ // Tmpfs mounts are NOT included.
+ // In 'docker inspect' this is separated into 'Binds' and 'Mounts' based
+ // on how a mount was added. We do not make this distinction and do not
+ // include a Mounts field in inspect.
+ // Format: <src>:<destination>[:<comma-separated options>]
+ Binds []string `json:"Binds"`
+ // ContainerIDFile is a file created during container creation to hold
+ // the ID of the created container.
+ // This is not handled within libpod and is stored in an annotation.
+ ContainerIDFile string `json:"ContainerIDFile"`
+ // LogConfig contains information on the container's logging backend
+ LogConfig *InspectLogConfig `json:"LogConfig"`
+ // NetworkMode is the configuration of the container's network
+ // namespace.
+ // Populated as follows:
+ // default - A network namespace is being created and configured via CNI
+ // none - A network namespace is being created, not configured via CNI
+ // host - No network namespace created
+ // container:<id> - Using another container's network namespace
+ // ns:<path> - A path to a network namespace has been specified
+ NetworkMode string `json:"NetworkMode"`
+ // PortBindings contains the container's port bindings.
+ // It is formatted as map[string][]InspectHostPort.
+ // The string key here is formatted as <integer port number>/<protocol>
+ // and represents the container port. A single container port may be
+ // bound to multiple host ports (on different IPs).
+ PortBindings map[string][]InspectHostPort `json:"PortBindings"`
+ // RestartPolicy contains the container's restart policy.
+ RestartPolicy *InspectRestartPolicy `json:"RestartPolicy"`
+ // AutoRemove is whether the container will be automatically removed on
+ // exiting.
+ // It is not handled directly within libpod and is stored in an
+ // annotation.
+ AutoRemove bool `json:"AutoRemove"`
+ // VolumeDriver is presently unused and is retained for Docker
+ // compatibility.
+ VolumeDriver string `json:"VolumeDriver"`
+ // VolumesFrom is a list of containers which this container uses volumes
+ // from. This is not handled directly within libpod and is stored in an
+ // annotation.
+ // It is formatted as an array of container names and IDs.
+ VolumesFrom []string `json:"VolumesFrom"`
+ // CapAdd is a list of capabilities added to the container.
+ // It is not directly stored by Libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapAdd []string `json:"CapAdd"`
+ // CapDrop is a list of capabilities removed from the container.
+ // It is not directly stored by libpod, and instead computed from the
+ // capabilities listed in the container's spec, compared against a set
+ // of default capabilities.
+ CapDrop []string `json:"CapDrop"`
+ // Dns is a list of DNS nameservers that will be added to the
+ // container's resolv.conf
+ Dns []string `json:"Dns"`
+ // DnsOptions is a list of DNS options that will be set in the
+ // container's resolv.conf
+ DnsOptions []string `json:"DnsOptions"`
+ // DnsSearch is a list of DNS search domains that will be set in the
+ // container's resolv.conf
+ DnsSearch []string `json:"DnsSearch"`
+ // ExtraHosts contains hosts that will be aded to the container's
+ // /etc/hosts.
+ ExtraHosts []string `json:"ExtraHosts"`
+ // GroupAdd contains groups that the user inside the container will be
+ // added to.
+ GroupAdd []string `json:"GroupAdd"`
+ // IpcMode represents the configuration of the container's IPC
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, an IPC namespace will be created
+ // host - No IPC namespace created
+ // container:<id> - Using another container's IPC namespace
+ // ns:<path> - A path to an IPC namespace has been specified
+ IpcMode string `json:"IpcMode"`
+ // Cgroup contains the container's cgroup. It is presently not
+ // populated.
+ // TODO.
+ Cgroup string `json:"Cgroup"`
+ // Cgroups contains the container's CGroup mode.
+ // Allowed values are "default" (container is creating CGroups) and
+ // "disabled" (container is not creating CGroups).
+ // This is Libpod-specific and not included in `docker inspect`.
+ Cgroups string `json:"Cgroups"`
+ // Links is unused, and provided purely for Docker compatibility.
+ Links []string `json:"Links"`
+ // OOMScoreAdj is an adjustment that will be made to the container's OOM
+ // score.
+ OomScoreAdj int `json:"OomScoreAdj"`
+ // PidMode represents the configuration of the container's PID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a PID namespace will be created
+ // host - No PID namespace created
+ // container:<id> - Using another container's PID namespace
+ // ns:<path> - A path to a PID namespace has been specified
+ PidMode string `json:"PidMode"`
+ // Privileged indicates whether the container is running with elevated
+ // privileges.
+ // This has a very specific meaning in the Docker sense, so it's very
+ // difficult to decode from the spec and config, and so is stored as an
+ // annotation.
+ Privileged bool `json:"Privileged"`
+ // PublishAllPorts indicates whether image ports are being published.
+ // This is not directly stored in libpod and is saved as an annotation.
+ PublishAllPorts bool `json:"PublishAllPorts"`
+ // ReadonlyRootfs is whether the container will be mounted read-only.
+ ReadonlyRootfs bool `json:"ReadonlyRootfs"`
+ // SecurityOpt is a list of security-related options that are set in the
+ // container.
+ SecurityOpt []string `json:"SecurityOpt"`
+ // Tmpfs is a list of tmpfs filesystems that will be mounted into the
+ // container.
+ // It is a map of destination path to options for the mount.
+ Tmpfs map[string]string `json:"Tmpfs"`
+ // UTSMode represents the configuration of the container's UID
+ // namespace.
+ // Populated as follows:
+ // "" (empty string) - Default, a UTS namespace will be created
+ // host - no UTS namespace created
+ // container:<id> - Using another container's UTS namespace
+ // ns:<path> - A path to a UTS namespace has been specified
+ UTSMode string `json:"UTSMode"`
+ // UsernsMode represents the configuration of the container's user
+ // namespace.
+ // When running rootless, a user namespace is created outside of libpod
+ // to allow some privileged operations. This will not be reflected here.
+ // Populated as follows:
+ // "" (empty string) - No user namespace will be created
+ // private - The container will be run in a user namespace
+ // container:<id> - Using another container's user namespace
+ // ns:<path> - A path to a user namespace has been specified
+ // TODO Rootless has an additional 'keep-id' option, presently not
+ // reflected here.
+ UsernsMode string `json:"UsernsMode"`
+ // ShmSize is the size of the container's SHM device.
+ ShmSize int64 `json:"ShmSize"`
+ // Runtime is provided purely for Docker compatibility.
+ // It is set unconditionally to "oci" as Podman does not presently
+ // support non-OCI runtimes.
+ Runtime string `json:"Runtime"`
+ // ConsoleSize is an array of 2 integers showing the size of the
+ // container's console.
+ // It is only set if the container is creating a terminal.
+ // TODO.
+ ConsoleSize []uint `json:"ConsoleSize"`
+ // Isolation is presently unused and provided solely for Docker
+ // compatibility.
+ Isolation string `json:"Isolation"`
+ // CpuShares indicates the CPU resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning CPU time
+ // versus other CGroups.
+ CpuShares uint64 `json:"CpuShares"`
+ // Memory indicates the memory resources allocated to the container.
+ // This is the limit (in bytes) of RAM the container may use.
+ Memory int64 `json:"Memory"`
+ // NanoCpus indicates number of CPUs allocated to the container.
+ // It is an integer where one full CPU is indicated by 1000000000 (one
+ // billion).
+ // Thus, 2.5 CPUs (fractional portions of CPUs are allowed) would be
+ // 2500000000 (2.5 billion).
+ // In 'docker inspect' this is set exclusively of two further options in
+ // the output (CpuPeriod and CpuQuota) which are both used to implement
+ // this functionality.
+ // We can't distinguish here, so if CpuQuota is set to the default of
+ // 100000, we will set both CpuQuota, CpuPeriod, and NanoCpus. If
+ // CpuQuota is not the default, we will not set NanoCpus.
+ NanoCpus int64 `json:"NanoCpus"`
+ // CgroupParent is the CGroup parent of the container.
+ // Only set if not default.
+ CgroupParent string `json:"CgroupParent"`
+ // BlkioWeight indicates the I/O resources allocated to the container.
+ // It is a relative weight in the scheduler for assigning I/O time
+ // versus other CGroups.
+ BlkioWeight uint16 `json:"BlkioWeight"`
+ // BlkioWeightDevice is an array of I/O resource priorities for
+ // individual device nodes.
+ // Unfortunately, the spec only stores the device's Major/Minor numbers
+ // and not the path, which is used here.
+ // Fortunately, the kernel provides an interface for retrieving the path
+ // of a given node by major:minor at /sys/dev/. However, the exact path
+ // in use may not be what was used in the original CLI invocation -
+ // though it is guaranteed that the device node will be the same, and
+ // using the given path will be functionally identical.
+ BlkioWeightDevice []InspectBlkioWeightDevice `json:"BlkioWeightDevice"`
+ // BlkioDeviceReadBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets read rate cap in bytes per second for device
+ // nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadBps"`
+ // BlkioDeviceWriteBps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // this specifically sets write rate cap in bytes per second for device
+ // nodes.
+ // as with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteBps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteBps"`
+ // BlkioDeviceReadIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the read rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceReadIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceReadIOps"`
+ // BlkioDeviceWriteIOps is an array of I/O throttle parameters for
+ // individual device nodes.
+ // This specifically sets the write rate cap in iops per second for
+ // device nodes.
+ // As with BlkioWeightDevice, we pull the path from /sys/dev, and we
+ // don't guarantee the path will be identical to the original (though
+ // the node will be).
+ BlkioDeviceWriteIOps []InspectBlkioThrottleDevice `json:"BlkioDeviceWriteIOps"`
+ // CpuPeriod is the length of a CPU period in microseconds.
+ // It relates directly to CpuQuota.
+ CpuPeriod uint64 `json:"CpuPeriod"`
+ // CpuPeriod is the amount of time (in microseconds) that a container
+ // can use the CPU in every CpuPeriod.
+ CpuQuota int64 `json:"CpuQuota"`
+ // CpuRealtimePeriod is the length of time (in microseconds) of the CPU
+ // realtime period. If set to 0, no time will be allocated to realtime
+ // tasks.
+ CpuRealtimePeriod uint64 `json:"CpuRealtimePeriod"`
+ // CpuRealtimeRuntime is the length of time (in microseconds) allocated
+ // for realtime tasks within every CpuRealtimePeriod.
+ CpuRealtimeRuntime int64 `json:"CpuRealtimeRuntime"`
+ // CpusetCpus is the is the set of CPUs that the container will execute
+ // on. Formatted as `0-3` or `0,2`. Default (if unset) is all CPUs.
+ CpusetCpus string `json:"CpusetCpus"`
+ // CpusetMems is the set of memory nodes the container will use.
+ // Formatted as `0-3` or `0,2`. Default (if unset) is all memory nodes.
+ CpusetMems string `json:"CpusetMems"`
+ // Devices is a list of device nodes that will be added to the
+ // container.
+ // These are stored in the OCI spec only as type, major, minor while we
+ // display the host path. We convert this with /sys/dev, but we cannot
+ // guarantee that the host path will be identical - only that the actual
+ // device will be.
+ Devices []InspectDevice `json:"Devices"`
+ // DiskQuota is the maximum amount of disk space the container may use
+ // (in bytes).
+ // Presently not populated.
+ // TODO.
+ DiskQuota uint64 `json:"DiskQuota"`
+ // KernelMemory is the maximum amount of memory the kernel will devote
+ // to the container.
+ KernelMemory int64 `json:"KernelMemory"`
+ // MemoryReservation is the reservation (soft limit) of memory available
+ // to the container. Soft limits are warnings only and can be exceeded.
+ MemoryReservation int64 `json:"MemoryReservation"`
+ // MemorySwap is the total limit for all memory available to the
+ // container, including swap. 0 indicates that there is no limit to the
+ // amount of memory available.
+ MemorySwap int64 `json:"MemorySwap"`
+ // MemorySwappiness is the willingness of the kernel to page container
+ // memory to swap. It is an integer from 0 to 100, with low numbers
+ // being more likely to be put into swap.
+ // -1, the default, will not set swappiness and use the system defaults.
+ MemorySwappiness int64 `json:"MemorySwappiness"`
+ // OomKillDisable indicates whether the kernel OOM killer is disabled
+ // for the container.
+ OomKillDisable bool `json:"OomKillDisable"`
+ // Init indicates whether the container has an init mounted into it.
+ Init bool `json:"Init,omitempty"`
+ // PidsLimit is the maximum number of PIDs what may be created within
+ // the container. 0, the default, indicates no limit.
+ PidsLimit int64 `json:"PidsLimit"`
+ // Ulimits is a set of ulimits that will be set within the container.
+ Ulimits []InspectUlimit `json:"Ulimits"`
+ // CpuCount is Windows-only and not presently implemented.
+ CpuCount uint64 `json:"CpuCount"`
+ // CpuPercent is Windows-only and not presently implemented.
+ CpuPercent uint64 `json:"CpuPercent"`
+ // IOMaximumIOps is Windows-only and not presently implemented.
+ IOMaximumIOps uint64 `json:"IOMaximumIOps"`
+ // IOMaximumBandwidth is Windows-only and not presently implemented.
+ IOMaximumBandwidth uint64 `json:"IOMaximumBandwidth"`
+}
+
+// InspectBasicNetworkConfig holds basic configuration information (e.g. IP
+// addresses, MAC address, subnet masks, etc) that are common for all networks
+// (both additional and main).
+type InspectBasicNetworkConfig struct {
+ // EndpointID is unused, maintained exclusively for compatibility.
+ EndpointID string `json:"EndpointID"`
+ // Gateway is the IP address of the gateway this network will use.
+ Gateway string `json:"Gateway"`
+ // IPAddress is the IP address for this network.
+ IPAddress string `json:"IPAddress"`
+ // IPPrefixLen is the length of the subnet mask of this network.
+ IPPrefixLen int `json:"IPPrefixLen"`
+ // SecondaryIPAddresses is a list of extra IP Addresses that the
+ // container has been assigned in this network.
+ SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty"`
+ // IPv6Gateway is the IPv6 gateway this network will use.
+ IPv6Gateway string `json:"IPv6Gateway"`
+ // GlobalIPv6Address is the global-scope IPv6 Address for this network.
+ GlobalIPv6Address string `json:"GlobalIPv6Address"`
+ // GlobalIPv6PrefixLen is the length of the subnet mask of this network.
+ GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
+ // SecondaryIPv6Addresses is a list of extra IPv6 Addresses that the
+ // container has been assigned in this networ.
+ SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty"`
+ // MacAddress is the MAC address for the interface in this network.
+ MacAddress string `json:"MacAddress"`
+ // AdditionalMacAddresses is a set of additional MAC Addresses beyond
+ // the first. CNI may configure more than one interface for a single
+ // network, which can cause this.
+ AdditionalMacAddresses []string `json:"AdditionalMACAddresses,omitempty"`
+}
+
+// InspectAdditionalNetwork holds information about non-default CNI networks the
+// container has been connected to.
+// As with InspectNetworkSettings, many fields are unused and maintained only
+// for compatibility with Docker.
+type InspectAdditionalNetwork struct {
+ InspectBasicNetworkConfig
+
+ // Name of the network we're connecting to.
+ NetworkID string `json:"NetworkID,omitempty"`
+ // DriverOpts is presently unused and maintained exclusively for
+ // compatibility.
+ DriverOpts map[string]string `json:"DriverOpts"`
+ // IPAMConfig is presently unused and maintained exclusively for
+ // compatibility.
+ IPAMConfig map[string]string `json:"IPAMConfig"`
+ // Links is presently unused and maintained exclusively for
+ // compatibility.
+ Links []string `json:"Links"`
+}
+
+// InspectNetworkSettings holds information about the network settings of the
+// container.
+// Many fields are maintained only for compatibility with `docker inspect` and
+// are unused within Libpod.
+type InspectNetworkSettings struct {
+ InspectBasicNetworkConfig
+
+ Bridge string `json:"Bridge"`
+ SandboxID string `json:"SandboxID"`
+ HairpinMode bool `json:"HairpinMode"`
+ LinkLocalIPv6Address string `json:"LinkLocalIPv6Address"`
+ LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen"`
+ Ports []ocicni.PortMapping `json:"Ports"`
+ SandboxKey string `json:"SandboxKey"`
+ // Networks contains information on non-default CNI networks this
+ // container has joined.
+ // It is a map of network name to network information.
+ Networks map[string]*InspectAdditionalNetwork `json:"Networks,omitempty"`
+}
+
+// InspectContainerData provides a detailed record of a container's configuration
+// and state as viewed by Libpod.
+// Large portions of this structure are defined such that the output is
+// compatible with `docker inspect` JSON, but additional fields have been added
+// as required to share information not in the original output.
+type InspectContainerData struct {
+ ID string `json:"Id"`
+ Created time.Time `json:"Created"`
+ Path string `json:"Path"`
+ Args []string `json:"Args"`
+ State *InspectContainerState `json:"State"`
+ Image string `json:"Image"`
+ ImageName string `json:"ImageName"`
+ Rootfs string `json:"Rootfs"`
+ Pod string `json:"Pod"`
+ ResolvConfPath string `json:"ResolvConfPath"`
+ HostnamePath string `json:"HostnamePath"`
+ HostsPath string `json:"HostsPath"`
+ StaticDir string `json:"StaticDir"`
+ OCIConfigPath string `json:"OCIConfigPath,omitempty"`
+ OCIRuntime string `json:"OCIRuntime,omitempty"`
+ LogPath string `json:"LogPath"`
+ LogTag string `json:"LogTag"`
+ ConmonPidFile string `json:"ConmonPidFile"`
+ Name string `json:"Name"`
+ RestartCount int32 `json:"RestartCount"`
+ Driver string `json:"Driver"`
+ MountLabel string `json:"MountLabel"`
+ ProcessLabel string `json:"ProcessLabel"`
+ AppArmorProfile string `json:"AppArmorProfile"`
+ EffectiveCaps []string `json:"EffectiveCaps"`
+ BoundingCaps []string `json:"BoundingCaps"`
+ ExecIDs []string `json:"ExecIDs"`
+ GraphDriver *driver.Data `json:"GraphDriver"`
+ SizeRw *int64 `json:"SizeRw,omitempty"`
+ SizeRootFs int64 `json:"SizeRootFs,omitempty"`
+ Mounts []InspectMount `json:"Mounts"`
+ Dependencies []string `json:"Dependencies"`
+ NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO
+ ExitCommand []string `json:"ExitCommand"`
+ Namespace string `json:"Namespace"`
+ IsInfra bool `json:"IsInfra"`
+ Config *InspectContainerConfig `json:"Config"`
+ HostConfig *InspectContainerHostConfig `json:"HostConfig"`
+}
diff --git a/libpod/define/inspect.go b/libpod/define/inspect.go
new file mode 100644
index 000000000..b7cd13f82
--- /dev/null
+++ b/libpod/define/inspect.go
@@ -0,0 +1,54 @@
+package define
+
+// InspectExecSession contains information about a given exec session.
+type InspectExecSession struct {
+ // CanRemove is legacy and used purely for compatibility reasons.
+ // Will always be set to true, unless the exec session is running.
+ CanRemove bool `json:"CanRemove"`
+ // ContainerID is the ID of the container this exec session is attached
+ // to.
+ ContainerID string `json:"ContainerID"`
+ // DetachKeys are the detach keys used by the exec session.
+ // If set to "" the default keys are being used.
+ // Will show "<none>" if no detach keys are set.
+ DetachKeys string `json:"DetachKeys"`
+ // ExitCode is the exit code of the exec session. Will be set to 0 if
+ // the exec session has not yet exited.
+ ExitCode int `json:"ExitCode"`
+ // ID is the ID of the exec session.
+ ID string `json:"ID"`
+ // OpenStderr is whether the container's STDERR stream will be attached.
+ // Always set to true if the exec session created a TTY.
+ OpenStderr bool `json:"OpenStderr"`
+ // OpenStdin is whether the container's STDIN stream will be attached
+ // to.
+ OpenStdin bool `json:"OpenStdin"`
+ // OpenStdout is whether the container's STDOUT stream will be attached.
+ // Always set to true if the exec session created a TTY.
+ OpenStdout bool `json:"OpenStdout"`
+ // Running is whether the exec session is running.
+ Running bool `json:"Running"`
+ // Pid is the PID of the exec session's process.
+ // Will be set to 0 if the exec session is not running.
+ Pid int `json:"Pid"`
+ // ProcessConfig contains information about the exec session's process.
+ ProcessConfig *InspectExecProcess `json:"ProcessConfig"`
+}
+
+// InspectExecProcess contains information about the process in a given exec
+// session.
+type InspectExecProcess struct {
+ // Arguments are the arguments to the entrypoint command of the exec
+ // session.
+ Arguments []string `json:"arguments"`
+ // Entrypoint is the entrypoint for the exec session (the command that
+ // will be executed in the container).
+ Entrypoint string `json:"entrypoint"`
+ // Privileged is whether the exec session will be started with elevated
+ // privileges.
+ Privileged bool `json:"privileged"`
+ // Tty is whether the exec session created a terminal.
+ Tty bool `json:"tty"`
+ // User is the user the exec session was started as.
+ User string `json:"user"`
+}
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index 76b7a1fcf..08a613dfe 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -53,28 +53,6 @@ const (
HealthCheckStarting string = "starting"
)
-// HealthCheckResults describes the results/logs from a healthcheck
-type HealthCheckResults struct {
- // Status healthy or unhealthy
- Status string `json:"Status"`
- // FailingStreak is the number of consecutive failed healthchecks
- FailingStreak int `json:"FailingStreak"`
- // Log describes healthcheck attempts and results
- Log []HealthCheckLog `json:"Log"`
-}
-
-// HealthCheckLog describes the results of a single healthcheck
-type HealthCheckLog struct {
- // Start time as string
- Start string `json:"Start"`
- // End time as a string
- End string `json:"End"`
- // Exitcode is 0 or 1
- ExitCode int `json:"ExitCode"`
- // Output is the stdout/stderr from the healthcheck command
- Output string `json:"Output"`
-}
-
// hcWriteCloser allows us to use bufio as a WriteCloser
type hcWriteCloser struct {
*bufio.Writer
@@ -200,8 +178,8 @@ func checkHealthCheckCanBeRun(c *Container) (HealthCheckStatus, error) {
return HealthCheckDefined, nil
}
-func newHealthCheckLog(start, end time.Time, exitCode int, log string) HealthCheckLog {
- return HealthCheckLog{
+func newHealthCheckLog(start, end time.Time, exitCode int, log string) define.HealthCheckLog {
+ return define.HealthCheckLog{
Start: start.Format(time.RFC3339Nano),
End: end.Format(time.RFC3339Nano),
ExitCode: exitCode,
@@ -225,7 +203,7 @@ func (c *Container) updateHealthStatus(status string) error {
}
// UpdateHealthCheckLog parses the health check results and writes the log
-func (c *Container) updateHealthCheckLog(hcl HealthCheckLog, inStartPeriod bool) error {
+func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPeriod bool) error {
healthCheck, err := c.GetHealthCheckLog()
if err != nil {
return err
@@ -266,8 +244,8 @@ func (c *Container) healthCheckLogPath() string {
// GetHealthCheckLog returns HealthCheck results by reading the container's
// health check log file. If the health check log file does not exist, then
// an empty healthcheck struct is returned
-func (c *Container) GetHealthCheckLog() (HealthCheckResults, error) {
- var healthCheck HealthCheckResults
+func (c *Container) GetHealthCheckLog() (define.HealthCheckResults, error) {
+ var healthCheck define.HealthCheckResults
if _, err := os.Stat(c.healthCheckLogPath()); os.IsNotExist(err) {
return healthCheck, nil
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index f1bf79ce7..ab6ec4439 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -154,13 +154,25 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
return ctrNS, networkStatus, err
}
-func checkSlirpFlags(path string) (bool, bool, bool, error) {
+type slirpFeatures struct {
+ HasDisableHostLoopback bool
+ HasMTU bool
+ HasEnableSandbox bool
+ HasEnableSeccomp bool
+}
+
+func checkSlirpFlags(path string) (*slirpFeatures, error) {
cmd := exec.Command(path, "--help")
out, err := cmd.CombinedOutput()
if err != nil {
- return false, false, false, errors.Wrapf(err, "slirp4netns %q", out)
- }
- return strings.Contains(string(out), "--disable-host-loopback"), strings.Contains(string(out), "--mtu"), strings.Contains(string(out), "--enable-sandbox"), nil
+ return nil, errors.Wrapf(err, "slirp4netns %q", out)
+ }
+ return &slirpFeatures{
+ HasDisableHostLoopback: strings.Contains(string(out), "--disable-host-loopback"),
+ HasMTU: strings.Contains(string(out), "--mtu"),
+ HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"),
+ HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"),
+ }, nil
}
// Configure the network namespace for a rootless container
@@ -187,19 +199,22 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
logPath := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
cmdArgs := []string{}
- dhp, mtu, sandbox, err := checkSlirpFlags(path)
+ slirpFeatures, err := checkSlirpFlags(path)
if err != nil {
return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
}
- if dhp {
+ if slirpFeatures.HasDisableHostLoopback {
cmdArgs = append(cmdArgs, "--disable-host-loopback")
}
- if mtu {
+ if slirpFeatures.HasMTU {
cmdArgs = append(cmdArgs, "--mtu", "65520")
}
- if sandbox {
+ if slirpFeatures.HasEnableSandbox {
cmdArgs = append(cmdArgs, "--enable-sandbox")
}
+ if slirpFeatures.HasEnableSeccomp {
+ cmdArgs = append(cmdArgs, "--enable-seccomp")
+ }
// the slirp4netns arguments being passed are describes as follows:
// from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns
@@ -230,7 +245,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
}
// workaround for https://github.com/rootless-containers/slirp4netns/pull/153
- if sandbox {
+ if slirpFeatures.HasEnableSandbox {
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS
cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
}
@@ -558,8 +573,8 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
// Produce an InspectNetworkSettings containing information on the container
// network.
-func (c *Container) getContainerNetworkInfo() (*InspectNetworkSettings, error) {
- settings := new(InspectNetworkSettings)
+func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) {
+ settings := new(define.InspectNetworkSettings)
settings.Ports = []ocicni.PortMapping{}
if c.config.PortMappings != nil {
// TODO: This may not be safe.
@@ -585,13 +600,13 @@ func (c *Container) getContainerNetworkInfo() (*InspectNetworkSettings, error) {
return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(c.config.Networks), len(c.state.NetworkStatus))
}
- settings.Networks = make(map[string]*InspectAdditionalNetwork)
+ settings.Networks = make(map[string]*define.InspectAdditionalNetwork)
// CNI results should be in the same order as the list of
// networks we pass into CNI.
for index, name := range c.config.Networks {
cniResult := c.state.NetworkStatus[index]
- addedNet := new(InspectAdditionalNetwork)
+ addedNet := new(define.InspectAdditionalNetwork)
addedNet.NetworkID = name
basicConfig, err := resultToBasicNetworkConfig(cniResult)
@@ -625,8 +640,8 @@ func (c *Container) getContainerNetworkInfo() (*InspectNetworkSettings, error) {
// resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI
// result
-func resultToBasicNetworkConfig(result *cnitypes.Result) (InspectBasicNetworkConfig, error) {
- config := InspectBasicNetworkConfig{}
+func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNetworkConfig, error) {
+ config := define.InspectBasicNetworkConfig{}
for _, ctrIP := range result.IPs {
size, _ := ctrIP.Address.Mask.Size()
diff --git a/libpod/networking_unsupported.go b/libpod/networking_unsupported.go
index 32b354a44..1ef8fe2dc 100644
--- a/libpod/networking_unsupported.go
+++ b/libpod/networking_unsupported.go
@@ -20,7 +20,7 @@ func (r *Runtime) createNetNS(ctr *Container) (err error) {
return define.ErrNotImplemented
}
-func (c *Container) getContainerNetworkInfo() (*InspectNetworkSettings, error) {
+func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) {
return nil, define.ErrNotImplemented
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 82c5d7020..6d9a976cb 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -793,7 +793,6 @@ func (r *ConmonOCIRuntime) ExecAttachResize(ctr *Container, sessionID string, ne
}
defer controlFile.Close()
- logrus.Debugf("Received a resize event for container %s exec session %s: %+v", ctr.ID(), sessionID, newSize)
if _, err = fmt.Fprintf(controlFile, "%d %d %d\n", 1, newSize.Height, newSize.Width); err != nil {
return errors.Wrapf(err, "failed to write to ctl file to resize terminal")
}
diff --git a/libpod/options.go b/libpod/options.go
index 9b61d7947..74f9c485e 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -21,6 +21,8 @@ import (
var (
// NameRegex is a regular expression to validate container/pod names.
+ // This must NOT be changed from outside of Libpod. It should be a
+ // constant, but Go won't let us do that.
NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
// RegexError is thrown in presence of an invalid container/pod name.
RegexError = errors.Wrapf(define.ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 0b18436ca..b99e6df84 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -830,6 +830,24 @@ func (r *Runtime) GetLatestContainer() (*Container, error) {
return ctrs[lastCreatedIndex], nil
}
+// GetExecSessionContainer gets the container that a given exec session ID is
+// attached to.
+func (r *Runtime) GetExecSessionContainer(id string) (*Container, error) {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+
+ if !r.valid {
+ return nil, define.ErrRuntimeStopped
+ }
+
+ ctrID, err := r.state.GetExecSession(id)
+ if err != nil {
+ return nil, err
+ }
+
+ return r.state.Container(ctrID)
+}
+
// PruneContainers removes stopped and exited containers from localstorage. A set of optional filters
// can be provided to be more granular.
func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int64, map[string]error, error) {
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index d522ffb6c..d5fede1d1 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -5,7 +5,6 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
- "github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
)
@@ -130,10 +129,8 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
}
// PruneVolumes removes unused volumes from the system
-func (r *Runtime) PruneVolumes(ctx context.Context) ([]*entities.VolumePruneReport, error) {
- var (
- reports []*entities.VolumePruneReport
- )
+func (r *Runtime) PruneVolumes(ctx context.Context) (map[string]error, error) {
+ reports := make(map[string]error)
vols, err := r.GetAllVolumes()
if err != nil {
return nil, err
@@ -142,12 +139,12 @@ func (r *Runtime) PruneVolumes(ctx context.Context) ([]*entities.VolumePruneRepo
for _, vol := range vols {
if err := r.RemoveVolume(ctx, vol, false); err != nil {
if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved {
- reports = append(reports, &entities.VolumePruneReport{Id: vol.Name(), Err: err})
+ reports[vol.Name()] = err
}
continue
}
vol.newVolumeEvent(events.Prune)
- reports = append(reports, &entities.VolumePruneReport{Id: vol.Name()})
+ reports[vol.Name()] = nil
}
return reports, nil
}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 32a84b60d..46db7ebe8 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -32,12 +32,12 @@ import (
)
// Inspect returns an inspect struct from varlink
-func (c *Container) Inspect(size bool) (*libpod.InspectContainerData, error) {
+func (c *Container) Inspect(size bool) (*define.InspectContainerData, error) {
reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID(), size)
if err != nil {
return nil, err
}
- data := libpod.InspectContainerData{}
+ data := define.InspectContainerData{}
if err := json.Unmarshal([]byte(reply), &data); err != nil {
return nil, err
}
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 7817a1f98..76e221fae 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -356,11 +356,11 @@ func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
errs = append(errs, err)
return vids, errs
}
- for _, r := range reports {
- if r.Err == nil {
- vids = append(vids, r.Id)
+ for k, v := range reports {
+ if v == nil {
+ vids = append(vids, k)
} else {
- errs = append(errs, r.Err)
+ errs = append(errs, v)
}
}
return vids, errs
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
new file mode 100644
index 000000000..ec1a8ac96
--- /dev/null
+++ b/pkg/api/handlers/compat/exec.go
@@ -0,0 +1,107 @@
+package compat
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ExecCreateHandler creates an exec session for a given container.
+func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ input := new(handlers.ExecCreateConfig)
+ if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error decoding request body as JSON"))
+ return
+ }
+
+ ctrName := utils.GetName(r)
+ ctr, err := runtime.LookupContainer(ctrName)
+ if err != nil {
+ utils.ContainerNotFound(w, ctrName, err)
+ return
+ }
+
+ libpodConfig := new(libpod.ExecConfig)
+ libpodConfig.Command = input.Cmd
+ libpodConfig.Terminal = input.Tty
+ libpodConfig.AttachStdin = input.AttachStdin
+ libpodConfig.AttachStderr = input.AttachStderr
+ libpodConfig.AttachStdout = input.AttachStdout
+ if input.DetachKeys != "" {
+ libpodConfig.DetachKeys = &input.DetachKeys
+ }
+ libpodConfig.Environment = make(map[string]string)
+ for _, envStr := range input.Env {
+ split := strings.SplitN(envStr, "=", 2)
+ if len(split) != 2 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, errors.Errorf("environment variable %q badly formed, must be key=value", envStr))
+ return
+ }
+ libpodConfig.Environment[split[0]] = split[1]
+ }
+ libpodConfig.WorkDir = input.WorkingDir
+ libpodConfig.Privileged = input.Privileged
+ libpodConfig.User = input.User
+
+ sessID, err := ctr.ExecCreate(libpodConfig)
+ if err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid {
+ // Check if the container is paused. If so, return a 409
+ state, err := ctr.State()
+ if err == nil {
+ // Ignore the error != nil case. We're already
+ // throwing an InternalServerError below.
+ if state == define.ContainerStatePaused {
+ utils.Error(w, "Container is paused", http.StatusConflict, errors.Errorf("cannot create exec session as container %s is paused", ctr.ID()))
+ return
+ }
+ }
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ resp := new(handlers.ExecCreateResponse)
+ resp.ID = sessID
+
+ utils.WriteResponse(w, http.StatusCreated, resp)
+}
+
+// ExecInspectHandler inspects a given exec session.
+func ExecInspectHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ sessionID := mux.Vars(r)["id"]
+ sessionCtr, err := runtime.GetExecSessionContainer(sessionID)
+ if err != nil {
+ utils.Error(w, fmt.Sprintf("No such exec session: %s", sessionID), http.StatusNotFound, err)
+ return
+ }
+
+ logrus.Debugf("Inspecting exec session %s of container %s", sessionID, sessionCtr.ID())
+
+ session, err := sessionCtr.ExecSession(sessionID)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error retrieving exec session %s from container %s", sessionID, sessionCtr.ID()))
+ return
+ }
+
+ inspectOut, err := session.Inspect()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, inspectOut)
+}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index cce718f54..354a13bf5 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -64,6 +64,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
+ All bool
Filters map[string][]string `schema:"filters"`
}{
// This is where you can override the golang default value for one of fields
@@ -80,7 +81,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
- pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters)
+ pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/compat/images_remove.go b/pkg/api/handlers/compat/images_remove.go
index 3d346543e..ed0153529 100644
--- a/pkg/api/handlers/compat/images_remove.go
+++ b/pkg/api/handlers/compat/images_remove.go
@@ -36,17 +36,23 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
return
}
- _, err = runtime.RemoveImage(r.Context(), newImage, query.Force)
+ results, err := runtime.RemoveImage(r.Context(), newImage, query.Force)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
- // TODO
- // This will need to be fixed for proper response, like Deleted: and Untagged:
- m := make(map[string]string)
- m["Deleted"] = newImage.ID()
- foo := []map[string]string{}
- foo = append(foo, m)
- utils.WriteResponse(w, http.StatusOK, foo)
+
+ response := make([]map[string]string, 0, len(results.Untagged)+1)
+ deleted := make(map[string]string, 1)
+ deleted["Deleted"] = results.Deleted
+ response = append(response, deleted)
+
+ for _, u := range results.Untagged {
+ untagged := make(map[string]string, 1)
+ untagged["Untagged"] = u
+ response = append(response, untagged)
+ }
+
+ utils.WriteResponse(w, http.StatusOK, response)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index f8e666451..ee85c1a41 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -119,12 +119,12 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
- all bool
err error
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
+ All bool `schema:"all"`
Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
@@ -140,7 +140,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
if _, found := r.URL.Query()["filters"]; found {
dangling := query.Filters["all"]
if len(dangling) > 0 {
- all, err = strconv.ParseBool(query.Filters["all"][0])
+ query.All, err = strconv.ParseBool(query.Filters["all"][0])
if err != nil {
utils.InternalServerError(w, err)
return
@@ -152,7 +152,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
}
- cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters)
+
+ cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 27ec64d89..7e9c2e2c0 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -4,14 +4,13 @@ import (
"encoding/json"
"fmt"
"net/http"
- "strings"
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
"github.com/containers/libpod/libpod"
"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/specgen"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -20,76 +19,14 @@ import (
func PodCreate(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- options []libpod.PodCreateOption
err error
)
- labels := make(map[string]string)
- input := handlers.PodCreateConfig{}
- if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ var psg specgen.PodSpecGenerator
+ if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
+ utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
return
}
- if len(input.InfraCommand) > 0 || len(input.InfraImage) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError,
- errors.New("infra-command and infra-image are not implemented yet"))
- return
- }
- // TODO long term we should break the following out of adapter and into libpod proper
- // so that the cli and api can share the creation of a pod with the same options
- if len(input.CGroupParent) > 0 {
- options = append(options, libpod.WithPodCgroupParent(input.CGroupParent))
- }
-
- if len(input.Labels) > 0 {
- labels, err = parse.GetAllLabels([]string{}, input.Labels)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- }
-
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if len(input.Name) > 0 {
- options = append(options, libpod.WithPodName(input.Name))
- }
-
- if len(input.Hostname) > 0 {
- options = append(options, libpod.WithPodHostname(input.Hostname))
- }
-
- if input.Infra {
- // TODO infra-image and infra-command are not supported in the libpod API yet. Will fix
- // when implemented in libpod
- options = append(options, libpod.WithInfraContainer())
- sharedNamespaces := shared.DefaultKernelNamespaces
- if len(input.Share) > 0 {
- sharedNamespaces = input.Share
- }
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(sharedNamespaces, ","))
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, nsOptions...)
- }
-
- if len(input.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(input.Publish)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- pod, err := runtime.NewPod(r.Context(), options...)
+ pod, err := psg.MakePod(runtime)
if err != nil {
http_code := http.StatusInternalServerError
if errors.Cause(err) == define.ErrPodExists {
@@ -102,9 +39,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 +52,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 +80,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 +112,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 +150,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 +191,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 +208,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 +236,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 +246,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 +272,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 +292,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 +335,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/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index e61d272f4..5a6fc021e 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -149,13 +149,20 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ reports []*entities.VolumePruneReport
)
pruned, err := runtime.PruneVolumes(r.Context())
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, pruned)
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
+ }
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func RemoveVolume(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index 4ba123ba9..e6e937729 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -2,7 +2,9 @@ package handlers
import (
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"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"
)
@@ -108,7 +110,7 @@ type swagDockerTopResponse struct {
type swagLibpodInspectContainerResponse struct {
// in:body
Body struct {
- libpod.InspectContainerData
+ define.InspectContainerData
}
}
@@ -116,7 +118,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/types.go b/pkg/api/handlers/types.go
index 84ca0fbed..1ca5db3f9 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -133,19 +133,6 @@ type ContainerTopOKBody struct {
dockerContainer.ContainerTopOKBody
}
-// swagger:model PodCreateConfig
-type PodCreateConfig struct {
- Name string `json:"name"`
- CGroupParent string `json:"cgroup-parent"`
- Hostname string `json:"hostname"`
- Infra bool `json:"infra"`
- InfraCommand string `json:"infra-command"`
- InfraImage string `json:"infra-image"`
- Labels []string `json:"labels"`
- Publish []string `json:"publish"`
- Share string `json:"share"`
-}
-
type ErrorModel struct {
Message string `json:"message"`
}
@@ -172,6 +159,14 @@ type ImageTreeResponse struct {
Layers []ImageLayer `json:"layers"`
}
+type ExecCreateConfig struct {
+ docker.ExecConfig
+}
+
+type ExecCreateResponse struct {
+ docker.IDResponse
+}
+
func EventToApiEvent(e *events.Event) *Event {
return &Event{dockerEvents.Message{
Type: e.Type.String(),
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_exec.go b/pkg/api/server/register_exec.go
index d27d21a04..71fb50307 100644
--- a/pkg/api/server/register_exec.go
+++ b/pkg/api/server/register_exec.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerExecHandlers(r *mux.Router) error {
- // swagger:operation POST /containers/{name}/create compat createExec
+ // swagger:operation POST /containers/{name}/exec compat createExec
// ---
// tags:
// - exec (compat)
@@ -74,9 +74,9 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/containers/{name}/create", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle("/containers/{name}/exec", s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /exec/{id}/start compat startExec
// ---
// tags:
@@ -169,15 +169,15 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/exec/{id}/json", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle("/exec/{id}/json", s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
/*
libpod api follows
*/
- // swagger:operation POST /libpod/containers/{name}/create libpod libpodCreateExec
+ // swagger:operation POST /libpod/containers/{name}/exec libpod libpodCreateExec
// ---
// tags:
// - exec
@@ -243,7 +243,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /libpod/exec/{id}/start libpod libpodStartExec
// ---
// tags:
@@ -332,6 +332,6 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index af2330665..5ba2263e8 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -37,7 +37,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: attributes for creating a pod
// schema:
// type: object
- // $ref: "#/definitions/PodCreateConfig"
+ // $ref: "#/definitions/PodSpecGenerator"
// responses:
// 200:
// $ref: "#/definitions/IdResponse"
@@ -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/api/server/swagger.go b/pkg/api/server/swagger.go
index 9156f3f8a..2433a6a05 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -2,6 +2,7 @@ package server
import (
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
)
@@ -178,6 +179,6 @@ type swagVolumeListResponse struct {
type swagHealthCheckRunResponse struct {
// in:body
Body struct {
- libpod.HealthCheckResults
+ define.HealthCheckResults
}
}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 231b6f232..bad1294f4 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -5,9 +5,10 @@ import (
"net/http"
"net/url"
"strconv"
+ "strings"
- "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
)
@@ -106,7 +107,7 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
// or a partial/full ID. The size bool determines whether the size of the container's root filesystem
// should be calculated. Calculating the size of a container requires extra work from the filesystem and
// is therefore slower.
-func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectContainerData, error) {
+func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectContainerData, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
@@ -119,7 +120,7 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectC
if err != nil {
return nil, err
}
- inspect := libpod.InspectContainerData{}
+ inspect := define.InspectContainerData{}
return &inspect, response.Process(&inspect)
}
@@ -194,7 +195,40 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error {
}
func Stats() {}
-func Top() {}
+
+// Top gathers statistics about the running processes in a container. The nameOrID can be a container name
+// or a partial/full ID. The descriptors allow for specifying which data to collect from the process.
+func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+
+ if len(descriptors) > 0 {
+ // flatten the slice into one string
+ params.Set("ps_args", strings.Join(descriptors, ","))
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+
+ body := handlers.ContainerTopOKBody{}
+ if err = response.Process(&body); err != nil {
+ return nil, err
+ }
+
+ // handlers.ContainerTopOKBody{} returns a slice of slices where each cell in the top table is an item.
+ // In libpod land, we're just using a slice with cells being split by tabs, which allows for an idiomatic
+ // usage of the tabwriter.
+ topOutput := []string{strings.Join(body.Titles, "\t")}
+ for _, out := range body.Processes {
+ topOutput = append(topOutput, strings.Join(out, "\t"))
+ }
+
+ return topOutput, err
+}
// Unpause resumes the given paused container. The nameOrID can be a container name
// or a partial/full ID.
diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go
new file mode 100644
index 000000000..48f9ed697
--- /dev/null
+++ b/pkg/bindings/containers/exec.go
@@ -0,0 +1,71 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "strings"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+// ExecCreate creates a new exec session in an existing container.
+// The exec session will not be started; that is done with ExecStart.
+// Returns ID of new exec session, or an error if one occurred.
+func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreateConfig) (string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ if config == nil {
+ return "", errors.Errorf("must provide a configuration for exec session")
+ }
+
+ requestJSON, err := json.Marshal(config)
+ if err != nil {
+ return "", errors.Wrapf(err, "error marshalling exec config to JSON")
+ }
+ jsonReader := strings.NewReader(string(requestJSON))
+
+ resp, err := conn.DoRequest(jsonReader, http.MethodPost, "/containers/%s/exec", nil, nameOrID)
+ if err != nil {
+ return "", err
+ }
+
+ respStruct := new(handlers.ExecCreateResponse)
+ if err := resp.Process(respStruct); err != nil {
+ return "", err
+ }
+
+ return respStruct.ID, nil
+}
+
+// ExecInspect inspects an existing exec session, returning detailed information
+// about it.
+func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSession, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ logrus.Debugf("Inspecting session ID %q", sessionID)
+
+ resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, sessionID)
+ if err != nil {
+ return nil, err
+ }
+
+ respStruct := new(define.InspectExecSession)
+ if err := resp.Process(respStruct); err != nil {
+ return nil, err
+ }
+
+ return respStruct, nil
+}
diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go
index 85cc2814c..2b783ac73 100644
--- a/pkg/bindings/containers/healthcheck.go
+++ b/pkg/bindings/containers/healthcheck.go
@@ -4,19 +4,19 @@ import (
"context"
"net/http"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
)
// RunHealthCheck executes the container's healthcheck and returns the health status of the
// container.
-func RunHealthCheck(ctx context.Context, nameOrID string) (*libpod.HealthCheckResults, error) {
+func RunHealthCheck(ctx context.Context, nameOrID string) (*define.HealthCheckResults, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
var (
- status libpod.HealthCheckResults
+ status define.HealthCheckResults
)
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/healthcheck", nil, nameOrID)
if err != nil {
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index e67965042..5e3af7a60 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -154,7 +154,7 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c
// Prune removes unused images from local storage. The optional filters can be used to further
// define which images should be pruned.
-func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
+func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]string, error) {
var (
deleted []string
)
@@ -163,6 +163,9 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
return nil, err
}
params := url.Values{}
+ if all != nil {
+ params.Set("all", strconv.FormatBool(*all))
+ }
if filters != nil {
stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
@@ -174,7 +177,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
if err != nil {
return deleted, err
}
- return deleted, response.Process(nil)
+ return deleted, response.Process(&deleted)
}
// Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required.
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index 1a8c31be1..bb0abebc4 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -5,14 +5,33 @@ import (
"net/http"
"net/url"
"strconv"
+ "strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ jsoniter "github.com/json-iterator/go"
)
-func CreatePod() error {
- // TODO
- return bindings.ErrNotImplemented
+func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entities.PodCreateReport, error) {
+ var (
+ pcr entities.PodCreateReport
+ )
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ specgenString, err := jsoniter.MarshalToString(s)
+ if err != nil {
+ return nil, err
+ }
+ stringReader := strings.NewReader(specgenString)
+ response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil)
+ if err != nil {
+ return nil, err
+ }
+ return &pcr, response.Process(&pcr)
}
// Exists is a lightweight method to determine if a pod exists in local storage
@@ -44,10 +63,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 +77,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 +111,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 +129,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 +162,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 +192,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 +204,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 +219,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/containers_test.go b/pkg/bindings/test/containers_test.go
index f5465c803..9dd9cb707 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -1,12 +1,12 @@
package test_bindings
import (
- "github.com/containers/libpod/libpod/define"
"net/http"
"strconv"
"strings"
"time"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/specgen"
@@ -34,7 +34,7 @@ var _ = Describe("Podman containers ", func() {
AfterEach(func() {
s.Kill()
- //bt.cleanup()
+ bt.cleanup()
})
It("podman pause a bogus container", func() {
@@ -380,4 +380,34 @@ var _ = Describe("Podman containers ", func() {
_, err = time.Parse(time.RFC1123Z, o)
Expect(err).To(BeNil())
})
+
+ It("podman top", func() {
+ var name = "top"
+ cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ // By name
+ output, err := containers.Top(bt.conn, name, nil)
+ Expect(err).To(BeNil())
+
+ // By id
+ output, err = containers.Top(bt.conn, cid, nil)
+ Expect(err).To(BeNil())
+
+ // With descriptors
+ output, err = containers.Top(bt.conn, cid, []string{"user,pid,hpid"})
+ Expect(err).To(BeNil())
+ header := strings.Split(output[0], "\t")
+ for _, d := range []string{"USER", "PID", "HPID"} {
+ Expect(d).To(BeElementOf(header))
+ }
+
+ // With bogus ID
+ _, err = containers.Top(bt.conn, "IdoNotExist", nil)
+ Expect(err).ToNot(BeNil())
+
+ // With bogus descriptors
+ _, err = containers.Top(bt.conn, cid, []string{"Me,Neither"})
+ Expect(err).To(BeNil())
+ })
})
diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go
new file mode 100644
index 000000000..1ef2197b6
--- /dev/null
+++ b/pkg/bindings/test/exec_test.go
@@ -0,0 +1,77 @@
+package test_bindings
+
+import (
+ "time"
+
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/bindings/containers"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman containers exec", func() {
+ var (
+ bt *bindingTest
+ s *gexec.Session
+ )
+
+ BeforeEach(func() {
+ bt = newBindingTest()
+ bt.RestoreImagesFromCache()
+ s = bt.startAPIService()
+ time.Sleep(1 * time.Second)
+ err := bt.NewConnection()
+ Expect(err).To(BeNil())
+ })
+
+ AfterEach(func() {
+ s.Kill()
+ bt.cleanup()
+ })
+
+ It("Podman exec create makes an exec session", func() {
+ name := "testCtr"
+ cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ execConfig := new(handlers.ExecCreateConfig)
+ execConfig.Cmd = []string{"echo", "hello world"}
+
+ sessionID, err := containers.ExecCreate(bt.conn, name, execConfig)
+ Expect(err).To(BeNil())
+ Expect(sessionID).To(Not(Equal("")))
+
+ inspectOut, err := containers.ExecInspect(bt.conn, sessionID)
+ Expect(err).To(BeNil())
+ Expect(inspectOut.ContainerID).To(Equal(cid))
+ Expect(inspectOut.ProcessConfig.Entrypoint).To(Equal("echo"))
+ Expect(len(inspectOut.ProcessConfig.Arguments)).To(Equal(1))
+ Expect(inspectOut.ProcessConfig.Arguments[0]).To(Equal("hello world"))
+ })
+
+ It("Podman exec create with bad command fails", func() {
+ name := "testCtr"
+ _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ execConfig := new(handlers.ExecCreateConfig)
+
+ _, err = containers.ExecCreate(bt.conn, name, execConfig)
+ Expect(err).To(Not(BeNil()))
+ })
+
+ It("Podman exec create with invalid container fails", func() {
+ execConfig := new(handlers.ExecCreateConfig)
+ execConfig.Cmd = []string{"echo", "hello world"}
+
+ _, err := containers.ExecCreate(bt.conn, "doesnotexist", execConfig)
+ Expect(err).To(Not(BeNil()))
+ })
+
+ It("Podman exec inspect on invalid session fails", func() {
+ _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000")
+ Expect(err).To(Not(BeNil()))
+ })
+})
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 5e4cfe7be..13b6086c3 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -16,22 +16,22 @@ import (
var _ = Describe("Podman images", func() {
var (
- //tempdir string
- //err error
- //podmanTest *PodmanTestIntegration
+ // tempdir string
+ // err error
+ // podmanTest *PodmanTestIntegration
bt *bindingTest
s *gexec.Session
err error
)
BeforeEach(func() {
- //tempdir, err = CreateTempDirInTempDir()
- //if err != nil {
+ // tempdir, err = CreateTempDirInTempDir()
+ // if err != nil {
// os.Exit(1)
- //}
- //podmanTest = PodmanTestCreate(tempdir)
- //podmanTest.Setup()
- //podmanTest.SeedImages()
+ // }
+ // podmanTest = PodmanTestCreate(tempdir)
+ // podmanTest.Setup()
+ // podmanTest.SeedImages()
bt = newBindingTest()
bt.RestoreImagesFromCache()
s = bt.startAPIService()
@@ -41,12 +41,13 @@ var _ = Describe("Podman images", func() {
})
AfterEach(func() {
- //podmanTest.Cleanup()
- //f := CurrentGinkgoTestDescription()
- //processTestResult(f)
+ // podmanTest.Cleanup()
+ // f := CurrentGinkgoTestDescription()
+ // processTestResult(f)
s.Kill()
bt.cleanup()
})
+
It("inspect image", func() {
// Inspect invalid image be 404
_, err = images.GetImage(bt.conn, "foobar5000", nil)
@@ -71,7 +72,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
// TODO it looks like the images API alwaays returns size regardless
// of bool or not. What should we do ?
- //Expect(data.Size).To(BeZero())
+ // Expect(data.Size).To(BeZero())
// Enabling the size parameter should result in size being populated
data, err = images.GetImage(bt.conn, alpine.name, &bindings.PTrue)
@@ -142,7 +143,7 @@ var _ = Describe("Podman images", func() {
err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName)
Expect(err).To(BeNil())
- //Validates if name updates when the image is retagged.
+ // Validates if name updates when the image is retagged.
_, err := images.GetImage(bt.conn, "alpine:demo", nil)
Expect(err).To(BeNil())
@@ -165,7 +166,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
Expect(len(imageSummary)).To(Equal(3))
- //Validate the image names.
+ // Validate the image names.
var names []string
for _, i := range imageSummary {
names = append(names, i.RepoTags...)
@@ -289,6 +290,7 @@ var _ = Describe("Podman images", func() {
Expect(data.Comment).To(Equal(testMessage))
})
+
It("History Image", func() {
// a bogus name should return a 404
_, err := images.History(bt.conn, "foobar")
@@ -343,4 +345,12 @@ var _ = Describe("Podman images", func() {
Expect(len(imgs)).To(BeNumerically(">=", 1))
})
+ It("Prune images", func() {
+ trueBoxed := true
+ results, err := images.Prune(bt.conn, &trueBoxed, nil)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(results)).To(BeNumerically(">", 0))
+ Expect(results).To(ContainElement("docker.io/library/alpine:latest"))
+ })
+
})
diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
index e94048a9c..0f786e341 100644
--- a/pkg/bindings/test/pods_test.go
+++ b/pkg/bindings/test/pods_test.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/pods"
+ "github.com/containers/libpod/pkg/specgen"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -71,7 +72,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())
@@ -79,9 +80,7 @@ var _ = Describe("Podman pods", func() {
// The test validates the list pod endpoint with passing filters as the params.
It("List pods with filters", func() {
- var (
- newpod2 string = "newpod2"
- )
+ newpod2 := "newpod2"
bt.Podcreate(&newpod2)
_, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod)
Expect(err).To(BeNil())
@@ -109,13 +108,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)
@@ -123,7 +123,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())
@@ -134,7 +134,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())
})
@@ -157,7 +157,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))
@@ -169,9 +169,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)).
@@ -179,9 +180,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)).
@@ -191,28 +193,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)).
@@ -220,11 +223,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))
@@ -234,10 +237,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))
@@ -262,11 +265,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())
@@ -276,21 +280,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)).
@@ -302,4 +308,15 @@ var _ = Describe("Podman pods", func() {
Expect(err).To(BeNil())
Expect(len(podSummary)).To(Equal(0))
})
+
+ It("simple create pod", func() {
+ ps := specgen.PodSpecGenerator{}
+ ps.Name = "foobar"
+ _, err := pods.CreatePodFromSpec(bt.conn, &ps)
+ Expect(err).To(BeNil())
+
+ exists, err := pods.Exists(bt.conn, "foobar")
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+ })
})
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 8b7406ae8..fbc0247ab 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -22,6 +22,11 @@ type BoolReport struct {
Value bool
}
+// StringSliceReport wraps a string slice.
+type StringSliceReport struct {
+ Value []string
+}
+
type PauseUnPauseOptions struct {
All bool
}
@@ -44,6 +49,16 @@ type StopReport struct {
Id string
}
+type TopOptions struct {
+ // CLI flags.
+ ListDescriptors bool
+ Latest bool
+
+ // Options for the API.
+ Descriptors []string
+ NameOrID string
+}
+
type KillOptions struct {
All bool
Latest bool
@@ -81,3 +96,13 @@ type RmReport struct {
Err error
Id string
}
+
+type ContainerInspectOptions struct {
+ Format string
+ Latest bool
+ Size bool
+}
+
+type ContainerInspectReport struct {
+ *define.InspectContainerData
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 2efdbd602..fceed1003 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -6,6 +6,7 @@ import (
type ContainerEngine interface {
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
+ ContainerInspect(ctx context.Context, namesOrIds []string, options ContainerInspectOptions) ([]*ContainerInspectReport, error)
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
@@ -13,7 +14,17 @@ type ContainerEngine interface {
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
+ ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
+ PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, 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/engine_image.go b/pkg/domain/entities/engine_image.go
index d44fdaf53..d0c860a04 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -5,7 +5,8 @@ import (
)
type ImageEngine interface {
- Delete(ctx context.Context, nameOrId string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
+ Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
+ Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error)
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index f04317e37..20af0356f 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -81,14 +81,18 @@ func (i *ImageSummary) IsDangling() bool {
}
type ImageDeleteOptions struct {
+ All bool
Force bool
}
-// ImageDeleteResponse is the response for removing an image from storage and containers
-// what was untagged vs actually removed
+// ImageDeleteResponse is the response for removing one or more image(s) from storage
+// and containers what was untagged vs actually removed
type ImageDeleteReport struct {
- Untagged []string `json:"untagged"`
- Deleted string `json:"deleted"`
+ Untagged []string `json:",omitempty"`
+ Deleted []string `json:",omitempty"`
+ Errors []error
+ ImageNotFound error
+ ImageInUse error
}
type ImageHistoryOptions struct{}
@@ -115,7 +119,7 @@ type ImageInspectOptions struct {
type ImageListOptions struct {
All bool `json:"all" schema:"all"`
- Filter []string `json:",omitempty"`
+ Filter []string `json:"Filter,omitempty"`
Filters url.Values `json:"filters" schema:"filters"`
}
@@ -124,8 +128,9 @@ type ImageListOptions struct {
// }
type ImagePruneOptions struct {
- All bool
- Filter ImageFilter
+ All bool `json:"all" schema:"all"`
+ Filter []string `json:"filter" schema:"filter"`
+ Filters url.Values `json:"filters" schema:"filters"`
}
type ImagePruneReport struct {
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
new file mode 100644
index 000000000..efda17d65
--- /dev/null
+++ b/pkg/domain/entities/pods.go
@@ -0,0 +1,143 @@
+package entities
+
+import (
+ "time"
+
+ "github.com/containers/libpod/pkg/specgen"
+)
+
+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
+}
+
+type PodCreateOptions struct {
+ CGroupParent string
+ Hostname string
+ Infra bool
+ InfraImage string
+ InfraCommand string
+ Labels map[string]string
+ Name string
+ Net *NetOptions
+ Share []string
+}
+
+type PodCreateReport struct {
+ Id string
+}
+
+func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
+ // Basic Config
+ s.Name = p.Name
+ s.Hostname = p.Hostname
+ s.Labels = p.Labels
+ s.NoInfra = !p.Infra
+ s.InfraCommand = []string{p.InfraCommand}
+ s.InfraImage = p.InfraImage
+ s.SharedNamespaces = p.Share
+
+ // Networking config
+ s.NetNS = p.Net.Network
+ s.StaticIP = p.Net.StaticIP
+ s.StaticMAC = p.Net.StaticMAC
+ s.PortMappings = p.Net.PublishPorts
+ s.CNINetworks = p.Net.CNINetworks
+ if p.Net.DNSHost {
+ s.NoManageResolvConf = true
+ }
+ s.DNSServer = p.Net.DNSServers
+ s.DNSSearch = p.Net.DNSSearch
+ s.DNSOption = p.Net.DNSOptions
+ s.NoManageHosts = p.Net.NoHosts
+ s.HostAdd = p.Net.AddHosts
+
+ // Cgroup
+ s.CgroupParent = p.CGroupParent
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index e7757a74b..a1a729584 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -1,5 +1,12 @@
package entities
+import (
+ "net"
+
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
type Container struct {
IdOrNamed
}
@@ -15,3 +22,23 @@ type Report struct {
type PodDeleteReport struct{ Report }
type PodPruneOptions struct{}
+
+type PodPruneReport struct{ Report }
+type VolumeDeleteOptions struct{}
+type VolumeDeleteReport struct{ Report }
+
+// NetOptions reflect the shared network options between
+// pods and containers
+type NetOptions struct {
+ AddHosts []string
+ CNINetworks []string
+ DNSHost bool
+ DNSOptions []string
+ DNSSearch []string
+ DNSServers []net.IP
+ Network specgen.Namespace
+ NoHosts bool
+ PublishPorts []ocicni.PortMapping
+ StaticIP *net.IP
+ StaticMAC *net.HardwareAddr
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index a3da310c2..3965c5f75 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -239,3 +239,41 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
}
return reports, nil
}
+
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.ContainerInspectOptions) ([]*entities.ContainerInspectReport, error) {
+ var reports []*entities.ContainerInspectReport
+ ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range ctrs {
+ data, err := c.Inspect(options.Size)
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
+ var (
+ container *libpod.Container
+ err error
+ )
+
+ // Look up the container.
+ if options.Latest {
+ container, err = ic.Libpod.GetLatestContainer()
+ } else {
+ container, err = ic.Libpod.LookupContainer(options.NameOrID)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to lookup requested container")
+ }
+
+ // Run Top.
+ report := &entities.StringSliceReport{}
+ report.Value, err = container.Top(options.Descriptors)
+ return report, err
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 6e9d7f566..44420c1e1 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -4,38 +4,102 @@ package abi
import (
"context"
+ "fmt"
libpodImage "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/domain/utils"
+ "github.com/containers/storage"
+ "github.com/pkg/errors"
)
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
- if err != nil {
- return nil, err
+func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
+ if _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId); err != nil {
+ return &entities.BoolReport{}, nil
}
+ return &entities.BoolReport{Value: true}, nil
+}
- results, err := ir.Libpod.RemoveImage(ctx, image, opts.Force)
- if err != nil {
- return nil, err
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ report := entities.ImageDeleteReport{}
+
+ if opts.All {
+ var previousTargets []*libpodImage.Image
+ repeatRun:
+ targets, err := ir.Libpod.ImageRuntime().GetRWImages()
+ if err != nil {
+ return &report, errors.Wrapf(err, "unable to query local images")
+ }
+
+ if len(targets) > 0 && len(targets) == len(previousTargets) {
+ return &report, errors.New("unable to delete all images; re-run the rmi command again.")
+ }
+ previousTargets = targets
+
+ for _, img := range targets {
+ isParent, err := img.IsParent(ctx)
+ if err != nil {
+ return &report, err
+ }
+ if isParent {
+ continue
+ }
+ err = ir.deleteImage(ctx, img, opts, report)
+ report.Errors = append(report.Errors, err)
+ }
+ if len(previousTargets) != 1 {
+ goto repeatRun
+ }
+ return &report, nil
}
- report := entities.ImageDeleteReport{}
- if err := utils.DeepCopy(&report, results); err != nil {
- return nil, err
+ for _, id := range nameOrId {
+ image, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ if err != nil {
+ return nil, err
+ }
+
+ err = ir.deleteImage(ctx, image, opts, report)
+ if err != nil {
+ return &report, err
+ }
}
return &report, nil
}
+func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, opts entities.ImageDeleteOptions, report entities.ImageDeleteReport) error {
+ results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case storage.ErrImageUsedByContainer:
+ report.ImageInUse = errors.New(
+ fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
+ return nil
+ case libpodImage.ErrNoSuchImage:
+ report.ImageNotFound = err
+ return nil
+ default:
+ return err
+ }
+
+ report.Deleted = append(report.Deleted, results.Deleted)
+ report.Untagged = append(report.Untagged, results.Untagged...)
+ return nil
+}
+
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{})
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
return nil, err
}
- report := entities.ImagePruneReport{}
- copy(report.Report.Id, results)
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ Size: 0,
+ }
return &report, nil
}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index de22de68e..619e973cf 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -4,12 +4,47 @@ package abi
import (
"context"
- "github.com/pkg/errors"
+ "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/containers/libpod/pkg/specgen"
+ "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 +52,201 @@ 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
+}
+
+func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
+ podSpec := specgen.NewPodSpecGenerator()
+ opts.ToPodSpecGen(podSpec)
+ pod, err := podSpec.MakePod(ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.PodCreateReport{Id: pod.ID()}, nil
+}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 0cc20474e..bdae4359d 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -107,13 +107,26 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
UID: v.UID(),
GID: v.GID(),
}
- reports = append(reports, &entities.VolumeInspectReport{&config})
+ reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config})
}
return reports, nil
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
- return ic.Libpod.PruneVolumes(ctx)
+ var (
+ reports []*entities.VolumePruneReport
+ )
+ pruned, err := ic.Libpod.PruneVolumes(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
+ }
+ return reports, nil
}
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
@@ -140,7 +153,7 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
UID: v.UID(),
GID: v.GID(),
}
- reports = append(reports, &entities.VolumeListReport{config})
+ reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config})
}
return reports, nil
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index a8ecff41b..3db38ea5c 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
)
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
@@ -138,3 +139,36 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
}
return reports, nil
}
+
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.ContainerInspectOptions) ([]*entities.ContainerInspectReport, error) {
+ var (
+ reports []*entities.ContainerInspectReport
+ )
+ ctrs, err := getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, con := range ctrs {
+ data, err := containers.Inspect(ic.ClientCxt, con.ID, &options.Size)
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
+ switch {
+ case options.Latest:
+ return nil, errors.New("latest is not supported")
+ case options.NameOrID == "":
+ return nil, errors.New("NameOrID must be specified")
+ }
+
+ topOutput, err := containers.Top(ic.ClientCxt, options.NameOrID, options.Descriptors)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.StringSliceReport{Value: topOutput}, 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/images.go b/pkg/domain/infra/tunnel/images.go
index 60df40498..6a3adc9ee 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,34 +2,36 @@ package tunnel
import (
"context"
- "net/url"
images "github.com/containers/libpod/pkg/bindings/images"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/utils"
)
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- results, err := images.Remove(ir.ClientCxt, nameOrId, &opts.Force)
- if err != nil {
- return nil, err
- }
+func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
+ found, err := images.Exists(ir.ClientCxt, nameOrId)
+ return &entities.BoolReport{Value: found}, err
+}
- report := entities.ImageDeleteReport{
- Untagged: nil,
- Deleted: "",
- }
+func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
+ report := entities.ImageDeleteReport{}
- for _, e := range results {
- if a, ok := e["Deleted"]; ok {
- report.Deleted = a
+ for _, id := range nameOrId {
+ results, err := images.Remove(ir.ClientCxt, id, &opts.Force)
+ if err != nil {
+ return nil, err
}
+ for _, e := range results {
+ if a, ok := e["Deleted"]; ok {
+ report.Deleted = append(report.Deleted, a)
+ }
- if a, ok := e["Untagged"]; ok {
- report.Untagged = append(report.Untagged, a)
+ if a, ok := e["Untagged"]; ok {
+ report.Untagged = append(report.Untagged, a)
+ }
}
}
- return &report, err
+ return &report, nil
}
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
@@ -69,12 +71,17 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := images.Prune(ir.ClientCxt, url.Values{})
+ results, err := images.Prune(ir.ClientCxt, &opts.All, opts.Filters)
if err != nil {
return nil, err
}
- report := entities.ImagePruneReport{}
- copy(report.Report.Id, results)
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ Size: 0,
+ }
return &report, nil
}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index 500069d51..4894874e5 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -5,9 +5,175 @@ import (
"github.com/containers/libpod/pkg/bindings/pods"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
)
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
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
+}
+
+func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
+ podSpec := specgen.NewPodSpecGenerator()
+ opts.ToPodSpecGen(podSpec)
+ return pods.CreatePodFromSpec(ic.ClientCxt, podSpec)
+}
diff --git a/pkg/specgen/create.go b/pkg/specgen/container_create.go
index aefbe7405..cf082441d 100644
--- a/pkg/specgen/create.go
+++ b/pkg/specgen/container_create.go
@@ -13,7 +13,7 @@ import (
// MakeContainer creates a container based on the SpecGenerator
func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, error) {
- if err := s.validate(rt); err != nil {
+ if err := s.validate(); err != nil {
return nil, errors.Wrap(err, "invalid config provided")
}
rtc, err := rt.GetConfig()
diff --git a/pkg/specgen/validate.go b/pkg/specgen/container_validate.go
index dd5ca3a55..b27659f5f 100644
--- a/pkg/specgen/validate.go
+++ b/pkg/specgen/container_validate.go
@@ -3,7 +3,7 @@ package specgen
import (
"strings"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
)
@@ -23,7 +23,7 @@ func exclusiveOptions(opt1, opt2 string) error {
// Validate verifies that the given SpecGenerator is valid and satisfies required
// input for creating a container.
-func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
+func (s *SpecGenerator) validate() error {
//
// ContainerBasicConfig
@@ -138,9 +138,6 @@ func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
if err := s.IpcNS.validate(); err != nil {
return err
}
- if err := validateNetNS(&s.NetNS); err != nil {
- return err
- }
if err := s.PidNS.validate(); err != nil {
return err
}
@@ -155,5 +152,16 @@ func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
if len(s.WorkDir) < 1 {
s.WorkDir = "/"
}
+
+ // Set defaults if network info is not provided
+ if s.NetNS.NSMode == "" {
+ s.NetNS.NSMode = Bridge
+ if rootless.IsRootless() {
+ s.NetNS.NSMode = Slirp
+ }
+ }
+ if err := validateNetNS(&s.NetNS); err != nil {
+ return err
+ }
return nil
}
diff --git a/pkg/specgen/pod_create.go b/pkg/specgen/pod_create.go
new file mode 100644
index 000000000..06aa24e22
--- /dev/null
+++ b/pkg/specgen/pod_create.go
@@ -0,0 +1,83 @@
+package specgen
+
+import (
+ "context"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/sirupsen/logrus"
+)
+
+func (p *PodSpecGenerator) MakePod(rt *libpod.Runtime) (*libpod.Pod, error) {
+ if err := p.validate(); err != nil {
+ return nil, err
+ }
+ options, err := p.createPodOptions()
+ if err != nil {
+ return nil, err
+ }
+ return rt.NewPod(context.Background(), options...)
+}
+
+func (p *PodSpecGenerator) createPodOptions() ([]libpod.PodCreateOption, error) {
+ var (
+ options []libpod.PodCreateOption
+ )
+ if !p.NoInfra {
+ options = append(options, libpod.WithInfraContainer())
+ nsOptions, err := shared.GetNamespaceOptions(p.SharedNamespaces)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, nsOptions...)
+ }
+ if len(p.CgroupParent) > 0 {
+ options = append(options, libpod.WithPodCgroupParent(p.CgroupParent))
+ }
+ if len(p.Labels) > 0 {
+ options = append(options, libpod.WithPodLabels(p.Labels))
+ }
+ if len(p.Name) > 0 {
+ options = append(options, libpod.WithPodName(p.Name))
+ }
+ if len(p.Hostname) > 0 {
+ options = append(options, libpod.WithPodHostname(p.Hostname))
+ }
+ if len(p.HostAdd) > 0 {
+ options = append(options, libpod.WithPodHosts(p.HostAdd))
+ }
+ if len(p.DNSOption) > 0 {
+ options = append(options, libpod.WithPodDNSOption(p.DNSOption))
+ }
+ if len(p.DNSSearch) > 0 {
+ options = append(options, libpod.WithPodDNSSearch(p.DNSSearch))
+ }
+ if p.StaticIP != nil {
+ options = append(options, libpod.WithPodStaticIP(*p.StaticIP))
+ }
+ if p.StaticMAC != nil {
+ options = append(options, libpod.WithPodStaticMAC(*p.StaticMAC))
+ }
+ if p.NoManageResolvConf {
+ options = append(options, libpod.WithPodUseImageResolvConf())
+ }
+ switch p.NetNS.NSMode {
+ case Bridge:
+ logrus.Debugf("Pod using default network mode")
+ case Host:
+ logrus.Debugf("Pod will use host networking")
+ options = append(options, libpod.WithPodHostNetwork())
+ default:
+ logrus.Debugf("Pod joining CNI networks: %v", p.CNINetworks)
+ options = append(options, libpod.WithPodNetworks(p.CNINetworks))
+ }
+
+ if p.NoManageHosts {
+ options = append(options, libpod.WithPodUseImageHosts())
+ }
+ if len(p.PortMappings) > 0 {
+ options = append(options, libpod.WithInfraContainerPorts(p.PortMappings))
+ }
+ options = append(options, libpod.WithPodCgroups())
+ return options, nil
+}
diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go
new file mode 100644
index 000000000..50309f096
--- /dev/null
+++ b/pkg/specgen/pod_validate.go
@@ -0,0 +1,104 @@
+package specgen
+
+import (
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+)
+
+var (
+ // ErrInvalidPodSpecConfig describes an error given when the podspecgenerator is invalid
+ ErrInvalidPodSpecConfig error = errors.New("invalid pod spec")
+)
+
+func exclusivePodOptions(opt1, opt2 string) error {
+ return errors.Wrapf(ErrInvalidPodSpecConfig, "%s and %s are mutually exclusive pod options", opt1, opt2)
+}
+
+func (p *PodSpecGenerator) validate() error {
+ // PodBasicConfig
+ if p.NoInfra {
+ if len(p.InfraCommand) > 0 {
+ return exclusivePodOptions("NoInfra", "InfraCommand")
+ }
+ if len(p.InfraImage) > 0 {
+ return exclusivePodOptions("NoInfra", "InfraImage")
+ }
+ if len(p.SharedNamespaces) > 0 {
+ return exclusivePodOptions("NoInfo", "SharedNamespaces")
+ }
+ }
+
+ // PodNetworkConfig
+ if err := p.NetNS.validate(); err != nil {
+ return err
+ }
+ if p.NoInfra {
+ if p.NetNS.NSMode == NoNetwork {
+ return errors.New("NoInfra and a none network cannot be used toegther")
+ }
+ if p.StaticIP != nil {
+ return exclusivePodOptions("NoInfra", "StaticIP")
+ }
+ if p.StaticMAC != nil {
+ return exclusivePodOptions("NoInfra", "StaticMAC")
+ }
+ if len(p.DNSOption) > 0 {
+ return exclusivePodOptions("NoInfra", "DNSOption")
+ }
+ if len(p.DNSSearch) > 0 {
+ return exclusivePodOptions("NoInfo", "DNSSearch")
+ }
+ if len(p.DNSServer) > 0 {
+ return exclusivePodOptions("NoInfra", "DNSServer")
+ }
+ if len(p.HostAdd) > 0 {
+ return exclusivePodOptions("NoInfra", "HostAdd")
+ }
+ if p.NoManageResolvConf {
+ return exclusivePodOptions("NoInfra", "NoManageResolvConf")
+ }
+ }
+ if p.NetNS.NSMode != Bridge {
+ if len(p.PortMappings) > 0 {
+ return errors.New("PortMappings can only be used with Bridge mode networking")
+ }
+ if len(p.CNINetworks) > 0 {
+ return errors.New("CNINetworks can only be used with Bridge mode networking")
+ }
+ }
+ if p.NoManageResolvConf {
+ if len(p.DNSServer) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSServer")
+ }
+ if len(p.DNSSearch) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSSearch")
+ }
+ if len(p.DNSOption) > 0 {
+ return exclusivePodOptions("NoManageResolvConf", "DNSOption")
+ }
+ }
+ if p.NoManageHosts && len(p.HostAdd) > 0 {
+ return exclusivePodOptions("NoManageHosts", "HostAdd")
+ }
+
+ if err := p.NetNS.validate(); err != nil {
+ return err
+ }
+
+ // Set Defaults
+ if p.NetNS.Value == "" {
+ if rootless.IsRootless() {
+ p.NetNS.NSMode = Slirp
+ } else {
+ p.NetNS.NSMode = Bridge
+ }
+ }
+ if len(p.InfraImage) < 1 {
+ p.InfraImage = define.DefaultInfraImage
+ }
+ if len(p.InfraCommand) < 1 {
+ p.InfraCommand = []string{define.DefaultInfraCommand}
+ }
+ return nil
+}
diff --git a/pkg/specgen/pod.go b/pkg/specgen/podspecgen.go
index 1aada83c4..3f830014d 100644
--- a/pkg/specgen/pod.go
+++ b/pkg/specgen/podspecgen.go
@@ -138,3 +138,16 @@ type PodCgroupConfig struct {
// Optional.
CgroupParent string `json:"cgroup_parent,omitempty"`
}
+
+// PodSpecGenerator describes options to create a pod
+// swagger:model PodSpecGenerator
+type PodSpecGenerator struct {
+ PodBasicConfig
+ PodNetworkConfig
+ PodCgroupConfig
+}
+
+// NewPodSpecGenerator creates a new pod spec
+func NewPodSpecGenerator() *PodSpecGenerator {
+ return &PodSpecGenerator{}
+}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index b123c1da5..89c76c273 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -394,18 +394,18 @@ type SpecGenerator struct {
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
func NewSpecGenerator(image string) *SpecGenerator {
- net := ContainerNetworkConfig{
+ networkConfig := ContainerNetworkConfig{
NetNS: Namespace{
NSMode: Bridge,
},
}
csc := ContainerStorageConfig{Image: image}
if rootless.IsRootless() {
- net.NetNS.NSMode = Slirp
+ networkConfig.NetNS.NSMode = Slirp
}
return &SpecGenerator{
ContainerStorageConfig: csc,
- ContainerNetworkConfig: net,
+ ContainerNetworkConfig: networkConfig,
}
}
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index cbb4a70cc..e497cb537 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -6,7 +6,7 @@ import (
"encoding/json"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
)
@@ -113,11 +113,11 @@ func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
if err != nil {
return call.ReplyVolumesPrune([]string{}, []string{err.Error()})
}
- for _, i := range responses {
- if i.Err == nil {
- prunedNames = append(prunedNames, i.Id)
+ for k, v := range responses {
+ if v == nil {
+ prunedNames = append(prunedNames, k)
} else {
- prunedErrors = append(prunedErrors, i.Err.Error())
+ prunedErrors = append(prunedErrors, v.Error())
}
}
return call.ReplyVolumesPrune(prunedNames, prunedErrors)
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index ab345b8f2..70d9f8203 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -12,11 +12,11 @@ t GET libpod/pods/notfoo/exists 404
t GET libpod/pods/foo/json 200 \
.Config.name=foo \
.Config.id=$pod_id \
- .Containers=null
+ .Containers\|length=1
t GET libpod/pods/json 200 \
- .[0].Config.name=foo \
- .[0].Config.id=$pod_id \
- .[0].Containers=null
+ .[0].Name=foo \
+ .[0].Id=$pod_id \
+ .[0].Containers\|length=1
# Cannot create a dup pod with the same name
t POST libpod/pods/create name=foo 409 .cause="pod already exists"
@@ -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 \
@@ -34,7 +34,7 @@ fi
t POST libpod/pods/foo/unpause '' 200
t POST libpod/pods/foo/unpause '' 200 # (2nd time)
t POST libpod/pods/foo/stop '' 304
-t POST libpod/pods/foo/restart '' 500 .cause="no such container"
+t POST libpod/pods/foo/restart '' 200
t POST libpod/pods/bar/restart '' 404
@@ -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
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 16b971e65..b10c3237d 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -15,6 +15,7 @@ import (
"time"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/rootless"
. "github.com/containers/libpod/test/utils"
@@ -320,7 +321,7 @@ func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
}
// InspectContainer returns a container's inspect data in JSON format
-func (p *PodmanTestIntegration) InspectContainer(name string) []libpod.InspectContainerData {
+func (p *PodmanTestIntegration) InspectContainer(name string) []define.InspectContainerData {
cmd := []string{"inspect", name}
session := p.Podman(cmd)
session.WaitWithDefaultTimeout()
@@ -492,8 +493,8 @@ func (p *PodmanTestIntegration) PullImage(image string) error {
// InspectContainerToJSON takes the session output of an inspect
// container and returns json
-func (s *PodmanSessionIntegration) InspectContainerToJSON() []libpod.InspectContainerData {
- var i []libpod.InspectContainerData
+func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectContainerData {
+ var i []define.InspectContainerData
err := json.Unmarshal(s.Out.Contents(), &i)
Expect(err).To(BeNil())
return i
@@ -519,6 +520,21 @@ func (p *PodmanTestIntegration) CreatePod(name string) (*PodmanSessionIntegratio
return session, session.ExitCode(), session.OutputToString()
}
+// CreatePod creates a pod with no infra container and some labels.
+// it optionally takes a pod name
+func (p *PodmanTestIntegration) CreatePodWithLabels(name string, labels map[string]string) (*PodmanSessionIntegration, int, string) {
+ var podmanArgs = []string{"pod", "create", "--infra=false", "--share", ""}
+ if name != "" {
+ podmanArgs = append(podmanArgs, "--name", name)
+ }
+ for labelKey, labelValue := range labels {
+ podmanArgs = append(podmanArgs, "--label", fmt.Sprintf("%s=%s", labelKey, labelValue))
+ }
+ session := p.Podman(podmanArgs)
+ session.WaitWithDefaultTimeout()
+ return session, session.ExitCode(), session.OutputToString()
+}
+
func (p *PodmanTestIntegration) RunTopContainerInPod(name, pod string) *PodmanSessionIntegration {
var podmanArgs = []string{"run", "--pod", pod}
if name != "" {
diff --git a/test/e2e/config.go b/test/e2e/config.go
index 95b0481b3..49a47c7da 100644
--- a/test/e2e/config.go
+++ b/test/e2e/config.go
@@ -10,7 +10,7 @@ var (
ALPINEAMD64ID = "961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4"
ALPINEARM64DIGEST = "docker.io/library/alpine@sha256:db7f3dcef3d586f7dd123f107c93d7911515a5991c4b9e51fa2a43e46335a43e"
ALPINEARM64ID = "915beeae46751fc564998c79e73a1026542e945ca4f73dc841d09ccc6c2c0672"
- infra = "k8s.gcr.io/pause:3.1"
+ infra = "k8s.gcr.io/pause:3.2"
BB = "docker.io/library/busybox:latest"
healthcheck = "docker.io/libpod/alpine_healthcheck:latest"
ImageCacheDir = "/tmp/podman/imagecachedir"
diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go
index aa07be55c..551ad3818 100644
--- a/test/e2e/pod_ps_test.go
+++ b/test/e2e/pod_ps_test.go
@@ -204,4 +204,27 @@ var _ = Describe("Podman ps", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(BeEmpty())
})
+
+ It("podman pod ps filter labels", func() {
+ _, ec, podid1 := podmanTest.CreatePod("")
+ Expect(ec).To(Equal(0))
+
+ _, ec, podid2 := podmanTest.CreatePodWithLabels("", map[string]string{
+ "io.podman.test.label": "value1",
+ "io.podman.test.key": "irrelevant-value",
+ })
+ Expect(ec).To(Equal(0))
+
+ _, ec, podid3 := podmanTest.CreatePodWithLabels("", map[string]string{
+ "io.podman.test.label": "value2",
+ })
+ Expect(ec).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"pod", "ps", "--no-trunc", "--filter", "label=io.podman.test.key", "--filter", "label=io.podman.test.label=value1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Not(ContainSubstring(podid1)))
+ Expect(session.OutputToString()).To(ContainSubstring(podid2))
+ Expect(session.OutputToString()).To(Not(ContainSubstring(podid3)))
+ })
})
diff --git a/test/endpoint/endpoint.go b/test/endpoint/endpoint.go
index f1634b6f0..5b5484865 100644
--- a/test/endpoint/endpoint.go
+++ b/test/endpoint/endpoint.go
@@ -26,7 +26,7 @@ var (
ImageCacheDir = "/tmp/podman/imagecachedir"
VarlinkBinary = "/usr/bin/varlink"
ALPINE = "docker.io/library/alpine:latest"
- infra = "k8s.gcr.io/pause:3.1"
+ infra = "k8s.gcr.io/pause:3.2"
BB = "docker.io/library/busybox:latest"
redis = "docker.io/library/redis:alpine"
fedoraMinimal = "quay.io/libpod/fedora-minimal:latest"