summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--cmd/podman/main_local_unsupported.go44
-rw-r--r--cmd/podman/shared/funcs_linux_test.go119
-rw-r--r--cmd/podman/shared/funcs_test.go112
-rw-r--r--cmd/podmanV2/containers/exists.go43
-rw-r--r--cmd/podmanV2/containers/wait.go82
-rw-r--r--cmd/podmanV2/images/inspect.go2
-rw-r--r--cmd/podmanV2/main.go13
-rw-r--r--cmd/podmanV2/parse/parse.go188
-rw-r--r--cmd/podmanV2/parse/parse_test.go152
-rw-r--r--cmd/podmanV2/pods/exists.go43
-rw-r--r--cmd/podmanV2/registry/registry.go14
-rw-r--r--cmd/podmanV2/root.go3
-rw-r--r--cmd/podmanV2/volumes/create.go72
-rw-r--r--cmd/podmanV2/volumes/volume.go10
-rw-r--r--libpod/container_top_unsupported.go6
-rw-r--r--libpod/lock/shm/shm_lock_test.go2
-rw-r--r--libpod/util_unsupported.go2
-rw-r--r--pkg/adapter/terminal_unsupported.go23
-rw-r--r--pkg/api/handlers/libpod/containers.go6
-rw-r--r--pkg/api/handlers/libpod/volumes.go20
-rw-r--r--pkg/api/handlers/types.go12
-rw-r--r--pkg/api/server/swagger.go3
-rw-r--r--pkg/bindings/connection.go7
-rw-r--r--pkg/bindings/containers/containers.go5
-rw-r--r--pkg/bindings/test/common_test.go5
-rw-r--r--pkg/bindings/test/containers_test.go7
-rw-r--r--pkg/bindings/test/volumes_test.go29
-rw-r--r--pkg/bindings/volumes/volumes.go6
-rw-r--r--pkg/domain/entities/containers.go22
-rw-r--r--pkg/domain/entities/engine.go9
-rw-r--r--pkg/domain/entities/engine_container.go16
-rw-r--r--pkg/domain/entities/volumes.go41
-rw-r--r--pkg/domain/infra/abi/containers.go66
-rw-r--r--pkg/domain/infra/abi/parse/parse.go68
-rw-r--r--pkg/domain/infra/abi/pods.go19
-rw-r--r--pkg/domain/infra/abi/runtime.go2
-rw-r--r--pkg/domain/infra/abi/volumes.go38
-rw-r--r--pkg/domain/infra/runtime_abi.go19
-rw-r--r--pkg/domain/infra/runtime_image_proxy.go6
-rw-r--r--pkg/domain/infra/runtime_libpod.go12
-rw-r--r--pkg/domain/infra/runtime_proxy.go39
-rw-r--r--pkg/domain/infra/runtime_tunnel.go16
-rw-r--r--pkg/domain/infra/tunnel/containers.go42
-rw-r--r--pkg/domain/infra/tunnel/helpers.go41
-rw-r--r--pkg/domain/infra/tunnel/pods.go13
-rw-r--r--pkg/domain/infra/tunnel/runtime.go12
-rw-r--r--pkg/domain/infra/tunnel/volumes.go16
-rw-r--r--pkg/specgen/config_unsupported.go3
-rw-r--r--pkg/specgen/pod.go140
-rw-r--r--pkg/specgen/specgen.go2
-rw-r--r--pkg/util/utils_linux_test.go29
-rw-r--r--pkg/util/utils_test.go19
53 files changed, 1429 insertions, 292 deletions
diff --git a/.gitignore b/.gitignore
index 6ebb899cf..ea154fe5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,4 @@ podman*.tar.gz
contrib/spec/podman.spec
*.rpm
*.coverprofile
+/cmd/podmanV2/podmanV2
diff --git a/cmd/podman/main_local_unsupported.go b/cmd/podman/main_local_unsupported.go
new file mode 100644
index 000000000..75728627e
--- /dev/null
+++ b/cmd/podman/main_local_unsupported.go
@@ -0,0 +1,44 @@
+// +build !remoteclient,!linux
+
+package main
+
+// The ONLY purpose of this file is to allow the subpackage to compile. Don’t expect anything
+// to work.
+
+import (
+ "syscall"
+
+ "github.com/spf13/cobra"
+)
+
+const remote = false
+
+func setSyslog() error {
+ return nil
+}
+
+func profileOn(cmd *cobra.Command) error {
+ return nil
+}
+
+func profileOff(cmd *cobra.Command) error {
+ return nil
+}
+
+func setupRootless(cmd *cobra.Command, args []string) error {
+ return nil
+}
+
+func setRLimits() error {
+ return nil
+}
+
+func setUMask() {
+ // Be sure we can create directories with 0755 mode.
+ syscall.Umask(0022)
+}
+
+// checkInput can be used to verify any of the globalopt values
+func checkInput() error {
+ return nil
+}
diff --git a/cmd/podman/shared/funcs_linux_test.go b/cmd/podman/shared/funcs_linux_test.go
new file mode 100644
index 000000000..88571153f
--- /dev/null
+++ b/cmd/podman/shared/funcs_linux_test.go
@@ -0,0 +1,119 @@
+package shared
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenerateCommand(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Nil(t, err)
+ assert.Equal(t, "hello world", newCommand[11])
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandCheckSubstitution(t *testing.T) {
+ type subsTest struct {
+ input string
+ expected string
+ shouldFail bool
+ }
+
+ absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(absTmpFile.Name())
+
+ relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(relTmpFile.Name())
+ relTmpCmd, err := filepath.Abs(relTmpFile.Name())
+ assert.Nil(t, err, "error getting absolute path for relative tmpfile")
+
+ // this has a (low) potential of race conditions but no other way
+ removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
+ assert.Nil(t, err, "error creating tempfile")
+ os.Remove(removedTmpFile.Name())
+
+ absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
+ tests := []subsTest{
+ {
+ input: "docker run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: "podman run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: absTmpCmd,
+ expected: absTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "./" + relTmpFile.Name(),
+ expected: relTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "ls -la",
+ expected: "ls -la",
+ shouldFail: false,
+ },
+ {
+ input: removedTmpFile.Name(),
+ expected: "",
+ shouldFail: true,
+ },
+ }
+
+ for _, test := range tests {
+ newCommand, err := GenerateCommand(test.input, "foo", "bar", "")
+ if test.shouldFail {
+ assert.NotNil(t, err)
+ } else {
+ assert.Nil(t, err)
+ }
+ assert.Equal(t, test.expected, strings.Join(newCommand, " "))
+ }
+}
+
+func TestGenerateCommandPath(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
+ newCommand, _ := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandNoSetName(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandNoName(t *testing.T) {
+ inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandAlreadyPodman(t *testing.T) {
+ inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
diff --git a/cmd/podman/shared/funcs_test.go b/cmd/podman/shared/funcs_test.go
index c05348242..dd856166e 100644
--- a/cmd/podman/shared/funcs_test.go
+++ b/cmd/podman/shared/funcs_test.go
@@ -1,11 +1,6 @@
package shared
import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
"testing"
"github.com/containers/libpod/pkg/util"
@@ -17,113 +12,6 @@ var (
imageName = "bar"
)
-func TestGenerateCommand(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
- newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Nil(t, err)
- assert.Equal(t, "hello world", newCommand[11])
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandCheckSubstitution(t *testing.T) {
- type subsTest struct {
- input string
- expected string
- shouldFail bool
- }
-
- absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
- assert.Nil(t, err, "error creating tempfile")
- defer os.Remove(absTmpFile.Name())
-
- relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
- assert.Nil(t, err, "error creating tempfile")
- defer os.Remove(relTmpFile.Name())
- relTmpCmd, err := filepath.Abs(relTmpFile.Name())
- assert.Nil(t, err, "error getting absolute path for relative tmpfile")
-
- // this has a (low) potential of race conditions but no other way
- removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
- assert.Nil(t, err, "error creating tempfile")
- os.Remove(removedTmpFile.Name())
-
- absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
- tests := []subsTest{
- {
- input: "docker run -it alpine:latest",
- expected: "/proc/self/exe run -it alpine:latest",
- shouldFail: false,
- },
- {
- input: "podman run -it alpine:latest",
- expected: "/proc/self/exe run -it alpine:latest",
- shouldFail: false,
- },
- {
- input: absTmpCmd,
- expected: absTmpCmd,
- shouldFail: false,
- },
- {
- input: "./" + relTmpFile.Name(),
- expected: relTmpCmd,
- shouldFail: false,
- },
- {
- input: "ls -la",
- expected: "ls -la",
- shouldFail: false,
- },
- {
- input: removedTmpFile.Name(),
- expected: "",
- shouldFail: true,
- },
- }
-
- for _, test := range tests {
- newCommand, err := GenerateCommand(test.input, "foo", "bar", "")
- if test.shouldFail {
- assert.NotNil(t, err)
- } else {
- assert.Nil(t, err)
- }
- assert.Equal(t, test.expected, strings.Join(newCommand, " "))
- }
-}
-
-func TestGenerateCommandPath(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand, _ := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandNoSetName(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandNoName(t *testing.T) {
- inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandAlreadyPodman(t *testing.T) {
- inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
func TestGenerateRunEnvironment(t *testing.T) {
opts := make(map[string]string)
opts["opt1"] = "one"
diff --git a/cmd/podmanV2/containers/exists.go b/cmd/podmanV2/containers/exists.go
new file mode 100644
index 000000000..22c798fcd
--- /dev/null
+++ b/cmd/podmanV2/containers/exists.go
@@ -0,0 +1,43 @@
+package containers
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.`
+
+ existsCommand = &cobra.Command{
+ Use: "exists CONTAINER",
+ Short: "Check if a container exists in local storage",
+ Long: containerExistsDescription,
+ Example: `podman container exists containerID
+ podman container exists myctr || podman run --name myctr [etc...]`,
+ RunE: exists,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCommand,
+ Parent: containerCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0])
+ if err != nil {
+ return err
+ }
+ if !response.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go
new file mode 100644
index 000000000..27acb3348
--- /dev/null
+++ b/cmd/podmanV2/containers/wait.go
@@ -0,0 +1,82 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ waitDescription = `Block until one or more containers stop and then print their exit codes.
+`
+ waitCommand = &cobra.Command{
+ Use: "wait [flags] CONTAINER [CONTAINER...]",
+ Short: "Block on one or more containers",
+ Long: waitDescription,
+ RunE: wait,
+ Example: `podman wait --latest
+ podman wait --interval 5000 ctrID
+ podman wait ctrID1 ctrID2`,
+ }
+)
+
+var (
+ waitFlags = entities.WaitOptions{}
+ waitCondition string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: waitCommand,
+ Parent: containerCmd,
+ })
+
+ flags := waitCommand.Flags()
+ flags.DurationVarP(&waitFlags.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion")
+ flags.BoolVarP(&waitFlags.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on")
+ if registry.EngineOpts.EngineMode == entities.ABIMode {
+ // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func wait(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ )
+ if waitFlags.Latest && len(args) > 0 {
+ return errors.New("cannot combine latest flag and arguments")
+ }
+ if waitFlags.Interval == 0 {
+ return errors.New("interval must be greater then 0")
+ }
+
+ waitFlags.Condition, err = define.StringToContainerStatus(waitCondition)
+ if err != nil {
+ return err
+ }
+
+ responses, err := registry.ContainerEngine().ContainerWait(context.Background(), args, waitFlags)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Error == nil {
+ fmt.Println(r.Id)
+ }
+ }
+ for _, r := range responses {
+ if r.Error != nil {
+ fmt.Println(err)
+ }
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
index 9c44cea35..2ecbbb201 100644
--- a/cmd/podmanV2/images/inspect.go
+++ b/cmd/podmanV2/images/inspect.go
@@ -52,7 +52,7 @@ func init() {
flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size")
flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template")
- if registry.GlobalFlags.EngineMode == entities.ABIMode {
+ if registry.EngineOpts.EngineMode == entities.ABIMode {
// TODO: This is the same as V1. We could skip creating the flag altogether in V2...
_ = flags.MarkHidden("latest")
}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index 0df086352..24f21d804 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -5,6 +5,7 @@ import (
"os"
"reflect"
"runtime"
+ "strings"
_ "github.com/containers/libpod/cmd/podmanV2/containers"
_ "github.com/containers/libpod/cmd/podmanV2/images"
@@ -31,17 +32,19 @@ func initCobra() {
case "darwin":
fallthrough
case "windows":
- registry.GlobalFlags.EngineMode = entities.TunnelMode
+ registry.EngineOpts.EngineMode = entities.TunnelMode
case "linux":
- registry.GlobalFlags.EngineMode = entities.ABIMode
+ registry.EngineOpts.EngineMode = entities.ABIMode
default:
logrus.Errorf("%s is not a supported OS", runtime.GOOS)
os.Exit(1)
}
// TODO: Is there a Cobra way to "peek" at os.Args?
- if ok := Contains("--remote", os.Args); ok {
- registry.GlobalFlags.EngineMode = entities.TunnelMode
+ for _, v := range os.Args {
+ if strings.HasPrefix(v, "--remote") {
+ registry.EngineOpts.EngineMode = entities.TunnelMode
+ }
}
cobra.OnInitialize(func() {})
@@ -50,7 +53,7 @@ func initCobra() {
func main() {
fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
for _, c := range registry.Commands {
- if Contains(registry.GlobalFlags.EngineMode, c.Mode) {
+ if Contains(registry.EngineOpts.EngineMode, c.Mode) {
parent := rootCmd
if c.Parent != nil {
parent = c.Parent
diff --git a/cmd/podmanV2/parse/parse.go b/cmd/podmanV2/parse/parse.go
new file mode 100644
index 000000000..03cda268c
--- /dev/null
+++ b/cmd/podmanV2/parse/parse.go
@@ -0,0 +1,188 @@
+//nolint
+// most of these validate and parse functions have been taken from projectatomic/docker
+// and modified for cri-o
+package parse
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "regexp"
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+const (
+ Protocol_TCP Protocol = 0
+ Protocol_UDP Protocol = 1
+)
+
+type Protocol int32
+
+// PortMapping specifies the port mapping configurations of a sandbox.
+type PortMapping struct {
+ // Protocol of the port mapping.
+ Protocol Protocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=runtime.Protocol" json:"protocol,omitempty"`
+ // Port number within the container. Default: 0 (not specified).
+ ContainerPort int32 `protobuf:"varint,2,opt,name=container_port,json=containerPort,proto3" json:"container_port,omitempty"`
+ // Port number on the host. Default: 0 (not specified).
+ HostPort int32 `protobuf:"varint,3,opt,name=host_port,json=hostPort,proto3" json:"host_port,omitempty"`
+ // Host IP.
+ HostIp string `protobuf:"bytes,4,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
+}
+
+// Note: for flags that are in the form <number><unit>, use the RAMInBytes function
+// from the units package in docker/go-units/size.go
+
+var (
+ whiteSpaces = " \t"
+ alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
+ domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
+)
+
+// validateExtraHost validates that the specified string is a valid extrahost and returns it.
+// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
+// for add-host flag
+func ValidateExtraHost(val string) (string, error) { //nolint
+ // allow for IPv6 addresses in extra hosts by only splitting on first ":"
+ arr := strings.SplitN(val, ":", 2)
+ if len(arr) != 2 || len(arr[0]) == 0 {
+ return "", fmt.Errorf("bad format for add-host: %q", val)
+ }
+ if _, err := validateIPAddress(arr[1]); err != nil {
+ return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
+ }
+ return val, nil
+}
+
+// validateIPAddress validates an Ip address.
+// for dns, ip, and ip6 flags also
+func validateIPAddress(val string) (string, error) {
+ var ip = net.ParseIP(strings.TrimSpace(val))
+ if ip != nil {
+ return ip.String(), nil
+ }
+ return "", fmt.Errorf("%s is not an ip address", val)
+}
+
+func ValidateDomain(val string) (string, error) {
+ if alphaRegexp.FindString(val) == "" {
+ return "", fmt.Errorf("%s is not a valid domain", val)
+ }
+ ns := domainRegexp.FindSubmatch([]byte(val))
+ if len(ns) > 0 && len(ns[1]) < 255 {
+ return string(ns[1]), nil
+ }
+ return "", fmt.Errorf("%s is not a valid domain", val)
+}
+
+// GetAllLabels retrieves all labels given a potential label file and a number
+// of labels provided from the command line.
+func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
+ labels := make(map[string]string)
+ for _, file := range labelFile {
+ // Use of parseEnvFile still seems safe, as it's missing the
+ // extra parsing logic of parseEnv.
+ // There's an argument that we SHOULD be doing that parsing for
+ // all environment variables, even those sourced from files, but
+ // that would require a substantial rework.
+ if err := parseEnvFile(labels, file); err != nil {
+ // FIXME: parseEnvFile is using parseEnv, so we need to add extra
+ // logic for labels.
+ return nil, err
+ }
+ }
+ for _, label := range inputLabels {
+ split := strings.SplitN(label, "=", 2)
+ if split[0] == "" {
+ return nil, errors.Errorf("invalid label format: %q", label)
+ }
+ value := ""
+ if len(split) > 1 {
+ value = split[1]
+ }
+ labels[split[0]] = value
+ }
+ return labels, nil
+}
+
+func parseEnv(env map[string]string, line string) error {
+ data := strings.SplitN(line, "=", 2)
+
+ // catch invalid variables such as "=" or "=A"
+ if data[0] == "" {
+ return errors.Errorf("invalid environment variable: %q", line)
+ }
+
+ // trim the front of a variable, but nothing else
+ name := strings.TrimLeft(data[0], whiteSpaces)
+ if strings.ContainsAny(name, whiteSpaces) {
+ return errors.Errorf("name %q has white spaces, poorly formatted name", name)
+ }
+
+ if len(data) > 1 {
+ env[name] = data[1]
+ } else {
+ if strings.HasSuffix(name, "*") {
+ name = strings.TrimSuffix(name, "*")
+ for _, e := range os.Environ() {
+ part := strings.SplitN(e, "=", 2)
+ if len(part) < 2 {
+ continue
+ }
+ if strings.HasPrefix(part[0], name) {
+ env[part[0]] = part[1]
+ }
+ }
+ } else {
+ // if only a pass-through variable is given, clean it up.
+ if val, ok := os.LookupEnv(name); ok {
+ env[name] = val
+ }
+ }
+ }
+ return nil
+}
+
+// parseEnvFile reads a file with environment variables enumerated by lines
+func parseEnvFile(env map[string]string, filename string) error {
+ fh, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer fh.Close()
+
+ scanner := bufio.NewScanner(fh)
+ for scanner.Scan() {
+ // trim the line from all leading whitespace first
+ line := strings.TrimLeft(scanner.Text(), whiteSpaces)
+ // line is not empty, and not starting with '#'
+ if len(line) > 0 && !strings.HasPrefix(line, "#") {
+ if err := parseEnv(env, line); err != nil {
+ return err
+ }
+ }
+ }
+ return scanner.Err()
+}
+
+// ValidateFileName returns an error if filename contains ":"
+// as it is currently not supported
+func ValidateFileName(filename string) error {
+ if strings.Contains(filename, ":") {
+ return errors.Errorf("invalid filename (should not contain ':') %q", filename)
+ }
+ return nil
+}
+
+// ValidURL checks a string urlStr is a url or not
+func ValidURL(urlStr string) error {
+ _, err := url.ParseRequestURI(urlStr)
+ if err != nil {
+ return errors.Wrapf(err, "invalid url path: %q", urlStr)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/parse/parse_test.go b/cmd/podmanV2/parse/parse_test.go
new file mode 100644
index 000000000..a6ddc2be9
--- /dev/null
+++ b/cmd/podmanV2/parse/parse_test.go
@@ -0,0 +1,152 @@
+//nolint
+// most of these validate and parse functions have been taken from projectatomic/docker
+// and modified for cri-o
+package parse
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var (
+ Var1 = []string{"ONE=1", "TWO=2"}
+)
+
+func createTmpFile(content []byte) (string, error) {
+ tmpfile, err := ioutil.TempFile(os.TempDir(), "unittest")
+ if err != nil {
+ return "", err
+ }
+
+ if _, err := tmpfile.Write(content); err != nil {
+ return "", err
+
+ }
+ if err := tmpfile.Close(); err != nil {
+ return "", err
+ }
+ return tmpfile.Name(), nil
+}
+
+func TestValidateExtraHost(t *testing.T) {
+ type args struct {
+ val string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ //2001:0db8:85a3:0000:0000:8a2e:0370:7334
+ {name: "good-ipv4", args: args{val: "foobar:192.168.1.1"}, want: "foobar:192.168.1.1", wantErr: false},
+ {name: "bad-ipv4", args: args{val: "foobar:999.999.999.99"}, want: "", wantErr: true},
+ {name: "bad-ipv4", args: args{val: "foobar:999.999.999"}, want: "", wantErr: true},
+ {name: "noname-ipv4", args: args{val: "192.168.1.1"}, want: "", wantErr: true},
+ {name: "noname-ipv4", args: args{val: ":192.168.1.1"}, want: "", wantErr: true},
+ {name: "noip", args: args{val: "foobar:"}, want: "", wantErr: true},
+ {name: "noip", args: args{val: "foobar"}, want: "", wantErr: true},
+ {name: "good-ipv6", args: args{val: "foobar:2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "foobar:2001:0db8:85a3:0000:0000:8a2e:0370:7334", wantErr: false},
+ {name: "bad-ipv6", args: args{val: "foobar:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ {name: "bad-ipv6", args: args{val: "foobar:0db8:85a3:0000:0000:8a2e:0370:7334.0000.0000.000"}, want: "", wantErr: true},
+ {name: "noname-ipv6", args: args{val: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ {name: "noname-ipv6", args: args{val: ":2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ValidateExtraHost(tt.args.val)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ValidateExtraHost() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("ValidateExtraHost() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_validateIPAddress(t *testing.T) {
+ type args struct {
+ val string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {name: "ipv4-good", args: args{val: "192.168.1.1"}, want: "192.168.1.1", wantErr: false},
+ {name: "ipv4-bad", args: args{val: "192.168.1.1.1"}, want: "", wantErr: true},
+ {name: "ipv4-bad", args: args{val: "192."}, want: "", wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := validateIPAddress(tt.args.val)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("validateIPAddress() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("validateIPAddress() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestValidateFileName(t *testing.T) {
+ type args struct {
+ filename string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {name: "good", args: args{filename: "/some/rand/path"}, wantErr: false},
+ {name: "good", args: args{filename: "some/rand/path"}, wantErr: false},
+ {name: "good", args: args{filename: "/"}, wantErr: false},
+ {name: "bad", args: args{filename: "/:"}, wantErr: true},
+ {name: "bad", args: args{filename: ":/"}, wantErr: true},
+ {name: "bad", args: args{filename: "/some/rand:/path"}, wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := ValidateFileName(tt.args.filename); (err != nil) != tt.wantErr {
+ t.Errorf("ValidateFileName() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestGetAllLabels(t *testing.T) {
+ fileLabels := []string{}
+ labels, _ := GetAllLabels(fileLabels, Var1)
+ assert.Equal(t, len(labels), 2)
+}
+
+func TestGetAllLabelsBadKeyValue(t *testing.T) {
+ inLabels := []string{"=badValue", "="}
+ fileLabels := []string{}
+ _, err := GetAllLabels(fileLabels, inLabels)
+ assert.Error(t, err, assert.AnError)
+}
+
+func TestGetAllLabelsBadLabelFile(t *testing.T) {
+ fileLabels := []string{"/foobar5001/be"}
+ _, err := GetAllLabels(fileLabels, Var1)
+ assert.Error(t, err, assert.AnError)
+}
+
+func TestGetAllLabelsFile(t *testing.T) {
+ content := []byte("THREE=3")
+ tFile, err := createTmpFile(content)
+ defer os.Remove(tFile)
+ assert.NoError(t, err)
+ fileLabels := []string{tFile}
+ result, _ := GetAllLabels(fileLabels, Var1)
+ assert.Equal(t, len(result), 3)
+}
diff --git a/cmd/podmanV2/pods/exists.go b/cmd/podmanV2/pods/exists.go
new file mode 100644
index 000000000..e37f2ebd7
--- /dev/null
+++ b/cmd/podmanV2/pods/exists.go
@@ -0,0 +1,43 @@
+package pods
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ podExistsDescription = `If the named pod exists in local storage, podman pod exists exits with 0, otherwise the exit code will be 1.`
+
+ existsCommand = &cobra.Command{
+ Use: "exists POD",
+ Short: "Check if a pod exists in local storage",
+ Long: podExistsDescription,
+ RunE: exists,
+ Args: cobra.ExactArgs(1),
+ Example: `podman pod exists podID
+ podman pod exists mypod || podman pod create --name mypod`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCommand,
+ Parent: podCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().PodExists(context.Background(), args[0])
+ if err != nil {
+ return err
+ }
+ if !response.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index fa51d6535..793d520a8 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -14,11 +14,13 @@ type CliCommand struct {
}
var (
- Commands []CliCommand
- GlobalFlags entities.EngineFlags
+ Commands []CliCommand
+
imageEngine entities.ImageEngine
containerEngine entities.ContainerEngine
- PodmanTunnel bool
+
+ EngineOpts entities.EngineOptions
+ GlobalFlags entities.EngineFlags
)
// HelpTemplate returns the help template for podman commands
@@ -63,7 +65,8 @@ func ImageEngine() entities.ImageEngine {
// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
if imageEngine == nil {
- engine, err := infra.NewImageEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
+ EngineOpts.FlagSet = cmd.Flags()
+ engine, err := infra.NewImageEngine(EngineOpts)
if err != nil {
return nil, err
}
@@ -79,7 +82,8 @@ func ContainerEngine() entities.ContainerEngine {
// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
if containerEngine == nil {
- engine, err := infra.NewContainerEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
+ EngineOpts.FlagSet = cmd.Flags()
+ engine, err := infra.NewContainerEngine(EngineOpts)
if err != nil {
return nil, err
}
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index 778184f28..24b083b9f 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -24,7 +24,8 @@ func init() {
// Override default --help information of `--version` global flag}
var dummyVersion bool
rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman")
- rootCmd.PersistentFlags().BoolVarP(&registry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel")
+ rootCmd.PersistentFlags().StringVarP(&registry.EngineOpts.Uri, "remote", "r", "", "URL to access podman service")
+ rootCmd.PersistentFlags().StringSliceVar(&registry.EngineOpts.Identities, "identity", []string{}, "path to SSH identity file")
}
func Execute() {
diff --git a/cmd/podmanV2/volumes/create.go b/cmd/podmanV2/volumes/create.go
new file mode 100644
index 000000000..91181dd03
--- /dev/null
+++ b/cmd/podmanV2/volumes/create.go
@@ -0,0 +1,72 @@
+package volumes
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ createDescription = `If using the default driver, "local", the volume will be created on the host in the volumes directory under container storage.`
+
+ createCommand = &cobra.Command{
+ Use: "create [flags] [NAME]",
+ Short: "Create a new volume",
+ Long: createDescription,
+ RunE: create,
+ Example: `podman volume create myvol
+ podman volume create
+ podman volume create --label foo=bar myvol`,
+ }
+)
+
+var (
+ createOpts = entities.VolumeCreateOptions{}
+ opts = struct {
+ Label []string
+ Opts []string
+ }{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ Parent: volumeCmd,
+ })
+ flags := createCommand.Flags()
+ flags.StringVar(&createOpts.Driver, "driver", "", "Specify volume driver name (default local)")
+ flags.StringSliceVarP(&opts.Label, "label", "l", []string{}, "Set metadata for a volume (default [])")
+ flags.StringArrayVarP(&opts.Opts, "opt", "o", []string{}, "Set driver specific options (default [])")
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ )
+ if len(args) > 1 {
+ return errors.Errorf("too many arguments, create takes at most 1 argument")
+ }
+ if len(args) > 0 {
+ createOpts.Name = args[0]
+ }
+ createOpts.Label, err = parse.GetAllLabels([]string{}, opts.Label)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+ createOpts.Options, err = parse.GetAllLabels([]string{}, opts.Opts)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process options")
+ }
+ response, err := registry.ContainerEngine().VolumeCreate(context.Background(), createOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.IdOrName)
+ return nil
+}
diff --git a/cmd/podmanV2/volumes/volume.go b/cmd/podmanV2/volumes/volume.go
index 245c06da0..84abe3d24 100644
--- a/cmd/podmanV2/volumes/volume.go
+++ b/cmd/podmanV2/volumes/volume.go
@@ -1,4 +1,4 @@
-package images
+package volumes
import (
"github.com/containers/libpod/cmd/podmanV2/registry"
@@ -8,7 +8,7 @@ import (
var (
// Command: podman _volume_
- cmd = &cobra.Command{
+ volumeCmd = &cobra.Command{
Use: "volume",
Short: "Manage volumes",
Long: "Volumes are created in and can be shared between containers",
@@ -21,10 +21,10 @@ var (
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
- Command: cmd,
+ Command: volumeCmd,
})
- cmd.SetHelpTemplate(registry.HelpTemplate())
- cmd.SetUsageTemplate(registry.UsageTemplate())
+ volumeCmd.SetHelpTemplate(registry.HelpTemplate())
+ volumeCmd.SetUsageTemplate(registry.UsageTemplate())
}
func preRunE(cmd *cobra.Command, args []string) error {
diff --git a/libpod/container_top_unsupported.go b/libpod/container_top_unsupported.go
index 382c98b54..12f6cbb6c 100644
--- a/libpod/container_top_unsupported.go
+++ b/libpod/container_top_unsupported.go
@@ -4,6 +4,12 @@ package libpod
import "github.com/containers/libpod/libpod/define"
+// Top gathers statistics about the running processes in a container. It returns a
+// []string for output
+func (c *Container) Top(descriptors []string) ([]string, error) {
+ return nil, define.ErrNotImplemented
+}
+
// GetContainerPidInformation returns process-related data of all processes in
// the container. The output data can be controlled via the `descriptors`
// argument which expects format descriptors and supports all AIXformat
diff --git a/libpod/lock/shm/shm_lock_test.go b/libpod/lock/shm/shm_lock_test.go
index 830035881..41a150c59 100644
--- a/libpod/lock/shm/shm_lock_test.go
+++ b/libpod/lock/shm/shm_lock_test.go
@@ -1,3 +1,5 @@
+// +build linux
+
package shm
import (
diff --git a/libpod/util_unsupported.go b/libpod/util_unsupported.go
index 9a9a6eeb6..4c5616bd0 100644
--- a/libpod/util_unsupported.go
+++ b/libpod/util_unsupported.go
@@ -25,7 +25,7 @@ func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) {
// LabelVolumePath takes a mount path for a volume and gives it an
// selinux label of either shared or not
-func LabelVolumePath(path string, shared bool) error {
+func LabelVolumePath(path string) error {
return define.ErrNotImplemented
}
diff --git a/pkg/adapter/terminal_unsupported.go b/pkg/adapter/terminal_unsupported.go
new file mode 100644
index 000000000..3009f0a38
--- /dev/null
+++ b/pkg/adapter/terminal_unsupported.go
@@ -0,0 +1,23 @@
+// +build !linux
+
+package adapter
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+)
+
+// ExecAttachCtr execs and attaches to a container
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
+ return -1, define.ErrNotImplemented
+}
+
+// StartAttachCtr starts and (if required) attaches to a container
+// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
+// error. we may need to just lint disable this one.
+func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
+ return define.ErrNotImplemented
+}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 8020c391d..cdc34004f 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -21,8 +21,12 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
name := utils.GetName(r)
_, err := runtime.LookupContainer(name)
if err != nil {
- utils.ContainerNotFound(w, name, err)
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ }
+ utils.InternalServerError(w, err)
return
+
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 9b10ee890..06ca1d225 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -8,8 +8,8 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"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/gorilla/schema"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -25,7 +25,7 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}{
// override any golang type defaults
}
- input := handlers.VolumeCreateConfig{}
+ input := entities.VolumeCreateOptions{}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
@@ -46,8 +46,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
if len(input.Label) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
}
- if len(input.Opts) > 0 {
- parsedOptions, err := shared.ParseVolumeOptions(input.Opts)
+ if len(input.Options) > 0 {
+ parsedOptions, err := shared.ParseVolumeOptions(input.Options)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -64,7 +64,17 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, config)
+ volResponse := entities.VolumeConfigResponse{
+ Name: config.Name,
+ Labels: config.Labels,
+ Driver: config.Driver,
+ MountPoint: config.MountPoint,
+ CreatedTime: config.CreatedTime,
+ Options: config.Options,
+ UID: config.UID,
+ GID: config.GID,
+ }
+ utils.WriteResponse(w, http.StatusOK, volResponse)
}
func InspectVolume(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index ce4a9957b..c6b70251b 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -128,18 +128,6 @@ type CreateContainerConfig struct {
NetworkingConfig dockerNetwork.NetworkingConfig
}
-// swagger:model VolumeCreate
-type VolumeCreateConfig struct {
- // New volume's name. Can be left blank
- Name string `schema:"name"`
- // Volume driver to use
- Driver string `schema:"driver"`
- // User-defined key/value metadata.
- Label map[string]string `schema:"label"`
- // Mapping of driver options and values.
- Opts map[string]string `schema:"opts"`
-}
-
// swagger:model IDResponse
type IDResponse struct {
// ID
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index d2cf7503e..2e1a269f2 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -4,6 +4,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
)
// No such image
@@ -155,7 +156,7 @@ type ok struct {
type swagVolumeCreateResponse struct {
// in:body
Body struct {
- libpod.VolumeConfig
+ entities.VolumeConfigResponse
}
}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 289debd8c..4fe4dd72d 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -165,8 +165,13 @@ func sshClient(_url *url.URL, identity string, secure bool) (*http.Client, error
}
}
+ port := _url.Port()
+ if port == "" {
+ port = "22"
+ }
+
bastion, err := ssh.Dial("tcp",
- net.JoinHostPort(_url.Hostname(), _url.Port()),
+ net.JoinHostPort(_url.Hostname(), port),
&ssh.ClientConfig{
User: _url.User.Username(),
Auth: []ssh.AuthMethod{auth},
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index f298dbba1..534555a00 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -7,6 +7,7 @@ import (
"strconv"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
)
@@ -212,7 +213,7 @@ func Unpause(ctx context.Context, nameOrID string) error {
// Wait blocks until the given container reaches a condition. If not provided, the condition will
// default to stopped. If the condition is stopped, an exit code for the container will be provided. The
// nameOrID can be a container name or a partial/full ID.
-func Wait(ctx context.Context, nameOrID string, condition *string) (int32, error) {
+func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatus) (int32, error) { //nolint
var exitCode int32
conn, err := bindings.GetClient(ctx)
if err != nil {
@@ -220,7 +221,7 @@ func Wait(ctx context.Context, nameOrID string, condition *string) (int32, error
}
params := url.Values{}
if condition != nil {
- params.Set("condition", *condition)
+ params.Set("condition", condition.String())
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nameOrID)
if err != nil {
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index 409e620a3..6b8d6788c 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -3,6 +3,7 @@ package test_bindings
import (
"context"
"fmt"
+ "github.com/containers/libpod/libpod/define"
"io/ioutil"
"os"
"os/exec"
@@ -205,8 +206,8 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po
if err != nil {
return "", err
}
- waiting := "running"
- _, err = containers.Wait(b.conn, ctr.ID, &waiting)
+ wait := define.ContainerStateRunning
+ _, err = containers.Wait(b.conn, ctr.ID, &wait)
return ctr.ID, err
}
diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index afb0cc19b..f5465c803 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -1,6 +1,7 @@
package test_bindings
import (
+ "github.com/containers/libpod/libpod/define"
"net/http"
"strconv"
"strings"
@@ -282,8 +283,8 @@ var _ = Describe("Podman containers ", func() {
var (
name = "top"
exitCode int32 = -1
- pause = "paused"
- unpause = "running"
+ pause = define.ContainerStatePaused
+ running = define.ContainerStateRunning
)
errChan := make(chan error)
_, err := bt.RunTopContainer(&name, nil, nil)
@@ -301,7 +302,7 @@ var _ = Describe("Podman containers ", func() {
errChan = make(chan error)
go func() {
- _, waitErr := containers.Wait(bt.conn, name, &unpause)
+ _, waitErr := containers.Wait(bt.conn, name, &running)
errChan <- waitErr
close(errChan)
}()
diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go
index 1d5ae1329..9da034d24 100644
--- a/pkg/bindings/test/volumes_test.go
+++ b/pkg/bindings/test/volumes_test.go
@@ -6,11 +6,10 @@ import (
"net/http"
"time"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/bindings/volumes"
-
- "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -53,13 +52,13 @@ var _ = Describe("Podman volumes", func() {
It("create volume", func() {
// create a volume with blank config should work
- _, err := volumes.Create(connText, handlers.VolumeCreateConfig{})
+ _, err := volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
- vcc := handlers.VolumeCreateConfig{
- Name: "foobar",
- Label: nil,
- Opts: nil,
+ vcc := entities.VolumeCreateOptions{
+ Name: "foobar",
+ Label: nil,
+ Options: nil,
}
vol, err := volumes.Create(connText, vcc)
Expect(err).To(BeNil())
@@ -73,7 +72,7 @@ var _ = Describe("Podman volumes", func() {
})
It("inspect volume", func() {
- vol, err := volumes.Create(connText, handlers.VolumeCreateConfig{})
+ vol, err := volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
data, err := volumes.Inspect(connText, vol.Name)
Expect(err).To(BeNil())
@@ -87,13 +86,13 @@ var _ = Describe("Podman volumes", func() {
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Removing an unused volume should work
- vol, err := volumes.Create(connText, handlers.VolumeCreateConfig{})
+ vol, err := volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
err = volumes.Remove(connText, vol.Name, nil)
Expect(err).To(BeNil())
// Removing a volume that is being used without force should be 409
- vol, err = volumes.Create(connText, handlers.VolumeCreateConfig{})
+ vol, err = volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/foobar", vol.Name), "--name", "vtest", alpine.name, "top"})
session.Wait(45)
@@ -119,7 +118,7 @@ var _ = Describe("Podman volumes", func() {
// create a bunch of named volumes and make verify with list
volNames := []string{"homer", "bart", "lisa", "maggie", "marge"}
for i := 0; i < 5; i++ {
- _, err = volumes.Create(connText, handlers.VolumeCreateConfig{Name: volNames[i]})
+ _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: volNames[i]})
Expect(err).To(BeNil())
}
vols, err = volumes.List(connText, nil)
@@ -152,15 +151,15 @@ var _ = Describe("Podman volumes", func() {
Expect(err).To(BeNil())
// Removing an unused volume should work
- _, err = volumes.Create(connText, handlers.VolumeCreateConfig{})
+ _, err = volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
vols, err := volumes.Prune(connText)
Expect(err).To(BeNil())
Expect(len(vols)).To(BeNumerically("==", 1))
- _, err = volumes.Create(connText, handlers.VolumeCreateConfig{Name: "homer"})
+ _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: "homer"})
Expect(err).To(BeNil())
- _, err = volumes.Create(connText, handlers.VolumeCreateConfig{})
+ _, err = volumes.Create(connText, entities.VolumeCreateOptions{})
Expect(err).To(BeNil())
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/homer", "homer"), "--name", "vtest", alpine.name, "top"})
session.Wait(45)
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
index 0bc818605..a2164e0af 100644
--- a/pkg/bindings/volumes/volumes.go
+++ b/pkg/bindings/volumes/volumes.go
@@ -8,15 +8,15 @@ import (
"strings"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
jsoniter "github.com/json-iterator/go"
)
// Create creates a volume given its configuration.
-func Create(ctx context.Context, config handlers.VolumeCreateConfig) (*libpod.VolumeConfig, error) {
+func Create(ctx context.Context, config entities.VolumeCreateOptions) (*entities.VolumeConfigResponse, error) {
var (
- v libpod.VolumeConfig
+ v entities.VolumeConfigResponse
)
conn, err := bindings.GetClient(ctx)
if err != nil {
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 1899b98f7..0e1208b3b 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -1 +1,23 @@
package entities
+
+import (
+ "time"
+
+ "github.com/containers/libpod/libpod/define"
+)
+
+type WaitOptions struct {
+ Condition define.ContainerStatus
+ Interval time.Duration
+ Latest bool
+}
+
+type WaitReport struct {
+ Id string
+ Error error
+ ExitCode int32
+}
+
+type BoolReport struct {
+ Value bool
+}
diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go
index a1096f1f1..08ef1df92 100644
--- a/pkg/domain/entities/engine.go
+++ b/pkg/domain/entities/engine.go
@@ -1,7 +1,6 @@
package entities
import (
- "net/url"
"os/user"
"path/filepath"
@@ -20,11 +19,13 @@ func (m EngineMode) String() string {
return string(m)
}
+// FIXME: merge EngineOptions and EngineFlags
type EngineOptions struct {
- Uri *url.URL
+ Uri string
Identities []string
- FlagSet pflag.FlagSet
+ FlagSet *pflag.FlagSet
Flags EngineFlags
+ EngineMode EngineMode
}
type EngineFlags struct {
@@ -58,8 +59,6 @@ type EngineFlags struct {
Port int
IdentityFile string
IgnoreHosts bool
-
- EngineMode EngineMode
}
func NewEngineOptions() (EngineFlags, error) {
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index d08f37d44..5820c12c3 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -5,22 +5,14 @@ import (
)
type ContainerEngine interface {
- ContainerRuntime
- PodRuntime
- VolumeRuntime
-}
-
-type ContainerRuntime interface {
ContainerDelete(ctx context.Context, opts ContainerDeleteOptions) (*ContainerDeleteReport, error)
ContainerPrune(ctx context.Context) (*ContainerPruneReport, error)
-}
-
-type PodRuntime interface {
+ ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
+ ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error)
+ PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
PodPrune(ctx context.Context) (*PodPruneReport, error)
-}
-
-type VolumeRuntime interface {
+ VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
VolumeDelete(ctx context.Context, opts VolumeDeleteOptions) (*VolumeDeleteReport, error)
VolumePrune(ctx context.Context) (*VolumePruneReport, error)
}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
new file mode 100644
index 000000000..ad12d0d01
--- /dev/null
+++ b/pkg/domain/entities/volumes.go
@@ -0,0 +1,41 @@
+package entities
+
+import "time"
+
+// swagger:model VolumeCreate
+type VolumeCreateOptions struct {
+ // New volume's name. Can be left blank
+ Name string `schema:"name"`
+ // Volume driver to use
+ Driver string `schema:"driver"`
+ // User-defined key/value metadata.
+ Label map[string]string `schema:"label"`
+ // Mapping of driver options and values.
+ Options map[string]string `schema:"opts"`
+}
+
+type IdOrNameResponse struct {
+ // The Id or Name of an object
+ IdOrName string
+}
+
+type VolumeConfigResponse struct {
+ // Name of the volume.
+ Name string `json:"name"`
+ Labels map[string]string `json:"labels"`
+ // The volume driver. Empty string or local does not activate a volume
+ // driver, all other volumes will.
+ Driver string `json:"volumeDriver"`
+ // The location the volume is mounted at.
+ MountPoint string `json:"mountPoint"`
+ // Time the volume was created.
+ CreatedTime time.Time `json:"createdAt,omitempty"`
+ // Options to pass to the volume driver. For the local driver, this is
+ // a list of mount options. For other drivers, they are passed to the
+ // volume driver handling the volume.
+ Options map[string]string `json:"volumeOptions,omitempty"`
+ // UID the volume will be created as.
+ UID int `json:"uid"`
+ // GID the volume will be created as.
+ GID int `json:"gid"`
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
new file mode 100644
index 000000000..cdcd77246
--- /dev/null
+++ b/pkg/domain/infra/abi/containers.go
@@ -0,0 +1,66 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/adapter/shortcuts"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+)
+
+// TODO: Should return *entities.ContainerExistsReport, error
+func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
+ _, err := ic.Libpod.LookupContainer(nameOrId)
+ if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
+ return nil, err
+ }
+ return &entities.BoolReport{Value: err == nil}, nil
+}
+
+func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) {
+ var (
+ responses []entities.WaitReport
+ )
+ ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range ctrs {
+ response := entities.WaitReport{Id: c.ID()}
+ exitCode, err := c.WaitForConditionWithInterval(options.Interval, options.Condition)
+ if err != nil {
+ response.Error = err
+ } else {
+ response.ExitCode = exitCode
+ }
+ responses = append(responses, response)
+ }
+ return responses, nil
+}
+
+func (ic *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
+ panic("implement me")
+}
+
+func (ic *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
+ panic("implement me")
+}
+
+func (ic *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
+ panic("implement me")
+}
+
+func (ic *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
+ panic("implement me")
+}
+
+func (ic *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
+ panic("implement me")
+}
+
+func (ic *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
+ panic("implement me")
+}
diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go
new file mode 100644
index 000000000..6c0e1ee55
--- /dev/null
+++ b/pkg/domain/infra/abi/parse/parse.go
@@ -0,0 +1,68 @@
+package parse
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// Handle volume options from CLI.
+// Parse "o" option to find UID, GID.
+func ParseVolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) {
+ libpodOptions := []libpod.VolumeCreateOption{}
+ volumeOptions := make(map[string]string)
+
+ for key, value := range opts {
+ switch key {
+ case "o":
+ // o has special handling to parse out UID, GID.
+ // These are separate Libpod options.
+ splitVal := strings.Split(value, ",")
+ finalVal := []string{}
+ for _, o := range splitVal {
+ // Options will be formatted as either "opt" or
+ // "opt=value"
+ splitO := strings.SplitN(o, "=", 2)
+ switch strings.ToLower(splitO[0]) {
+ case "uid":
+ if len(splitO) != 2 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "uid option must provide a UID")
+ }
+ intUID, err := strconv.Atoi(splitO[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert UID %s to integer", splitO[1])
+ }
+ logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID)
+ libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID))
+ case "gid":
+ if len(splitO) != 2 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID")
+ }
+ intGID, err := strconv.Atoi(splitO[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert GID %s to integer", splitO[1])
+ }
+ logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID)
+ libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID))
+ default:
+ finalVal = append(finalVal, o)
+ }
+ }
+ if len(finalVal) > 0 {
+ volumeOptions[key] = strings.Join(finalVal, ",")
+ }
+ default:
+ volumeOptions[key] = value
+ }
+ }
+
+ if len(volumeOptions) > 0 {
+ libpodOptions = append(libpodOptions, libpod.WithVolumeOptions(volumeOptions))
+ }
+
+ return libpodOptions, nil
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
new file mode 100644
index 000000000..de22de68e
--- /dev/null
+++ b/pkg/domain/infra/abi/pods.go
@@ -0,0 +1,19 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+ "github.com/pkg/errors"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+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 {
+ return nil, err
+ }
+ return &entities.BoolReport{Value: err == nil}, nil
+}
diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go
index 479a69586..b53fb6d3a 100644
--- a/pkg/domain/infra/abi/runtime.go
+++ b/pkg/domain/infra/abi/runtime.go
@@ -4,7 +4,6 @@ package abi
import (
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/domain/entities"
)
// Image-related runtime linked against libpod library
@@ -14,6 +13,5 @@ type ImageEngine struct {
// Container-related runtime linked against libpod library
type ContainerEngine struct {
- entities.ContainerEngine
Libpod *libpod.Runtime
}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
new file mode 100644
index 000000000..0783af441
--- /dev/null
+++ b/pkg/domain/infra/abi/volumes.go
@@ -0,0 +1,38 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi/parse"
+)
+
+func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IdOrNameResponse, error) {
+ var (
+ volumeOptions []libpod.VolumeCreateOption
+ )
+ if len(opts.Name) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeName(opts.Name))
+ }
+ if len(opts.Driver) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(opts.Driver))
+ }
+ if len(opts.Label) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(opts.Label))
+ }
+ if len(opts.Options) > 0 {
+ parsedOptions, err := parse.ParseVolumeOptions(opts.Options)
+ if err != nil {
+ return nil, err
+ }
+ volumeOptions = append(volumeOptions, parsedOptions...)
+ }
+ vol, err := ic.Libpod.NewVolume(ctx, volumeOptions...)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.IdOrNameResponse{IdOrName: vol.Name()}, nil
+}
diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go
index de996f567..31f832423 100644
--- a/pkg/domain/infra/runtime_abi.go
+++ b/pkg/domain/infra/runtime_abi.go
@@ -8,32 +8,31 @@ import (
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/domain/infra/abi"
"github.com/containers/libpod/pkg/domain/infra/tunnel"
)
// NewContainerEngine factory provides a libpod runtime for container-related operations
-func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
- switch mode {
+func NewContainerEngine(opts entities.EngineOptions) (entities.ContainerEngine, error) {
+ switch opts.EngineMode {
case entities.ABIMode:
r, err := NewLibpodRuntime(opts.FlagSet, opts.Flags)
- return &abi.ContainerEngine{ContainerEngine: r}, err
+ return r, err
case entities.TunnelMode:
- ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...)
return &tunnel.ContainerEngine{ClientCxt: ctx}, err
}
- return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode)
}
// NewContainerEngine factory provides a libpod runtime for image-related operations
-func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
- switch mode {
+func NewImageEngine(opts entities.EngineOptions) (entities.ImageEngine, error) {
+ switch opts.EngineMode {
case entities.ABIMode:
r, err := NewLibpodImageRuntime(opts.FlagSet, opts.Flags)
return r, err
case entities.TunnelMode:
- ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...)
return &tunnel.ImageEngine{ClientCxt: ctx}, err
}
- return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode)
}
diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go
index 480e7c8d7..d2e66c08c 100644
--- a/pkg/domain/infra/runtime_image_proxy.go
+++ b/pkg/domain/infra/runtime_image_proxy.go
@@ -12,14 +12,10 @@ import (
// ContainerEngine Image Proxy will be EOL'ed after podmanV2 is separated from libpod repo
-func NewLibpodImageRuntime(flags pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) {
+func NewLibpodImageRuntime(flags *pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) {
r, err := GetRuntime(context.Background(), flags, opts)
if err != nil {
return nil, err
}
return &abi.ImageEngine{Libpod: r}, nil
}
-
-func (ir *runtime) ShutdownImageRuntime(force bool) error {
- return ir.Libpod.Shutdown(force)
-}
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index b42c52b6e..b835152bf 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -26,7 +26,7 @@ type engineOpts struct {
}
// GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers
-func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) {
+func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) {
return getRuntime(ctx, fs, &engineOpts{
name: newRuntime,
renumber: false,
@@ -38,7 +38,7 @@ func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineF
}
// GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify
-func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
return getRuntime(ctx, fs, &engineOpts{
renumber: false,
migrate: false,
@@ -49,7 +49,7 @@ func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.Engi
}
// GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber
-func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
return getRuntime(ctx, fs, &engineOpts{
renumber: true,
migrate: false,
@@ -60,7 +60,7 @@ func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.Engine
}
// GetRuntime generates a new libpod runtime configured by command line options
-func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+func GetRuntime(ctx context.Context, flags *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
return getRuntime(ctx, flags, &engineOpts{
renumber: false,
migrate: false,
@@ -71,7 +71,7 @@ func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags
}
// GetRuntimeNoStore generates a new libpod runtime configured by command line options
-func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
+func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
return getRuntime(ctx, fs, &engineOpts{
renumber: false,
migrate: false,
@@ -81,7 +81,7 @@ func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineF
})
}
-func getRuntime(ctx context.Context, fs flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
+func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
options := []libpod.RuntimeOption{}
storageOpts := storage.StoreOptions{}
storageSet := false
diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go
index d17b8efa1..4095ae6e2 100644
--- a/pkg/domain/infra/runtime_proxy.go
+++ b/pkg/domain/infra/runtime_proxy.go
@@ -5,50 +5,17 @@ package infra
import (
"context"
- "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
flag "github.com/spf13/pflag"
)
// ContainerEngine Proxy will be EOL'ed after podmanV2 is separated from libpod repo
-type runtime struct {
- entities.ContainerEngine
- Libpod *libpod.Runtime
-}
-
-func NewLibpodRuntime(flags flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) {
+func NewLibpodRuntime(flags *flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) {
r, err := GetRuntime(context.Background(), flags, opts)
if err != nil {
return nil, err
}
- return &runtime{Libpod: r}, nil
-}
-
-func (r *runtime) ShutdownRuntime(force bool) error {
- return r.Libpod.Shutdown(force)
-}
-
-func (r *runtime) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
- panic("implement me")
-}
-
-func (r *runtime) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
- panic("implement me")
-}
-
-func (r *runtime) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
- panic("implement me")
-}
-
-func (r *runtime) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
- panic("implement me")
-}
-
-func (r *runtime) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
- panic("implement me")
-}
-
-func (r *runtime) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
- panic("implement me")
+ return &abi.ContainerEngine{Libpod: r}, nil
}
diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go
index 8a606deaf..5816ef0c0 100644
--- a/pkg/domain/infra/runtime_tunnel.go
+++ b/pkg/domain/infra/runtime_tunnel.go
@@ -11,25 +11,25 @@ import (
"github.com/containers/libpod/pkg/domain/infra/tunnel"
)
-func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
- switch mode {
+func NewContainerEngine(opts entities.EngineOptions) (entities.ContainerEngine, error) {
+ switch opts.EngineMode {
case entities.ABIMode:
return nil, fmt.Errorf("direct runtime not supported")
case entities.TunnelMode:
- ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...)
return &tunnel.ContainerEngine{ClientCxt: ctx}, err
}
- return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode)
}
// NewImageEngine factory provides a libpod runtime for image-related operations
-func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
- switch mode {
+func NewImageEngine(opts entities.EngineOptions) (entities.ImageEngine, error) {
+ switch opts.EngineMode {
case entities.ABIMode:
return nil, fmt.Errorf("direct image runtime not supported")
case entities.TunnelMode:
- ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
+ ctx, err := bindings.NewConnection(context.Background(), opts.Uri, opts.Identities...)
return &tunnel.ImageEngine{ClientCxt: ctx}, err
}
- return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
+ return nil, fmt.Errorf("runtime mode '%v' is not supported", opts.EngineMode)
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
new file mode 100644
index 000000000..8bf74126d
--- /dev/null
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -0,0 +1,42 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
+ exists, err := containers.Exists(ic.ClientCxt, nameOrId)
+ return &entities.BoolReport{Value: exists}, err
+}
+
+func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) {
+ var (
+ responses []entities.WaitReport
+ )
+ cons, err := getContainersByContext(ic.ClientCxt, false, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range cons {
+ response := entities.WaitReport{Id: c.ID}
+ exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition)
+ if err != nil {
+ response.Error = err
+ } else {
+ response.ExitCode = exitCode
+ }
+ responses = append(responses, response)
+ }
+ return responses, nil
+}
+
+func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
+ panic("implement me")
+}
+
+func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
+ panic("implement me")
+}
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
new file mode 100644
index 000000000..d5a3224c2
--- /dev/null
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -0,0 +1,41 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]libpod.ListContainer, error) {
+ var (
+ cons []libpod.ListContainer
+ )
+ if all && len(namesOrIds) > 0 {
+ return nil, errors.New("cannot lookup containers and all")
+ }
+ c, err := containers.List(contextWithConnection, nil, &bindings.PTrue, nil, nil, nil, &bindings.PTrue)
+ if err != nil {
+ return nil, err
+ }
+ if all {
+ return c, err
+ }
+ for _, id := range namesOrIds {
+ var found bool
+ for _, con := range c {
+ if id == con.ID || util.StringInSlice(id, con.Names) {
+ cons = append(cons, con)
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil, errors.Errorf("unable to find container %q", id)
+ }
+ }
+ return cons, nil
+}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
new file mode 100644
index 000000000..500069d51
--- /dev/null
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -0,0 +1,13 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/bindings/pods"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+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
+}
diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go
index af433a6d9..eb9b34e4a 100644
--- a/pkg/domain/infra/tunnel/runtime.go
+++ b/pkg/domain/infra/tunnel/runtime.go
@@ -16,18 +16,6 @@ type ContainerEngine struct {
ClientCxt context.Context
}
-func (r *ContainerEngine) Shutdown(force bool) error {
- return nil
-}
-
-func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
- panic("implement me")
-}
-
-func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
- panic("implement me")
-}
-
func (r *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
panic("implement me")
}
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
new file mode 100644
index 000000000..49cf6a2f6
--- /dev/null
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -0,0 +1,16 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/pkg/bindings/volumes"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IdOrNameResponse, error) {
+ response, err := volumes.Create(ic.ClientCxt, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.IdOrNameResponse{IdOrName: response.Name}, nil
+}
diff --git a/pkg/specgen/config_unsupported.go b/pkg/specgen/config_unsupported.go
index 5d24ac39c..c2d3257c9 100644
--- a/pkg/specgen/config_unsupported.go
+++ b/pkg/specgen/config_unsupported.go
@@ -3,10 +3,11 @@
package specgen
import (
+ "github.com/containers/libpod/libpod/image"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
-func (s *SpecGenerator) getSeccompConfig(configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
+func (s *SpecGenerator) getSeccompConfig(configSpec *spec.Spec, img *image.Image) (*spec.LinuxSeccomp, error) {
return nil, errors.New("function not supported on non-linux OS's")
}
diff --git a/pkg/specgen/pod.go b/pkg/specgen/pod.go
new file mode 100644
index 000000000..1aada83c4
--- /dev/null
+++ b/pkg/specgen/pod.go
@@ -0,0 +1,140 @@
+package specgen
+
+import (
+ "net"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
+// PodBasicConfig contains basic configuration options for pods.
+type PodBasicConfig struct {
+ // Name is the name of the pod.
+ // If not provided, a name will be generated when the pod is created.
+ // Optional.
+ Name string `json:"name,omitempty"`
+ // Hostname is the pod's hostname. If not set, the name of the pod will
+ // be used (if a name was not provided here, the name auto-generated for
+ // the pod will be used). This will be used by the infra container and
+ // all containers in the pod as long as the UTS namespace is shared.
+ // Optional.
+ Hostname string `json:"hostname,omitempty"`
+ // Labels are key-value pairs that are used to add metadata to pods.
+ // Optional.
+ Labels map[string]string `json:"labels,omitempty"`
+ // NoInfra tells the pod not to create an infra container. If this is
+ // done, many networking-related options will become unavailable.
+ // Conflicts with setting any options in PodNetworkConfig, and the
+ // InfraCommand and InfraImages in this struct.
+ // Optional.
+ NoInfra bool `json:"no_infra,omitempty"`
+ // InfraCommand sets the command that will be used to start the infra
+ // container.
+ // If not set, the default set in the Libpod configuration file will be
+ // used.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ InfraCommand []string `json:"infra_command,omitempty"`
+ // InfraImage is the image that will be used for the infra container.
+ // If not set, the default set in the Libpod configuration file will be
+ // used.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ InfraImage string `json:"infra_image,omitempty"`
+ // SharedNamespaces instructs the pod to share a set of namespaces.
+ // Shared namespaces will be joined (by default) by every container
+ // which joins the pod.
+ // If not set and NoInfra is false, the pod will set a default set of
+ // namespaces to share.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ SharedNamespaces []string `json:"shared_namespaces,omitempty"`
+}
+
+// PodNetworkConfig contains networking configuration for a pod.
+type PodNetworkConfig struct {
+ // NetNS is the configuration to use for the infra container's network
+ // namespace. This network will, by default, be shared with all
+ // containers in the pod.
+ // Cannot be set to FromContainer and FromPod.
+ // Setting this to anything except "" conflicts with NoInfra=true.
+ // Defaults to Bridge as root and Slirp as rootless.
+ // Mandatory.
+ NetNS Namespace `json:"netns,omitempty"`
+ // StaticIP sets a static IP for the infra container. As the infra
+ // container's network is used for the entire pod by default, this will
+ // thus be a static IP for the whole pod.
+ // Only available if NetNS is set to Bridge (the default for root).
+ // As such, conflicts with NoInfra=true by proxy.
+ // Optional.
+ StaticIP *net.IP `json:"static_ip,omitempty"`
+ // StaticMAC sets a static MAC for the infra container. As the infra
+ // container's network is used for the entire pod by default, this will
+ // thus be a static MAC for the entire pod.
+ // Only available if NetNS is set to Bridge (the default for root).
+ // As such, conflicts with NoInfra=true by proxy.
+ // Optional.
+ StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"`
+ // PortMappings is a set of ports to map into the infra container.
+ // As, by default, containers share their network with the infra
+ // container, this will forward the ports to the entire pod.
+ // Only available if NetNS is set to Bridge or Slirp.
+ // Optional.
+ PortMappings []ocicni.PortMapping `json:"portmappings,omitempty"`
+ // CNINetworks is a list of CNI networks that the infra container will
+ // join. As, by default, containers share their network with the infra
+ // container, these networks will effectively be joined by the
+ // entire pod.
+ // Only available when NetNS is set to Bridge, the default for root.
+ // Optional.
+ CNINetworks []string `json:"cni_networks,omitempty"`
+ // NoManageResolvConf indicates that /etc/resolv.conf should not be
+ // managed by the pod. Instead, each container will create and manage a
+ // separate resolv.conf as if they had not joined a pod.
+ // Conflicts with NoInfra=true and DNSServer, DNSSearch, DNSOption.
+ // Optional.
+ NoManageResolvConf bool `json:"no_manage_resolv_conf,omitempty"`
+ // DNSServer is a set of DNS servers that will be used in the infra
+ // container's resolv.conf, which will, by default, be shared with all
+ // containers in the pod.
+ // If not provided, the host's DNS servers will be used, unless the only
+ // server set is a localhost address. As the container cannot connect to
+ // the host's localhost, a default server will instead be set.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ DNSServer []net.IP `json:"dns_server,omitempty"`
+ // DNSSearch is a set of DNS search domains that will be used in the
+ // infra container's resolv.conf, which will, by default, be shared with
+ // all containers in the pod.
+ // If not provided, DNS search domains from the host's resolv.conf will
+ // be used.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ DNSSearch []string `json:"dns_search,omitempty"`
+ // DNSOption is a set of DNS options that will be used in the infra
+ // container's resolv.conf, which will, by default, be shared with all
+ // containers in the pod.
+ // Conflicts with NoInfra=true.
+ // Optional.
+ DNSOption []string `json:"dns_option,omitempty"`
+ // NoManageHosts indicates that /etc/hosts should not be managed by the
+ // pod. Instead, each container will create a separate /etc/hosts as
+ // they would if not in a pod.
+ // Conflicts with HostAdd.
+ NoManageHosts bool `json:"no_manage_hosts,omitempty"`
+ // HostAdd is a set of hosts that will be added to the infra container's
+ // /etc/hosts that will, by default, be shared with all containers in
+ // the pod.
+ // Conflicts with NoInfra=true and NoManageHosts.
+ // Optional.
+ HostAdd []string `json:"hostadd,omitempty"`
+}
+
+// PodCgroupConfig contains configuration options about a pod's cgroups.
+// This will be expanded in future updates to pods.
+type PodCgroupConfig struct {
+ // CgroupParent is the parent for the CGroup that the pod will create.
+ // This pod cgroup will, in turn, be the default cgroup parent for all
+ // containers in the pod.
+ // Optional.
+ CgroupParent string `json:"cgroup_parent,omitempty"`
+}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 7a430652a..b123c1da5 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -53,7 +53,7 @@ type ContainerBasicConfig struct {
Terminal bool `json:"terminal,omitempty"`
// Stdin is whether the container will keep its STDIN open.
Stdin bool `json:"stdin,omitempty"`
- // Labels are key-valid labels that are used to add metadata to
+ // Labels are key-value pairs that are used to add metadata to
// containers.
// Optional.
Labels map[string]string `json:"labels,omitempty"`
diff --git a/pkg/util/utils_linux_test.go b/pkg/util/utils_linux_test.go
new file mode 100644
index 000000000..38e6dbef9
--- /dev/null
+++ b/pkg/util/utils_linux_test.go
@@ -0,0 +1,29 @@
+package util
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetImageConfigStopSignal(t *testing.T) {
+ // Linux-only beause parsing signal names is not supported on non-Linux systems by
+ // pkg/signal.
+ stopSignalValidInt, err := GetImageConfig([]string{"STOPSIGNAL 9"})
+ require.Nil(t, err)
+ assert.Equal(t, stopSignalValidInt.StopSignal, "9")
+
+ stopSignalValidString, err := GetImageConfig([]string{"STOPSIGNAL SIGKILL"})
+ require.Nil(t, err)
+ assert.Equal(t, stopSignalValidString.StopSignal, "9")
+
+ _, err = GetImageConfig([]string{"STOPSIGNAL 0"})
+ assert.NotNil(t, err)
+
+ _, err = GetImageConfig([]string{"STOPSIGNAL garbage"})
+ assert.NotNil(t, err)
+
+ _, err = GetImageConfig([]string{"STOPSIGNAL "})
+ assert.NotNil(t, err)
+}
diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go
index f4b03599d..0995d1e20 100644
--- a/pkg/util/utils_test.go
+++ b/pkg/util/utils_test.go
@@ -219,25 +219,6 @@ func TestGetImageConfigLabel(t *testing.T) {
assert.NotNil(t, err)
}
-func TestGetImageConfigStopSignal(t *testing.T) {
- stopSignalValidInt, err := GetImageConfig([]string{"STOPSIGNAL 9"})
- require.Nil(t, err)
- assert.Equal(t, stopSignalValidInt.StopSignal, "9")
-
- stopSignalValidString, err := GetImageConfig([]string{"STOPSIGNAL SIGKILL"})
- require.Nil(t, err)
- assert.Equal(t, stopSignalValidString.StopSignal, "9")
-
- _, err = GetImageConfig([]string{"STOPSIGNAL 0"})
- assert.NotNil(t, err)
-
- _, err = GetImageConfig([]string{"STOPSIGNAL garbage"})
- assert.NotNil(t, err)
-
- _, err = GetImageConfig([]string{"STOPSIGNAL "})
- assert.NotNil(t, err)
-}
-
func TestGetImageConfigOnBuild(t *testing.T) {
onBuildOne, err := GetImageConfig([]string{"ONBUILD ADD /testdir1"})
require.Nil(t, err)