summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/runlabel.go80
-rw-r--r--cmd/podman/shared/funcs.go4
-rw-r--r--cmd/podman/stop.go27
-rw-r--r--cmd/podman/utils.go48
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman-container-runlabel.1.md21
-rw-r--r--docs/podman-create.1.md6
-rw-r--r--docs/podman-run.1.md6
-rw-r--r--libpod/boltdb_state_linux.go2
-rw-r--r--libpod/container.go8
-rw-r--r--libpod/container_easyjson.go14
-rw-r--r--libpod/networking_linux.go19
-rw-r--r--libpod/options.go25
-rw-r--r--libpod/pod_easyjson.go2
-rw-r--r--pkg/spec/createconfig.go13
-rw-r--r--test/e2e/run_staticip_test.go58
-rw-r--r--vendor.conf5
-rw-r--r--vendor/github.com/containers/image/docker/docker_client.go12
-rw-r--r--vendor/github.com/containers/image/docker/docker_image.go2
-rw-r--r--vendor/github.com/containers/image/docker/docker_image_dest.go2
-rw-r--r--vendor/github.com/containers/image/docker/docker_image_src.go6
-rw-r--r--vendor/github.com/containers/image/openshift/openshift.go2
-rw-r--r--vendor/github.com/containers/image/pkg/docker/config/config.go9
-rw-r--r--vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go41
-rw-r--r--vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go17
26 files changed, 362 insertions, 72 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 9ab0e57e5..c1e15e2fb 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -249,6 +249,10 @@ var createFlags = []cli.Flag{
Usage: "Keep STDIN open even if not attached",
},
cli.StringFlag{
+ Name: "ip",
+ Usage: "Specify a static IPv4 address for the container",
+ },
+ cli.StringFlag{
Name: "ipc",
Usage: "IPC namespace to use",
},
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index 34e6b9093..d514a79fc 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -6,9 +6,11 @@ import (
"os"
"strings"
+ "github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -23,7 +25,7 @@ var (
},
cli.BoolFlag{
Name: "display",
- Usage: "preview the command that `podman install` would execute",
+ Usage: "preview the command that the label would run",
},
cli.StringFlag{
Name: "cert-dir",
@@ -74,13 +76,14 @@ var (
Executes a command as described by a container image label.
`
runlabelCommand = cli.Command{
- Name: "runlabel",
- Usage: "Execute the command described by an image label",
- Description: runlabelDescription,
- Flags: runlabelFlags,
- Action: runlabelCmd,
- ArgsUsage: "",
- OnUsageError: usageErrorHandler,
+ Name: "runlabel",
+ Usage: "Execute the command described by an image label",
+ Description: runlabelDescription,
+ Flags: runlabelFlags,
+ Action: runlabelCmd,
+ ArgsUsage: "",
+ SkipArgReorder: true,
+ OnUsageError: usageErrorHandler,
}
)
@@ -110,12 +113,8 @@ func runlabelCmd(c *cli.Context) error {
defer runtime.Shutdown(false)
args := c.Args()
- if len(args) == 0 {
- logrus.Errorf("an image name must be specified")
- return nil
- }
if len(args) < 2 {
- logrus.Errorf("the runlabel command requires at least 2 arguments")
+ logrus.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE")
return nil
}
if err := validateFlags(c, runlabelFlags); err != nil {
@@ -130,18 +129,17 @@ func runlabelCmd(c *cli.Context) error {
runlabelImage := args[1]
- if c.IsSet("opts1") {
- opts["opts1"] = c.String("opts1")
+ if c.IsSet("opt1") {
+ opts["opt1"] = c.String("opt1")
}
- if c.IsSet("opts2") {
- opts["opts2"] = c.String("opts2")
+ if c.IsSet("opt2") {
+ opts["opt2"] = c.String("opt2")
}
- if c.IsSet("opts3") {
- opts["opts3"] = c.String("opts3")
+ if c.IsSet("opt3") {
+ opts["opt3"] = c.String("opt3")
}
ctx := getContext()
- rtc := runtime.GetConfig()
stdErr = os.Stderr
stdOut = os.Stdout
@@ -154,7 +152,21 @@ func runlabelCmd(c *cli.Context) error {
}
if pull {
- newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, rtc.SignaturePolicyPath, "", stdOut, nil, image.SigningOptions{}, false, false)
+ var registryCreds *types.DockerAuthConfig
+ if c.IsSet("creds") {
+ creds, err := util.ParseRegistryCreds(c.String("creds"))
+ if err != nil {
+ return err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: c.String("cert-dir"),
+ DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
+ }
+
+ newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, c.String("signature-policy"), c.String("authfile"), stdOut, &dockerRegistryOptions, image.SigningOptions{}, false, false)
} else {
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
}
@@ -187,6 +199,23 @@ func runlabelCmd(c *cli.Context) error {
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
+ envmap := envSliceToMap(env)
+
+ envmapper := func(k string) string {
+ switch k {
+ case "OPT1":
+ return envmap["OPT1"]
+ case "OPT2":
+ return envmap["OPT2"]
+ case "OPT3":
+ return envmap["OPT3"]
+ }
+ return ""
+ }
+
+ newS := os.Expand(strings.Join(cmd, " "), envmapper)
+ cmd = strings.Split(newS, " ")
+
if !c.Bool("quiet") {
fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
if c.Bool("display") {
@@ -195,3 +224,12 @@ func runlabelCmd(c *cli.Context) error {
}
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
}
+
+func envSliceToMap(env []string) map[string]string {
+ m := make(map[string]string)
+ for _, i := range env {
+ split := strings.Split(i, "=")
+ m[split[0]] = strings.Join(split[1:], " ")
+ }
+ return m
+}
diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go
index 21e7fe10d..485944f29 100644
--- a/cmd/podman/shared/funcs.go
+++ b/cmd/podman/shared/funcs.go
@@ -24,10 +24,14 @@ func GenerateCommand(command, imageName, name string) []string {
newArg = imageName
case "IMAGE=IMAGE":
newArg = fmt.Sprintf("IMAGE=%s", imageName)
+ case "IMAGE=$IMAGE":
+ newArg = fmt.Sprintf("IMAGE=%s", imageName)
case "NAME":
newArg = name
case "NAME=NAME":
newArg = fmt.Sprintf("NAME=%s", name)
+ case "NAME=$NAME":
+ newArg = fmt.Sprintf("NAME=%s", name)
default:
newArg = arg
}
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index d2fa87730..664d91ea3 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
+ rt "runtime"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
@@ -98,21 +99,33 @@ func stopCmd(c *cli.Context) error {
}
}
+ var stopFuncs []workerInput
for _, ctr := range containers {
+ con := ctr
var stopTimeout uint
if c.IsSet("timeout") {
stopTimeout = c.Uint("timeout")
} else {
stopTimeout = ctr.StopTimeout()
}
- if err := ctr.StopWithTimeout(stopTimeout); err != nil && err != libpod.ErrCtrStopped {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
- }
- lastError = errors.Wrapf(err, "failed to stop container %v", ctr.ID())
- } else {
- fmt.Println(ctr.ID())
+ f := func() error {
+ return con.StopWithTimeout(stopTimeout)
+ }
+ stopFuncs = append(stopFuncs, workerInput{
+ containerID: con.ID(),
+ parallelFunc: f,
+ })
+ }
+
+ stopErrors := parallelExecuteWorkerPool(rt.NumCPU()*3, stopFuncs)
+
+ for cid, result := range stopErrors {
+ if result != nil && result != libpod.ErrCtrStopped {
+ fmt.Println(result.Error())
+ lastError = result
+ continue
}
+ fmt.Println(cid)
}
return lastError
}
diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go
index 89ec48dbe..1b767532e 100644
--- a/cmd/podman/utils.go
+++ b/cmd/podman/utils.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
gosignal "os/signal"
+ "sync"
"github.com/containers/libpod/libpod"
"github.com/docker/docker/pkg/signal"
@@ -215,3 +216,50 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error
}
return pods, lastError
}
+
+type pFunc func() error
+
+type workerInput struct {
+ containerID string
+ parallelFunc pFunc
+}
+
+// worker is a "threaded" worker that takes jobs from the channel "queue"
+func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results map[string]error) {
+ for j := range jobs {
+ err := j.parallelFunc()
+ results[j.containerID] = err
+ wg.Done()
+ }
+}
+
+// parallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker
+// int is determines how many workers/threads should be premade.
+func parallelExecuteWorkerPool(workers int, functions []workerInput) map[string]error {
+ var (
+ wg sync.WaitGroup
+ )
+ results := make(map[string]error)
+ paraJobs := make(chan workerInput, len(functions))
+
+ // If we have more workers than functions, match up the number of workers and functions
+ if workers > len(functions) {
+ workers = len(functions)
+ }
+
+ // Create the workers
+ for w := 1; w <= workers; w++ {
+ go worker(&wg, paraJobs, results)
+ }
+
+ // Add jobs to the workers
+ for _, j := range functions {
+ j := j
+ wg.Add(1)
+ paraJobs <- j
+ }
+
+ close(paraJobs)
+ wg.Wait()
+ return results
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index 604a25f5d..5cfed348f 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1512,6 +1512,7 @@ _podman_container_run() {
--hostname -h
--image-volume
--init-path
+ --ip
--ipc
--kernel-memory
--label-file
diff --git a/docs/podman-container-runlabel.1.md b/docs/podman-container-runlabel.1.md
index 889a5fb03..4611aa4d9 100644
--- a/docs/podman-container-runlabel.1.md
+++ b/docs/podman-container-runlabel.1.md
@@ -28,8 +28,8 @@ If the container image has a LABEL INSTALL instruction like the following:
Note: Podman will always ensure that `podman` is the first argument of the command being executed.
-**NAME**
-The name specified via the command. NAME will be replaced with IMAGE if it is not specified.
+**LABEL**
+The label name specified via the command.
**IMAGE**
Image name specified via the command.
@@ -95,6 +95,23 @@ Require HTTPS and verify certificates when contacting registries (default: true)
then tls verification will be used, If set to false then tls verification will not be used. If not specified
tls verification will be used unless the target registry is listed as an insecure registry in registries.conf
+## Examples ##
+
+Execute the run label of an image called foobar.
+```
+$ sudo podman container runlabel run foobar
+```
+
+Execute the install label of an image called foobar with additional arguments.
+```
+$ sudo podman container runlabel install foobar apples oranges
+```
+
+Display the command that would be executed by runlabel.
+```
+$ sudo podman container runlabel --display run foobar
+```
+
## SEE ALSO
podman(1)
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index c42671b76..509d8820f 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -286,7 +286,9 @@ Not implemented
**--ip**=""
-Not implemented
+Specify a static IP address for the container, for example '10.88.64.128'.
+Can only be used if no additional CNI networks to join were specified via '--network=<network-name>', and if the container is not joining another container's network namespace via '--network=container:<name|id>'.
+The address must be within the default CNI network's pool (default 10.88.0.0/16).
**--ipc**=""
@@ -416,7 +418,7 @@ to the container with **--name** then the daemon will also generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-**--network**="*bridge*"
+**--net**, **--network**="*bridge*"
Set the Network mode for the container
'bridge': create a network stack on the default bridge
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index fccebb7f7..c303492e7 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -297,7 +297,9 @@ Not implemented
**--ip**=""
-Not implemented
+Specify a static IP address for the container, for example '10.88.64.128'.
+Can only be used if no additional CNI networks to join were specified via '--network=<network-name>', and if the container is not joining another container's network namespace via '--network=container:<name|id>'.
+The address must be within the default CNI network's pool (default 10.88.0.0/16).
**--ipc**=""
@@ -401,7 +403,7 @@ to the container with **--name** then the daemon will also generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-**--network**="*bridge*"
+**--net**, **--network**="*bridge*"
Set the Network mode for the container:
- `bridge`: create a network stack on the default bridge
diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go
index fce3a1b1e..d91f311e5 100644
--- a/libpod/boltdb_state_linux.go
+++ b/libpod/boltdb_state_linux.go
@@ -25,7 +25,7 @@ func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) er
if err == nil {
newState.NetNS = ns
} else {
- logrus.Errorf("error joining network namespace for container %s", ctr.ID())
+ logrus.Errorf("error joining network namespace for container %s: %v", ctr.ID(), err)
ctr.valid = false
}
}
diff --git a/libpod/container.go b/libpod/container.go
index 55a0f3a2c..5997c0b66 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -269,9 +269,13 @@ type ContainerConfig struct {
// Network Config
// CreateNetNS indicates that libpod should create and configure a new
- // network namespace for the container
- // This cannot be set if NetNsCtr is also set
+ // network namespace for the container.
+ // This cannot be set if NetNsCtr is also set.
CreateNetNS bool `json:"createNetNS"`
+ // StaticIP is a static IP to request for the container.
+ // This cannot be set unless CreateNetNS is set.
+ // If not set, the container will be dynamically assigned an IP by CNI.
+ StaticIP net.IP `json:"staticIP"`
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true
diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go
index 916118aec..f78366065 100644
--- a/libpod/container_easyjson.go
+++ b/libpod/container_easyjson.go
@@ -1383,6 +1383,10 @@ func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, ou
}
case "createNetNS":
out.CreateNetNS = bool(in.Bool())
+ case "staticIP":
+ if data := in.UnsafeBytes(); in.Ok() {
+ in.AddError((out.StaticIP).UnmarshalText(data))
+ }
case "portMappings":
if in.IsNull() {
in.Skip()
@@ -2005,6 +2009,16 @@ func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer,
}
out.Bool(bool(in.CreateNetNS))
}
+ {
+ const prefix string = ",\"staticIP\":"
+ if first {
+ first = false
+ out.RawString(prefix[1:])
+ } else {
+ out.RawString(prefix)
+ }
+ out.RawText((in.StaticIP).MarshalText())
+ }
if len(in.PortMappings) != 0 {
const prefix string = ",\"portMappings\":"
if first {
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 17e79aa62..acb4e2a90 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -5,6 +5,7 @@ package libpod
import (
"crypto/rand"
"fmt"
+ "net"
"os"
"os/exec"
"path/filepath"
@@ -25,8 +26,8 @@ import (
)
// Get an OCICNI network config
-func getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping) ocicni.PodNetwork {
- return ocicni.PodNetwork{
+func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork {
+ network := ocicni.PodNetwork{
Name: name,
Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
ID: id,
@@ -34,11 +35,21 @@ func getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.Po
PortMappings: ports,
Networks: networks,
}
+
+ if staticIP != nil {
+ defaultNetwork := r.netPlugin.GetDefaultNetworkName()
+
+ network.Networks = []string{defaultNetwork}
+ network.NetworkConfig = make(map[string]ocicni.NetworkConfig)
+ network.NetworkConfig[defaultNetwork] = ocicni.NetworkConfig{IP: staticIP.String()}
+ }
+
+ return network
}
// Create and configure a new network namespace for a container
func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) {
- podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings)
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP)
results, err := r.netPlugin.SetUpPod(podNetwork)
if err != nil {
@@ -216,7 +227,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
- podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings)
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP)
// The network may have already been torn down, so don't fail here, just log
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
diff --git a/libpod/options.go b/libpod/options.go
index 977f3f4c2..9f966cead 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -828,6 +828,31 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netwo
}
}
+// WithStaticIP indicates that the container should request a static IP from
+// the CNI plugins.
+// It cannot be set unless WithNetNS has already been passed.
+// Further, it cannot be set if additional CNI networks to join have been
+// specified.
+func WithStaticIP(ip net.IP) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+
+ if !ctr.config.CreateNetNS {
+ return errors.Wrapf(ErrInvalidArg, "cannot set a static IP if the container is not creating a network namespace")
+ }
+
+ if len(ctr.config.Networks) != 0 {
+ return errors.Wrapf(ErrInvalidArg, "cannot set a static IP if joining additional CNI networks")
+ }
+
+ ctr.config.StaticIP = ip
+
+ return nil
+ }
+}
+
// WithLogPath sets the path to the log file.
func WithLogPath(path string) CtrCreateOption {
return func(ctr *Container) error {
diff --git a/libpod/pod_easyjson.go b/libpod/pod_easyjson.go
index 2891e51f2..6c1c939f3 100644
--- a/libpod/pod_easyjson.go
+++ b/libpod/pod_easyjson.go
@@ -1,3 +1,5 @@
+// +build seccomp ostree selinux varlink exclude_graphdriver_devicemapper
+
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package libpod
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 887ef8e95..e9a5dc9dc 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -2,6 +2,7 @@ package createconfig
import (
"encoding/json"
+ "net"
"os"
"strconv"
"strings"
@@ -311,9 +312,6 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
var pod *libpod.Pod
var err error
- // Uncomment after talking to mheon about unimplemented funcs
- // options = append(options, libpod.WithLabels(c.labels))
-
if c.Interactive {
options = append(options, libpod.WithStdin())
}
@@ -442,6 +440,15 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib
if logPath != "" {
options = append(options, libpod.WithLogPath(logPath))
}
+ if c.IPAddress != "" {
+ ip := net.ParseIP(c.IPAddress)
+ if ip == nil {
+ return nil, errors.Wrapf(libpod.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
+ } else if ip.To4() == nil {
+ return nil, errors.Wrapf(libpod.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
+ }
+ options = append(options, libpod.WithStaticIP(ip))
+ }
options = append(options, libpod.WithPrivileged(c.Privileged))
diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go
new file mode 100644
index 000000000..b69d15cee
--- /dev/null
+++ b/test/e2e/run_staticip_test.go
@@ -0,0 +1,58 @@
+package integration
+
+import (
+ "fmt"
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman run with --ip flag", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
+ GinkgoWriter.Write([]byte(timedResult))
+ })
+
+ It("Podman run --ip with garbage address", func() {
+ result := podmanTest.Podman([]string{"run", "-ti", "--ip", "114232346", ALPINE, "ls"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("Podman run --ip with v6 address", func() {
+ result := podmanTest.Podman([]string{"run", "-ti", "--ip", "2001:db8:bad:beef::1", ALPINE, "ls"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("Podman run --ip with non-allocatable IP", func() {
+ result := podmanTest.Podman([]string{"run", "-ti", "--ip", "203.0.113.124", ALPINE, "ls"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("Podman run with specified static IP has correct IP", func() {
+ result := podmanTest.Podman([]string{"run", "-ti", "--ip", "10.88.64.128", ALPINE, "ip", "addr"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring("10.88.64.128/16"))
+ })
+})
diff --git a/vendor.conf b/vendor.conf
index b526c1560..be89c418e 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -10,11 +10,11 @@ github.com/containerd/cgroups 58556f5ad8448d99a6f7bea69ea4bdb7747cfeb0
github.com/containerd/continuity master
github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins 1562a1e60ed101aacc5e08ed9dbeba8e9f3d4ec1
-github.com/containers/image 7a1eac5d1df2dbd73d8b71853ebce32d989fcae3
+github.com/containers/image 918dbb93e6e099b196b498c38d079f6bb924d0c8
github.com/containers/storage 41294c85d97bef688e18f710402895dbecde3308
github.com/containers/psgo 5dde6da0bc8831b35243a847625bcf18183bd1ee
github.com/coreos/go-systemd v14
-github.com/cri-o/ocicni master
+github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c
github.com/cyphar/filepath-securejoin v0.2.1
github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution 7a8efe719e55bbfaff7bc5718cdf0ed51ca821df
@@ -75,6 +75,7 @@ golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674
golang.org/x/sys master
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
+golang.org/x/sync master
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
gopkg.in/cheggaaa/pb.v1 v1.0.7
gopkg.in/inf.v0 v0.9.0
diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go
index a410279a5..6d2c5b670 100644
--- a/vendor/github.com/containers/image/docker/docker_client.go
+++ b/vendor/github.com/containers/image/docker/docker_client.go
@@ -259,7 +259,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
case http.StatusUnauthorized:
return ErrUnauthorizedForCredentials
default:
- return errors.Errorf("error occured with status code %q", resp.StatusCode)
+ return errors.Errorf("error occured with status code %d (%s)", resp.StatusCode, http.StatusText(resp.StatusCode))
}
}
@@ -329,7 +329,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- logrus.Debugf("error getting search results from v1 endpoint %q, status code %d", registry, resp.StatusCode)
+ logrus.Debugf("error getting search results from v1 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
} else {
if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
return nil, err
@@ -346,7 +346,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- logrus.Errorf("error getting search results from v2 endpoint %q, status code %d", registry, resp.StatusCode)
+ logrus.Errorf("error getting search results from v2 endpoint %q, status code %d (%s)", registry, resp.StatusCode, http.StatusText(resp.StatusCode))
} else {
if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
return nil, err
@@ -495,7 +495,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope
case http.StatusOK:
break
default:
- return nil, errors.Errorf("unexpected http code: %d, URL: %s", res.StatusCode, authReq.URL)
+ return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL)
}
tokenBlob, err := ioutil.ReadAll(res.Body)
if err != nil {
@@ -522,7 +522,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
defer resp.Body.Close()
logrus.Debugf("Ping %s status %d", url, resp.StatusCode)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
- return errors.Errorf("error pinging registry %s, response code %d", c.registry, resp.StatusCode)
+ return errors.Errorf("error pinging registry %s, response code %d (%s)", c.registry, resp.StatusCode, http.StatusText(resp.StatusCode))
}
c.challenges = parseAuthHeader(resp.Header)
c.scheme = scheme
@@ -542,8 +542,8 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
pingV1 := func(scheme string) bool {
url := fmt.Sprintf(resolvedPingV1URL, scheme, c.registry)
resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth)
- logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err)
if err != nil {
+ logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err)
return false
}
defer resp.Body.Close()
diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go
index a1a115080..2ab95f329 100644
--- a/vendor/github.com/containers/image/docker/docker_image.go
+++ b/vendor/github.com/containers/image/docker/docker_image.go
@@ -73,7 +73,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
// print url also
- return nil, errors.Errorf("Invalid status code returned when fetching tags list %d", res.StatusCode)
+ return nil, errors.Errorf("Invalid status code returned when fetching tags list %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
}
var tagsHolder struct {
diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go
index 94763d026..9bbffef93 100644
--- a/vendor/github.com/containers/image/docker/docker_image_dest.go
+++ b/vendor/github.com/containers/image/docker/docker_image_dest.go
@@ -207,7 +207,7 @@ func (d *dockerImageDestination) HasBlob(ctx context.Context, info types.BlobInf
logrus.Debugf("... not present")
return false, -1, nil
default:
- return false, -1, errors.Errorf("failed to read from destination repository %s: %v", reference.Path(d.ref.ref), http.StatusText(res.StatusCode))
+ return false, -1, errors.Errorf("failed to read from destination repository %s: %d (%s)", reference.Path(d.ref.ref), res.StatusCode, http.StatusText(res.StatusCode))
}
}
diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go
index 3ff826aaa..aedab9731 100644
--- a/vendor/github.com/containers/image/docker/docker_image_src.go
+++ b/vendor/github.com/containers/image/docker/docker_image_src.go
@@ -140,7 +140,7 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string)
resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth)
if err == nil {
if resp.StatusCode != http.StatusOK {
- err = errors.Errorf("error fetching external blob from %q: %d", url, resp.StatusCode)
+ err = errors.Errorf("error fetching external blob from %q: %d (%s)", url, resp.StatusCode, http.StatusText(resp.StatusCode))
logrus.Debug(err)
continue
}
@@ -175,7 +175,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo) (i
}
if res.StatusCode != http.StatusOK {
// print url also
- return nil, 0, errors.Errorf("Invalid status code returned when fetching blob %d", res.StatusCode)
+ return nil, 0, errors.Errorf("Invalid status code returned when fetching blob %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
}
return res.Body, getBlobSize(res), nil
}
@@ -274,7 +274,7 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (
if res.StatusCode == http.StatusNotFound {
return nil, true, nil
} else if res.StatusCode != http.StatusOK {
- return nil, false, errors.Errorf("Error reading signature from %s: status %d", url.String(), res.StatusCode)
+ return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode))
}
sig, err := ioutil.ReadAll(res.Body)
if err != nil {
diff --git a/vendor/github.com/containers/image/openshift/openshift.go b/vendor/github.com/containers/image/openshift/openshift.go
index 2cadb0ce2..a8d5072d9 100644
--- a/vendor/github.com/containers/image/openshift/openshift.go
+++ b/vendor/github.com/containers/image/openshift/openshift.go
@@ -127,7 +127,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re
if statusValid {
return nil, errors.New(status.Message)
}
- return nil, errors.Errorf("HTTP error: status code: %d, body: %s", res.StatusCode, string(body))
+ return nil, errors.Errorf("HTTP error: status code: %d (%s), body: %s", res.StatusCode, http.StatusText(res.StatusCode), string(body))
}
return body, nil
diff --git a/vendor/github.com/containers/image/pkg/docker/config/config.go b/vendor/github.com/containers/image/pkg/docker/config/config.go
index 58e8d5022..1f576253d 100644
--- a/vendor/github.com/containers/image/pkg/docker/config/config.go
+++ b/vendor/github.com/containers/image/pkg/docker/config/config.go
@@ -165,9 +165,12 @@ func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) {
var auths dockerConfigFile
raw, err := ioutil.ReadFile(path)
- if os.IsNotExist(err) {
- auths.AuthConfigs = map[string]dockerAuthConfig{}
- return auths, nil
+ if err != nil {
+ if os.IsNotExist(err) {
+ auths.AuthConfigs = map[string]dockerAuthConfig{}
+ return auths, nil
+ }
+ return dockerConfigFile{}, err
}
if legacyFormat {
diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go
index 33a3ae063..dfc216389 100644
--- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go
+++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go
@@ -3,6 +3,7 @@ package ocicni
import (
"errors"
"fmt"
+ "net"
"os"
"path"
"sort"
@@ -351,14 +352,14 @@ func (plugin *cniNetworkPlugin) getNetwork(name string) (*cniNetwork, error) {
return net, nil
}
-func (plugin *cniNetworkPlugin) getDefaultNetworkName() string {
+func (plugin *cniNetworkPlugin) GetDefaultNetworkName() string {
plugin.RLock()
defer plugin.RUnlock()
return plugin.defaultNetName
}
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
- defaultNetName := plugin.getDefaultNetworkName()
+ defaultNetName := plugin.GetDefaultNetworkName()
if defaultNetName == "" {
return nil
}
@@ -383,7 +384,7 @@ func (plugin *cniNetworkPlugin) Name() string {
func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork) error) error {
networks := podNetwork.Networks
if len(networks) == 0 {
- networks = append(networks, plugin.getDefaultNetworkName())
+ networks = append(networks, plugin.GetDefaultNetworkName())
}
for i, netName := range networks {
// Interface names start at "eth0" and count up for each network
@@ -408,7 +409,7 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
- _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo")
+ _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", "")
if err != nil {
logrus.Errorf("Error while adding to cni lo network: %s", err)
return nil, err
@@ -416,7 +417,12 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu
results := make([]cnitypes.Result, 0)
if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error {
- result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName)
+ ip := ""
+ if conf, ok := podNetwork.NetworkConfig[network.name]; ok {
+ ip = conf.IP
+ }
+
+ result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, ip)
if err != nil {
logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err)
return err
@@ -439,7 +445,12 @@ func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error {
defer plugin.podUnlock(podNetwork)
return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error {
- if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName); err != nil {
+ ip := ""
+ if conf, ok := podNetwork.NetworkConfig[network.name]; ok {
+ ip = conf.IP
+ }
+
+ if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, ip); err != nil {
logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err)
return err
}
@@ -491,8 +502,8 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn
return results, nil
}
-func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName string) (cnitypes.Result, error) {
- rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName)
+func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (cnitypes.Result, error) {
+ rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip)
if err != nil {
logrus.Errorf("Error adding network: %v", err)
return nil, err
@@ -509,8 +520,8 @@ func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork,
return res, nil
}
-func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName string) error {
- rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName)
+func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) error {
+ rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip)
if err != nil {
logrus.Errorf("Error deleting network: %v", err)
return err
@@ -526,7 +537,7 @@ func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNet
return nil
}
-func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string) (*libcni.RuntimeConf, error) {
+func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (*libcni.RuntimeConf, error) {
logrus.Infof("Got pod network %+v", podNetwork)
rt := &libcni.RuntimeConf{
@@ -542,6 +553,14 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string)
},
}
+ // Add requested static IP to CNI_ARGS
+ if ip != "" {
+ if tstIP := net.ParseIP(ip); tstIP == nil {
+ return nil, fmt.Errorf("unable to parse IP address %q", ip)
+ }
+ rt.Args = append(rt.Args, [2]string{"IP", ip})
+ }
+
if len(podNetwork.PortMappings) == 0 {
return rt, nil
}
diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go
index 8ca61657a..d76094292 100644
--- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go
+++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go
@@ -24,6 +24,14 @@ type PortMapping struct {
HostIP string `json:"hostIP"`
}
+// NetworkConfig is additional configuration for a single CNI network.
+type NetworkConfig struct {
+ // IP is a static IP to be specified in the network. Can only be used
+ // with the hostlocal IP allocator. If left unset, an IP will be
+ // dynamically allocated.
+ IP string
+}
+
// PodNetwork configures the network of a pod sandbox.
type PodNetwork struct {
// Name is the name of the sandbox.
@@ -40,6 +48,11 @@ type PodNetwork struct {
// Networks is a list of CNI network names to attach to the sandbox
// Leave this list empty to attach the default network to the sandbox
Networks []string
+
+ // NetworkConfig is configuration specific to a single CNI network.
+ // It is optional, and can be omitted for some or all specified networks
+ // without issue.
+ NetworkConfig map[string]NetworkConfig
}
// CNIPlugin is the interface that needs to be implemented by a plugin
@@ -48,6 +61,10 @@ type CNIPlugin interface {
// for a plugin by name, e.g.
Name() string
+ // GetDefaultNetworkName returns the name of the plugin's default
+ // network.
+ GetDefaultNetworkName() string
+
// SetUpPod is the method called after the sandbox container of
// the pod has been created but before the other containers of the
// pod are launched.