summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-11-07 20:26:14 +0100
committerGitHub <noreply@github.com>2019-11-07 20:26:14 +0100
commitd919961f621d0b9eb70b971fc8e8915ee279ab60 (patch)
tree05272b838219ba06a56c4d06038ae2f7d6fa1fe1
parent347499778cb1a7045dc831f99b9539bc20fe008d (diff)
parent82e4116e578f72bd627330ac10d541c3e234738c (diff)
downloadpodman-d919961f621d0b9eb70b971fc8e8915ee279ab60.tar.gz
podman-d919961f621d0b9eb70b971fc8e8915ee279ab60.tar.bz2
podman-d919961f621d0b9eb70b971fc8e8915ee279ab60.zip
Merge pull request #4451 from giuseppe/set-mac
podman: add support for specifying MAC
-rw-r--r--Makefile3
-rw-r--r--cmd/podman/cliconfig/config.go17
-rw-r--r--cmd/podman/common.go2
-rw-r--r--cmd/podman/restore.go1
-rw-r--r--cmd/podman/shared/create.go4
-rw-r--r--cni/87-podman-bridge.conflist3
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/source/markdown/podman-container-restore.1.md9
-rw-r--r--go.mod4
-rw-r--r--go.sum4
-rw-r--r--libpod/container.go8
-rw-r--r--libpod/container_api.go5
-rw-r--r--libpod/container_internal_linux.go50
-rw-r--r--libpod/networking_linux.go49
-rw-r--r--libpod/options.go25
-rw-r--r--pkg/adapter/containers.go13
-rw-r--r--pkg/spec/createconfig.go8
-rw-r--r--test/e2e/checkpoint_test.go11
-rw-r--r--test/e2e/create_staticmac_test.go46
-rw-r--r--vendor/github.com/containernetworking/cni/libcni/api.go230
-rw-r--r--vendor/github.com/containernetworking/cni/libcni/conf.go4
-rw-r--r--vendor/github.com/containernetworking/cni/pkg/invoke/args.go2
-rw-r--r--vendor/github.com/containernetworking/cni/pkg/types/args.go2
-rw-r--r--vendor/github.com/containernetworking/cni/pkg/types/types.go25
-rw-r--r--vendor/github.com/containernetworking/cni/pkg/utils/utils.go51
-rw-r--r--vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go411
-rw-r--r--vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go32
-rw-r--r--vendor/modules.txt5
28 files changed, 840 insertions, 185 deletions
diff --git a/Makefile b/Makefile
index feb8e0ca3..d0ee213c0 100644
--- a/Makefile
+++ b/Makefile
@@ -522,6 +522,9 @@ vendor:
$(GO) mod vendor && \
$(GO) mod verify
+vendor-in-container:
+ podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:1.12 make vendor
+
.PHONY: \
.gopathok \
binaries \
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 58d67ddc1..780b68333 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -467,14 +467,15 @@ type RestartValues struct {
type RestoreValues struct {
PodmanCommand
- All bool
- Keep bool
- Latest bool
- TcpEstablished bool
- Import string
- Name string
- IgnoreRootfs bool
- IgnoreStaticIP bool
+ All bool
+ Keep bool
+ Latest bool
+ TcpEstablished bool
+ Import string
+ Name string
+ IgnoreRootfs bool
+ IgnoreStaticIP bool
+ IgnoreStaticMAC bool
}
type RmValues struct {
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 8afbe2e0b..3e86b8e20 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -328,7 +328,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
)
createFlags.String(
"mac-address", "",
- "Container MAC address (e.g. 92:d0:c6:0a:29:33), not currently supported",
+ "Container MAC address (e.g. 92:d0:c6:0a:29:33)",
)
createFlags.StringP(
"memory", "m", "",
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index 90d0b2dc4..caefadb6d 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -47,6 +47,7 @@ func init() {
flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
flags.BoolVar(&restoreCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
flags.BoolVar(&restoreCommand.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
+ flags.BoolVar(&restoreCommand.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
markFlagHiddenForRemoteClient("latest", flags)
}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index dc343e694..6c74b8a9b 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -336,10 +336,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
return nil, err
}
- if c.String("mac-address") != "" {
- return nil, errors.Errorf("--mac-address option not currently supported")
- }
-
imageID := ""
inputCommand = c.InputArgs[1:]
diff --git a/cni/87-podman-bridge.conflist b/cni/87-podman-bridge.conflist
index 9db416a19..a7bcf47bb 100644
--- a/cni/87-podman-bridge.conflist
+++ b/cni/87-podman-bridge.conflist
@@ -33,6 +33,9 @@
{
"type": "firewall",
"backend": "iptables"
+ },
+ {
+ "type": "tuning"
}
]
}
diff --git a/completions/bash/podman b/completions/bash/podman
index 0abf9e738..4d552b0a7 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -877,6 +877,7 @@ _podman_container_restore() {
--tcp-established
--ignore-rootfs
--ignore-static-ip
+ --ignore-static-mac
"
case "$prev" in
-i|--import)
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index 1d2cf0b3e..d71daf4af 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -76,6 +76,15 @@ a container is restored multiple times from an exported checkpoint with **--name
Using **--ignore-static-ip** tells Podman to ignore the IP address if it was configured
with **--ip** during container creation.
+**--ignore-static-mac**
+
+If the container was started with **--mac-address** the restored container also
+tries to use that MAC address and restore fails if that MAC address is already
+in use. This can happen, if a container is restored multiple times from an
+exported checkpoint with **--name, -n**.
+
+Using **--ignore-static-mac** tells Podman to ignore the MAC address if it was
+configured with **--mac-address** during container creation.
## EXAMPLE
podman container restore mywebserver
diff --git a/go.mod b/go.mod
index 9d8777e4c..8454de8ff 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
- github.com/containernetworking/cni v0.7.1
+ github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
github.com/containernetworking/plugins v0.8.2
github.com/containers/buildah v1.11.5-0.20191031204705-20e92ffe0982
github.com/containers/image/v5 v5.0.0
@@ -17,7 +17,7 @@ require (
github.com/containers/storage v1.13.5
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
- github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca
+ github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b
github.com/cyphar/filepath-securejoin v0.2.2
github.com/davecgh/go-spew v1.1.1
github.com/docker/distribution v2.7.1+incompatible
diff --git a/go.sum b/go.sum
index 891270aea..b20489688 100644
--- a/go.sum
+++ b/go.sum
@@ -53,6 +53,8 @@ github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL
github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE=
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc=
+github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/plugins v0.8.2 h1:5lnwfsAYO+V7yXhysJKy3E1A2Gy9oVut031zfdOzI9w=
github.com/containernetworking/plugins v0.8.2/go.mod h1:TxALKWZpWL79BC3GOYKJzzXr7U8R23PdhwaLp6F3adc=
github.com/containers/buildah v1.11.4-0.20191028173731-21b4778b359e h1:iDavHEx5Yr7o+0l6495Ya6N0YEPplIUZuWC2e14baDM=
@@ -83,6 +85,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca h1:CJstDqYy9ClWuPcDHMTCAiUS+ckekluYetGR2iYYWuo=
github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca/go.mod h1:BO0al9TKber3XUTucLzKgoG5sq8qiOB41H7zSdfw6r8=
+github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b h1:SgS+WV10y2Bubuy2HquSBori6DXj9sqRN77Hgs5H7Qc=
+github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b/go.mod h1:ZOuIEOp/3MB1eCBWANnNxM3zUA3NWh76wSRCsnKAg2c=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
diff --git a/libpod/container.go b/libpod/container.go
index 8e24391b9..4f7fc067e 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -138,6 +138,10 @@ type Container struct {
// being checkpointed. If requestedIP is set it will be used instead
// of config.StaticIP.
requestedIP net.IP
+ // A restored container should have the same MAC address as before
+ // being checkpointed. If requestedMAC is set it will be used instead
+ // of config.StaticMAC.
+ requestedMAC net.HardwareAddr
// This is true if a container is restored from a checkpoint.
restoreFromCheckpoint bool
@@ -296,6 +300,10 @@ type ContainerConfig struct {
// 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"`
+ // StaticMAC is a static MAC to request for the container.
+ // This cannot be set unless CreateNetNS is set.
+ // If not set, the container will be dynamically assigned a MAC by CNI.
+ StaticMAC net.HardwareAddr `json:"staticMAC"`
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true
diff --git a/libpod/container_api.go b/libpod/container_api.go
index a6f5b54d5..b8cfe02f6 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -794,6 +794,11 @@ type ContainerCheckpointOptions struct {
// important to be able to restore a container multiple
// times with '--import --name'.
IgnoreStaticIP bool
+ // IgnoreStaticMAC tells the API to ignore the MAC set
+ // during 'podman run' with '--mac-address'. This is especially
+ // important to be able to restore a container multiple
+ // times with '--import --name'.
+ IgnoreStaticMAC bool
}
// Checkpoint checkpoints a container
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 471648bc8..26d6771b0 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -794,6 +794,15 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
c.config.StaticIP = nil
}
+ // If a container is restored multiple times from an exported checkpoint with
+ // the help of '--import --name', the restore will fail if during 'podman run'
+ // a static container MAC address was set with '--mac-address'. The user
+ // can tell the restore process to ignore the static MAC with
+ // '--ignore-static-mac'
+ if options.IgnoreStaticMAC {
+ c.config.StaticMAC = nil
+ }
+
// Read network configuration from checkpoint
// Currently only one interface with one IP is supported.
networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status"))
@@ -803,9 +812,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// TODO: This implicit restoring with or without IP depending on an
// unrelated restore parameter (--name) does not seem like the
// best solution.
- if err == nil && options.Name == "" && !options.IgnoreStaticIP {
+ if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) {
// The file with the network.status does exist. Let's restore the
- // container with the same IP address as during checkpointing.
+ // container with the same IP address / MAC address as during checkpointing.
defer networkStatusFile.Close()
var networkStatus []*cnitypes.Result
networkJSON, err := ioutil.ReadAll(networkStatusFile)
@@ -815,16 +824,35 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err := json.Unmarshal(networkJSON, &networkStatus); err != nil {
return err
}
- // Take the first IP address
- var IP net.IP
- if len(networkStatus) > 0 {
- if len(networkStatus[0].IPs) > 0 {
- IP = networkStatus[0].IPs[0].Address.IP
+ if !options.IgnoreStaticIP {
+ // Take the first IP address
+ var IP net.IP
+ if len(networkStatus) > 0 {
+ if len(networkStatus[0].IPs) > 0 {
+ IP = networkStatus[0].IPs[0].Address.IP
+ }
+ }
+ if IP != nil {
+ // Tell CNI which IP address we want.
+ c.requestedIP = IP
}
}
- if IP != nil {
- // Tell CNI which IP address we want.
- c.requestedIP = IP
+ if !options.IgnoreStaticMAC {
+ // Take the first device with a defined sandbox.
+ var MAC net.HardwareAddr
+ for _, n := range networkStatus[0].Interfaces {
+ if n.Sandbox != "" {
+ MAC, err = net.ParseMAC(n.Mac)
+ if err != nil {
+ return errors.Wrapf(err, "failed to parse MAC %v", n.Mac)
+ }
+ break
+ }
+ }
+ if MAC != nil {
+ // Tell CNI which MAC address we want.
+ c.requestedMAC = MAC
+ }
}
}
@@ -1314,7 +1342,7 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
// Teardown CNI config on refresh
func (c *Container) refreshCNI() error {
// Let's try and delete any lingering network config...
- podNetwork := c.runtime.getPodNetwork(c.ID(), c.config.Name, "", c.config.Networks, c.config.PortMappings, c.config.StaticIP)
+ podNetwork := c.runtime.getPodNetwork(c.ID(), c.config.Name, "", c.config.Networks, c.config.PortMappings, c.config.StaticIP, c.config.StaticMAC)
return c.runtime.netPlugin.TearDownPod(podNetwork)
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index ac1144fbe..cba7b636a 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -28,23 +28,34 @@ import (
)
// Get an OCICNI network config
-func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork {
+func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr) ocicni.PodNetwork {
defaultNetwork := r.netPlugin.GetDefaultNetworkName()
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,
NetNS: nsPath,
- Networks: networks,
RuntimeConfig: map[string]ocicni.RuntimeConfig{
defaultNetwork: {PortMappings: ports},
},
}
- if staticIP != nil {
- network.Networks = []string{defaultNetwork}
+ if staticIP != nil || staticMAC != nil {
+ network.Networks = []ocicni.NetAttachment{{Name: defaultNetwork}}
+ var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports}
+ if staticIP != nil {
+ rt.IP = staticIP.String()
+ }
+ if staticMAC != nil {
+ rt.MAC = staticMAC.String()
+ }
network.RuntimeConfig = map[string]ocicni.RuntimeConfig{
- defaultNetwork: {IP: staticIP.String(), PortMappings: ports},
+ defaultNetwork: rt,
+ }
+ } else {
+ network.Networks = make([]ocicni.NetAttachment, len(networks))
+ for i, netName := range networks {
+ network.Networks[i].Name = netName
}
}
@@ -62,7 +73,16 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
requestedIP = ctr.config.StaticIP
}
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
+ var requestedMAC net.HardwareAddr
+ if ctr.requestedMAC != nil {
+ requestedMAC = ctr.requestedMAC
+ // cancel request for a specific MAC in case the container is reused later
+ ctr.requestedMAC = nil
+ } else {
+ requestedMAC = ctr.config.StaticMAC
+ }
+
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
results, err := r.netPlugin.SetUpPod(podNetwork)
if err != nil {
@@ -78,10 +98,10 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
networkStatus := make([]*cnitypes.Result, 0)
for idx, r := range results {
- logrus.Debugf("[%d] CNI result: %v", idx, r.String())
- resultCurrent, err := cnitypes.GetResult(r)
+ logrus.Debugf("[%d] CNI result: %v", idx, r.Result.String())
+ resultCurrent, err := cnitypes.GetResult(r.Result)
if err != nil {
- return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.String(), err)
+ return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result.String(), err)
}
networkStatus = append(networkStatus, resultCurrent)
}
@@ -443,7 +463,16 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
requestedIP = ctr.config.StaticIP
}
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
+ var requestedMAC net.HardwareAddr
+ if ctr.requestedMAC != nil {
+ requestedMAC = ctr.requestedMAC
+ // cancel request for a specific MAC in case the container is reused later
+ ctr.requestedMAC = nil
+ } else {
+ requestedMAC = ctr.config.StaticMAC
+ }
+
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
diff --git a/libpod/options.go b/libpod/options.go
index 66e8ef93c..00b5626b4 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1052,6 +1052,31 @@ func WithStaticIP(ip net.IP) CtrCreateOption {
}
}
+// WithStaticMAC indicates that the container should request a static MAC 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 WithStaticMAC(mac net.HardwareAddr) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ if !ctr.config.CreateNetNS {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if the container is not creating a network namespace")
+ }
+
+ if len(ctr.config.Networks) != 0 {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining additional CNI networks")
+ }
+
+ ctr.config.StaticMAC = mac
+
+ return nil
+ }
+}
+
// WithLogDriver sets the log driver for the container
func WithLogDriver(driver string) CtrCreateOption {
return func(ctr *Container) error {
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index a09466243..287bd8474 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -538,12 +538,13 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
)
options := libpod.ContainerCheckpointOptions{
- Keep: c.Keep,
- TCPEstablished: c.TcpEstablished,
- TargetFile: c.Import,
- Name: c.Name,
- IgnoreRootfs: c.IgnoreRootfs,
- IgnoreStaticIP: c.IgnoreStaticIP,
+ Keep: c.Keep,
+ TCPEstablished: c.TcpEstablished,
+ TargetFile: c.Import,
+ Name: c.Name,
+ IgnoreRootfs: c.IgnoreRootfs,
+ IgnoreStaticIP: c.IgnoreStaticIP,
+ IgnoreStaticMAC: c.IgnoreStaticMAC,
}
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 2a8fe7332..e054b3b13 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -396,6 +396,14 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithStaticIP(ip))
}
+ if c.MacAddress != "" {
+ mac, err := net.ParseMAC(c.MacAddress)
+ if err != nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
+ }
+ options = append(options, libpod.WithStaticMAC(mac))
+ }
+
options = append(options, libpod.WithPrivileged(c.Privileged))
useImageVolumes := c.ImageVolumeType == TypeBind
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 4b43ceb30..2d3efcbef 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -334,6 +334,10 @@ var _ = Describe("Podman checkpoint", func() {
IPBefore.WaitWithDefaultTimeout()
Expect(IPBefore.ExitCode()).To(Equal(0))
+ MACBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
+ MACBefore.WaitWithDefaultTimeout()
+ Expect(MACBefore.ExitCode()).To(Equal(0))
+
result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"})
result.WaitWithDefaultTimeout()
@@ -348,9 +352,16 @@ var _ = Describe("Podman checkpoint", func() {
IPAfter.WaitWithDefaultTimeout()
Expect(IPAfter.ExitCode()).To(Equal(0))
+ MACAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
+ MACAfter.WaitWithDefaultTimeout()
+ Expect(MACAfter.ExitCode()).To(Equal(0))
+
// Check that IP address did not change between checkpointing and restoring
Expect(IPBefore.OutputToString()).To(Equal(IPAfter.OutputToString()))
+ // Check that MAC address did not change between checkpointing and restoring
+ Expect(MACBefore.OutputToString()).To(Equal(MACAfter.OutputToString()))
+
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go
new file mode 100644
index 000000000..6460659f7
--- /dev/null
+++ b/test/e2e/create_staticmac_test.go
@@ -0,0 +1,46 @@
+// +build !remoteclient
+
+package integration
+
+import (
+ "os"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman run with --mac-address flag", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ SkipIfRootless()
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.SeedImages()
+ // Cleanup the CNI networks used by the tests
+ os.RemoveAll("/var/lib/cni/networks/podman")
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("Podman run --mac-address", func() {
+ result := podmanTest.Podman([]string{"run", "--mac-address", "92:d0:c6:0a:29:34", ALPINE, "ip", "addr"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34"))
+ })
+})
diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go
index 0f14d3427..22b111742 100644
--- a/vendor/github.com/containernetworking/cni/libcni/api.go
+++ b/vendor/github.com/containernetworking/cni/libcni/api.go
@@ -25,6 +25,7 @@ import (
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version"
)
@@ -32,6 +33,10 @@ var (
CacheDir = "/var/lib/cni"
)
+const (
+ CNICacheV1 = "cniCacheV1"
+)
+
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
// excepting the network configuration, with the nested exception that
// the `runtimeConfig` from the network configuration is included
@@ -48,7 +53,7 @@ type RuntimeConf struct {
// to the plugin
CapabilityArgs map[string]interface{}
- // A cache directory in which to library data. Defaults to CacheDir
+ // DEPRECATED. Will be removed in a future release.
CacheDir string
}
@@ -70,19 +75,22 @@ type CNI interface {
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
+ GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
+ GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}
type CNIConfig struct {
- Path []string
- exec invoke.Exec
+ Path []string
+ exec invoke.Exec
+ cacheDir string
}
// CNIConfig implements the CNI interface
@@ -92,9 +100,18 @@ var _ CNI = &CNIConfig{}
// in the given paths and use the given exec interface to run those plugins,
// or if the exec interface is not given, will use a default exec handler.
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
+ return NewCNIConfigWithCacheDir(path, "", exec)
+}
+
+// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
+// in the given paths use the given exec interface to run those plugins,
+// or if the exec interface is not given, will use a default exec handler.
+// The given cache directory will be used for temporary data storage when needed.
+func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
return &CNIConfig{
- Path: path,
- exec: exec,
+ Path: path,
+ cacheDir: cacheDir,
+ exec: exec,
}
}
@@ -165,33 +182,122 @@ func (c *CNIConfig) ensureExec() invoke.Exec {
return c.exec
}
-func getResultCacheFilePath(netName string, rt *RuntimeConf) string {
- cacheDir := rt.CacheDir
- if cacheDir == "" {
- cacheDir = CacheDir
+type cachedInfo struct {
+ Kind string `json:"kind"`
+ ContainerID string `json:"containerId"`
+ Config []byte `json:"config"`
+ IfName string `json:"ifName"`
+ NetworkName string `json:"networkName"`
+ CniArgs [][2]string `json:"cniArgs,omitempty"`
+ CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
+ RawResult map[string]interface{} `json:"result,omitempty"`
+ Result types.Result `json:"-"`
+}
+
+// getCacheDir returns the cache directory in this order:
+// 1) global cacheDir from CNIConfig object
+// 2) deprecated cacheDir from RuntimeConf object
+// 3) fall back to default cache directory
+func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
+ if c.cacheDir != "" {
+ return c.cacheDir
+ }
+ if rt.CacheDir != "" {
+ return rt.CacheDir
+ }
+ return CacheDir
+}
+
+func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
+ if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
+ return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
}
- return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
+ return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
}
-func setCachedResult(result types.Result, netName string, rt *RuntimeConf) error {
+func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
+ cached := cachedInfo{
+ Kind: CNICacheV1,
+ ContainerID: rt.ContainerID,
+ Config: config,
+ IfName: rt.IfName,
+ NetworkName: netName,
+ CniArgs: rt.Args,
+ CapabilityArgs: rt.CapabilityArgs,
+ }
+
+ // We need to get type.Result into cachedInfo as JSON map
+ // Marshal to []byte, then Unmarshal into cached.RawResult
data, err := json.Marshal(result)
if err != nil {
return err
}
- fname := getResultCacheFilePath(netName, rt)
+
+ err = json.Unmarshal(data, &cached.RawResult)
+ if err != nil {
+ return err
+ }
+
+ newBytes, err := json.Marshal(&cached)
+ if err != nil {
+ return err
+ }
+
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return err
+ }
if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
return err
}
- return ioutil.WriteFile(fname, data, 0600)
+
+ return ioutil.WriteFile(fname, newBytes, 0600)
}
-func delCachedResult(netName string, rt *RuntimeConf) error {
- fname := getResultCacheFilePath(netName, rt)
+func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ // Ignore error
+ return nil
+ }
return os.Remove(fname)
}
-func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
- fname := getResultCacheFilePath(netName, rt)
+func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ var bytes []byte
+
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, nil, err
+ }
+ bytes, err = ioutil.ReadFile(fname)
+ if err != nil {
+ // Ignore read errors; the cached result may not exist on-disk
+ return nil, nil, nil
+ }
+
+ unmarshaled := cachedInfo{}
+ if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
+ return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %v", netName, err)
+ }
+ if unmarshaled.Kind != CNICacheV1 {
+ return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
+ }
+
+ newRt := *rt
+ if unmarshaled.CniArgs != nil {
+ newRt.Args = unmarshaled.CniArgs
+ }
+ newRt.CapabilityArgs = unmarshaled.CapabilityArgs
+
+ return unmarshaled.Config, &newRt, nil
+}
+
+func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, err
+ }
data, err := ioutil.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
@@ -222,16 +328,73 @@ func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result,
return result, err
}
+func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
+ fname, err := c.getCacheFilePath(netName, rt)
+ if err != nil {
+ return nil, err
+ }
+ fdata, err := ioutil.ReadFile(fname)
+ if err != nil {
+ // Ignore read errors; the cached result may not exist on-disk
+ return nil, nil
+ }
+
+ cachedInfo := cachedInfo{}
+ if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
+ return c.getLegacyCachedResult(netName, cniVersion, rt)
+ }
+
+ newBytes, err := json.Marshal(&cachedInfo.RawResult)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal cached network %q config: %v", netName, err)
+ }
+
+ // Read the version of the cached result
+ decoder := version.ConfigDecoder{}
+ resultCniVersion, err := decoder.Decode(newBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ // Ensure we can understand the result
+ result, err := version.NewResult(resultCniVersion, newBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert to the config version to ensure plugins get prevResult
+ // in the same version as the config. The cached result version
+ // should match the config version unless the config was changed
+ // while the container was running.
+ result, err = result.GetAsVersion(cniVersion)
+ if err != nil && resultCniVersion != cniVersion {
+ return nil, fmt.Errorf("failed to convert cached result version %q to config version %q: %v", resultCniVersion, cniVersion, err)
+ }
+ return result, err
+}
+
// GetNetworkListCachedResult returns the cached Result of the previous
-// previous AddNetworkList() operation for a network list, or an error.
+// AddNetworkList() operation for a network list, or an error.
func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
- return getCachedResult(list.Name, list.CNIVersion, rt)
+ return c.getCachedResult(list.Name, list.CNIVersion, rt)
}
// GetNetworkCachedResult returns the cached Result of the previous
-// previous AddNetwork() operation for a network, or an error.
+// AddNetwork() operation for a network, or an error.
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
- return getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+ return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+}
+
+// GetNetworkListCachedConfig copies the input RuntimeConf to output
+// RuntimeConf with fields updated with info from the cached Config.
+func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ return c.getCachedConfig(list.Name, rt)
+}
+
+// GetNetworkCachedConfig copies the input RuntimeConf to output
+// RuntimeConf with fields updated with info from the cached Config.
+func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
+ return c.getCachedConfig(net.Network.Name, rt)
}
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
@@ -240,6 +403,12 @@ func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net
if err != nil {
return nil, err
}
+ if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
+ return nil, err
+ }
+ if err := utils.ValidateNetworkName(name); err != nil {
+ return nil, err
+ }
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
@@ -260,7 +429,7 @@ func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList,
}
}
- if err = setCachedResult(result, list.Name, rt); err != nil {
+ if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)
}
@@ -295,7 +464,7 @@ func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigLis
return nil
}
- cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt)
+ cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
@@ -332,7 +501,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
- cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt)
+ cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
@@ -344,7 +513,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
return err
}
}
- _ = delCachedResult(list.Name, rt)
+ _ = c.cacheDel(list.Name, rt)
return nil
}
@@ -356,7 +525,7 @@ func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
return nil, err
}
- if err = setCachedResult(result, net.Network.Name, rt); err != nil {
+ if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err)
}
@@ -372,7 +541,7 @@ func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *Ru
return fmt.Errorf("configuration version %q does not support the CHECK command", net.Network.CNIVersion)
}
- cachedResult, err := getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+ cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
@@ -387,7 +556,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
- cachedResult, err = getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
+ cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
@@ -396,7 +565,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
- _ = delCachedResult(net.Network.Name, rt)
+ _ = c.cacheDel(net.Network.Name, rt)
return nil
}
@@ -455,7 +624,8 @@ func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]
// validatePlugin checks that an individual plugin's configuration is sane
func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
- pluginPath, err := invoke.FindInPath(pluginName, c.Path)
+ c.ensureExec()
+ pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
if err != nil {
return err
}
diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go
index ea56c509d..d8920cf8c 100644
--- a/vendor/github.com/containernetworking/cni/libcni/conf.go
+++ b/vendor/github.com/containernetworking/cni/libcni/conf.go
@@ -114,11 +114,11 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
for i, conf := range plugins {
newBytes, err := json.Marshal(conf)
if err != nil {
- return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err)
+ return nil, fmt.Errorf("failed to marshal plugin config %d: %v", i, err)
}
netConf, err := ConfFromBytes(newBytes)
if err != nil {
- return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
+ return nil, fmt.Errorf("failed to parse plugin config %d: %v", i, err)
}
list.Plugins = append(list.Plugins, netConf)
}
diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
index 913528c1d..d31a44e87 100644
--- a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
+++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go
@@ -32,7 +32,7 @@ type inherited struct{}
var inheritArgsFromEnv inherited
-func (_ *inherited) AsEnv() []string {
+func (*inherited) AsEnv() []string {
return nil
}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go
index bd8640fc9..4eac64899 100644
--- a/vendor/github.com/containernetworking/cni/pkg/types/args.go
+++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go
@@ -36,7 +36,7 @@ func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
case "0", "false":
*b = false
default:
- return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
+ return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
}
return nil
}
diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go
index d0d11006a..3e185c1ce 100644
--- a/vendor/github.com/containernetworking/cni/pkg/types/types.go
+++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go
@@ -16,7 +16,6 @@ package types
import (
"encoding/json"
- "errors"
"fmt"
"io"
"net"
@@ -134,9 +133,16 @@ func (r *Route) String() string {
// Well known error codes
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
const (
- ErrUnknown uint = iota // 0
- ErrIncompatibleCNIVersion // 1
- ErrUnsupportedField // 2
+ ErrUnknown uint = iota // 0
+ ErrIncompatibleCNIVersion // 1
+ ErrUnsupportedField // 2
+ ErrUnknownContainer // 3
+ ErrInvalidEnvironmentVariables // 4
+ ErrIOFailure // 5
+ ErrDecodingFailure // 6
+ ErrInvalidNetworkConfig // 7
+ ErrTryAgainLater uint = 11
+ ErrInternal uint = 999
)
type Error struct {
@@ -145,6 +151,14 @@ type Error struct {
Details string `json:"details,omitempty"`
}
+func NewError(code uint, msg, details string) *Error {
+ return &Error{
+ Code: code,
+ Msg: msg,
+ Details: details,
+ }
+}
+
func (e *Error) Error() string {
details := ""
if e.Details != "" {
@@ -194,6 +208,3 @@ func prettyPrint(obj interface{}) error {
_, err = os.Stdout.Write(data)
return err
}
-
-// NotImplementedError is used to indicate that a method is not implemented for the given platform
-var NotImplementedError = errors.New("Not Implemented")
diff --git a/vendor/github.com/containernetworking/cni/pkg/utils/utils.go b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go
new file mode 100644
index 000000000..324c40dea
--- /dev/null
+++ b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go
@@ -0,0 +1,51 @@
+// Copyright 2019 CNI authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "regexp"
+
+ "github.com/containernetworking/cni/pkg/types"
+)
+
+// cniValidNameChars is the regexp used to validate valid characters in
+// containerID and networkName
+const cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
+
+var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`)
+
+// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters
+func ValidateContainerID(containerID string) *types.Error {
+
+ if containerID == "" {
+ return types.NewError(types.ErrUnknownContainer, "missing containerID", "")
+ }
+ if !cniReg.MatchString(containerID) {
+ return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID)
+ }
+ return nil
+}
+
+// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters
+func ValidateNetworkName(networkName string) *types.Error {
+
+ if networkName == "" {
+ return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "")
+ }
+ if !cniReg.MatchString(networkName) {
+ return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName)
+ }
+ return nil
+}
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 8743abc56..0cdbf14b7 100644
--- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go
+++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go
@@ -2,11 +2,14 @@ package ocicni
import (
"context"
+ "encoding/json"
"errors"
"fmt"
+ "io/ioutil"
"net"
"os"
"path"
+ "path/filepath"
"sort"
"strings"
"sync"
@@ -21,10 +24,11 @@ import (
)
type cniNetworkPlugin struct {
+ cniConfig *libcni.CNIConfig
loNetwork *cniNetwork
sync.RWMutex
- defaultNetName string
+ defaultNetName netName
networks map[string]*cniNetwork
nsManager *nsManager
@@ -47,11 +51,15 @@ type cniNetworkPlugin struct {
cacheDir string
}
+type netName struct {
+ name string
+ changeable bool
+}
+
type cniNetwork struct {
- name string
- filePath string
- NetworkConfig *libcni.NetworkConfigList
- CNIConfig *libcni.CNIConfig
+ name string
+ filePath string
+ config *libcni.NetworkConfigList
}
var errMissingDefaultNetwork = errors.New("Missing CNI default network")
@@ -186,6 +194,8 @@ func (plugin *cniNetworkPlugin) monitorConfDir(start *sync.WaitGroup) {
// If defaultNetName is not empty, a CNI config with that network name will
// be used as the default CNI network, and container network operations will
// fail until that network config is present and valid.
+// If defaultNetName is empty, CNI config files should be reloaded real-time and
+// defaultNetName should be changeable and determined by file sorting.
func InitCNI(defaultNetName string, confDir string, binDirs ...string) (CNIPlugin, error) {
return initCNI(nil, "", defaultNetName, confDir, binDirs...)
}
@@ -198,17 +208,24 @@ func initCNI(exec cniinvoke.Exec, cacheDir, defaultNetName string, confDir strin
if len(binDirs) == 0 {
binDirs = []string{DefaultBinDir}
}
+
plugin := &cniNetworkPlugin{
- defaultNetName: defaultNetName,
- networks: make(map[string]*cniNetwork),
- loNetwork: getLoNetwork(exec, binDirs),
- confDir: confDir,
- binDirs: binDirs,
- shutdownChan: make(chan struct{}),
- done: &sync.WaitGroup{},
- pods: make(map[string]*podLock),
- exec: exec,
- cacheDir: cacheDir,
+ cniConfig: libcni.NewCNIConfig(binDirs, exec),
+ defaultNetName: netName{
+ name: defaultNetName,
+ // If defaultNetName is not assigned in initialization,
+ // it should be changeable
+ changeable: defaultNetName == "",
+ },
+ networks: make(map[string]*cniNetwork),
+ loNetwork: getLoNetwork(),
+ confDir: confDir,
+ binDirs: binDirs,
+ shutdownChan: make(chan struct{}),
+ done: &sync.WaitGroup{},
+ pods: make(map[string]*podLock),
+ exec: exec,
+ cacheDir: cacheDir,
}
if exec == nil {
@@ -246,7 +263,7 @@ func (plugin *cniNetworkPlugin) Shutdown() error {
return nil
}
-func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[string]*cniNetwork, string, error) {
+func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork, string, error) {
files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"})
if err != nil {
return nil, "", err
@@ -284,17 +301,30 @@ func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[st
logrus.Warningf("CNI config list %s has no networks, skipping", confFile)
continue
}
+
+ // Validation on CNI config should be done to pre-check presence
+ // of plugins which are necessary.
+ if _, err := cni.ValidateNetworkList(context.TODO(), confList); err != nil {
+ logrus.Warningf("Error validating CNI config file %s: %v", confFile, err)
+ continue
+ }
+
if confList.Name == "" {
confList.Name = path.Base(confFile)
}
+ cniNet := &cniNetwork{
+ name: confList.Name,
+ filePath: confFile,
+ config: confList,
+ }
+
logrus.Infof("Found CNI network %s (type=%v) at %s", confList.Name, confList.Plugins[0].Network.Type, confFile)
- networks[confList.Name] = &cniNetwork{
- name: confList.Name,
- filePath: confFile,
- NetworkConfig: confList,
- CNIConfig: libcni.NewCNIConfig(binDirs, exec),
+ if _, ok := networks[confList.Name]; !ok {
+ networks[confList.Name] = cniNet
+ } else {
+ logrus.Infof("Ignore CNI network %s (type=%v) at %s because already exists", confList.Name, confList.Plugins[0].Network.Type, confFile)
}
if defaultNetName == "" {
@@ -305,39 +335,49 @@ func loadNetworks(exec cniinvoke.Exec, confDir string, binDirs []string) (map[st
return networks, defaultNetName, nil
}
-func getLoNetwork(exec cniinvoke.Exec, binDirs []string) *cniNetwork {
- loConfig, err := libcni.ConfListFromBytes([]byte(`{
- "cniVersion": "0.2.0",
- "name": "cni-loopback",
+const (
+ loIfname string = "lo"
+ loNetname string = "cni-loopback"
+)
+
+func getLoNetwork() *cniNetwork {
+ loConfig, err := libcni.ConfListFromBytes([]byte(fmt.Sprintf(`{
+ "cniVersion": "0.3.1",
+ "name": "%s",
"plugins": [{
"type": "loopback"
}]
-}`))
+}`, loNetname)))
if err != nil {
// The hardcoded config above should always be valid and unit tests will
// catch this
panic(err)
}
loNetwork := &cniNetwork{
- name: "lo",
- NetworkConfig: loConfig,
- CNIConfig: libcni.NewCNIConfig(binDirs, exec),
+ name: loIfname,
+ config: loConfig,
}
return loNetwork
}
func (plugin *cniNetworkPlugin) syncNetworkConfig() error {
- networks, defaultNetName, err := loadNetworks(plugin.exec, plugin.confDir, plugin.binDirs)
+ networks, defaultNetName, err := loadNetworks(plugin.confDir, plugin.cniConfig)
if err != nil {
return err
}
plugin.Lock()
defer plugin.Unlock()
- if plugin.defaultNetName == "" {
- plugin.defaultNetName = defaultNetName
+
+ // Update defaultNetName if it is changeable
+ if plugin.defaultNetName.changeable {
+ plugin.defaultNetName.name = defaultNetName
+ logrus.Infof("Update default CNI network name to %s", defaultNetName)
+ } else {
+ logrus.Warnf("Default CNI network name %s is unchangeable", plugin.defaultNetName.name)
}
+
plugin.networks = networks
return nil
@@ -356,7 +396,7 @@ func (plugin *cniNetworkPlugin) getNetwork(name string) (*cniNetwork, error) {
func (plugin *cniNetworkPlugin) GetDefaultNetworkName() string {
plugin.RLock()
defer plugin.RUnlock()
- return plugin.defaultNetName
+ return plugin.defaultNetName.name
}
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
@@ -382,27 +422,120 @@ func (plugin *cniNetworkPlugin) Name() string {
return CNIPluginName
}
-func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork, RuntimeConfig) error) error {
+func (plugin *cniNetworkPlugin) loadNetworkFromCache(name string, rt *libcni.RuntimeConf) (*cniNetwork, *libcni.RuntimeConf, error) {
+ cniNet := &cniNetwork{
+ name: name,
+ config: &libcni.NetworkConfigList{
+ Name: name,
+ },
+ }
+
+ var confBytes []byte
+ var err error
+ confBytes, rt, err = plugin.cniConfig.GetNetworkListCachedConfig(cniNet.config, rt)
+ if err != nil {
+ return nil, nil, err
+ } else if confBytes == nil {
+ return nil, nil, fmt.Errorf("network %q not found in CNI cache", name)
+ }
+
+ cniNet.config, err = libcni.ConfListFromBytes(confBytes)
+ if err != nil {
+ // Might be a plain NetworkConfig
+ netConf, err := libcni.ConfFromBytes(confBytes)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Up-convert to a NetworkConfigList
+ cniNet.config, err = libcni.ConfListFromConf(netConf)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ return cniNet, rt, nil
+}
+
+type forEachNetworkFn func(*cniNetwork, *PodNetwork, *libcni.RuntimeConf) error
+
+func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, fromCache bool, actionFn forEachNetworkFn) error {
networks := podNetwork.Networks
if len(networks) == 0 {
- networks = append(networks, plugin.GetDefaultNetworkName())
+ networks = append(networks, NetAttachment{
+ Name: plugin.GetDefaultNetworkName(),
+ })
}
- for i, netName := range networks {
- // Interface names start at "eth0" and count up for each network
- ifName := fmt.Sprintf("eth%d", i)
- network, err := plugin.getNetwork(netName)
+
+ allIfNames := make(map[string]bool)
+ for _, req := range networks {
+ if req.Ifname != "" {
+ // Make sure the requested name isn't already assigned
+ if allIfNames[req.Ifname] {
+ return fmt.Errorf("network %q requested interface name %q already assigned", req.Name, req.Ifname)
+ }
+ allIfNames[req.Ifname] = true
+ }
+ }
+
+ for _, network := range networks {
+ ifName := network.Ifname
+ if ifName == "" {
+ for i := 0; i < 10000; i++ {
+ candidate := fmt.Sprintf("eth%d", i)
+ if !allIfNames[candidate] {
+ allIfNames[candidate] = true
+ ifName = candidate
+ break
+ }
+ }
+ if ifName == "" {
+ return fmt.Errorf("failed to find free interface name for network %q", network.Name)
+ }
+ }
+
+ rt, err := buildCNIRuntimeConf(plugin.cacheDir, podNetwork, ifName, podNetwork.RuntimeConfig[network.Name])
if err != nil {
- logrus.Errorf(err.Error())
+ logrus.Errorf("error building CNI runtime config: %v", err)
return err
}
- if err := forEachFunc(network, ifName, podNetwork, podNetwork.RuntimeConfig[netName]); err != nil {
+
+ var cniNet *cniNetwork
+ if fromCache {
+ var newRt *libcni.RuntimeConf
+ cniNet, newRt, err = plugin.loadNetworkFromCache(network.Name, rt)
+ if err != nil {
+ logrus.Errorf("error loading cached network config: %v", err)
+ // fall back to loading from existing plugins on disk
+ } else {
+ // Use the updated RuntimeConf
+ rt = newRt
+ }
+ }
+ if cniNet == nil {
+ cniNet, err = plugin.getNetwork(network.Name)
+ if err != nil {
+ logrus.Errorf(err.Error())
+ return err
+ }
+ }
+
+ if err := actionFn(cniNet, podNetwork, rt); err != nil {
return err
}
}
return nil
}
-func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Result, error) {
+func buildLoopbackRuntimeConf(cacheDir string, podNetwork *PodNetwork) *libcni.RuntimeConf {
+ return &libcni.RuntimeConf{
+ ContainerID: podNetwork.ID,
+ NetNS: podNetwork.NetNS,
+ CacheDir: cacheDir,
+ IfName: loIfname,
+ }
+}
+
+func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]NetResult, error) {
if err := plugin.networksAvailable(&podNetwork); err != nil {
return nil, err
}
@@ -410,20 +543,26 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
- _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", RuntimeConfig{})
- if err != nil {
+ loRt := buildLoopbackRuntimeConf(plugin.cacheDir, &podNetwork)
+ if _, err := plugin.loNetwork.addToNetwork(loRt, plugin.cniConfig); err != nil {
logrus.Errorf("Error while adding to cni lo network: %s", err)
return nil, err
}
- results := make([]cnitypes.Result, 0)
- if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error {
- result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig)
+ results := make([]NetResult, 0)
+ if err := plugin.forEachNetwork(&podNetwork, false, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
+ result, err := network.addToNetwork(rt, plugin.cniConfig)
if err != nil {
logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err)
return err
}
- results = append(results, result)
+ results = append(results, NetResult{
+ Result: result,
+ NetAttachment: NetAttachment{
+ Name: network.name,
+ Ifname: rt.IfName,
+ },
+ })
return nil
}); err != nil {
return nil, err
@@ -432,16 +571,99 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu
return results, nil
}
+func (plugin *cniNetworkPlugin) getCachedNetworkInfo(containerID string) ([]NetAttachment, error) {
+ cacheDir := libcni.CacheDir
+ if plugin.cacheDir != "" {
+ cacheDir = plugin.cacheDir
+ }
+
+ dirPath := filepath.Join(cacheDir, "results")
+ entries, err := ioutil.ReadDir(dirPath)
+ if err != nil {
+ return nil, err
+ }
+
+ fileNames := make([]string, 0, len(entries))
+ for _, e := range entries {
+ fileNames = append(fileNames, e.Name())
+ }
+ sort.Strings(fileNames)
+
+ attachments := []NetAttachment{}
+ for _, fname := range fileNames {
+ part := fmt.Sprintf("-%s-", containerID)
+ pos := strings.Index(fname, part)
+ if pos <= 0 || pos+len(part) >= len(fname) {
+ continue
+ }
+
+ cacheFile := filepath.Join(dirPath, fname)
+ bytes, err := ioutil.ReadFile(cacheFile)
+ if err != nil {
+ logrus.Warningf("failed to read CNI cache file %s: %v", cacheFile, err)
+ continue
+ }
+
+ cachedInfo := struct {
+ Kind string `json:"kind"`
+ IfName string `json:"ifName"`
+ ContainerID string `json:"containerID"`
+ NetName string `json:"networkName"`
+ }{}
+
+ if err := json.Unmarshal(bytes, &cachedInfo); err != nil {
+ logrus.Warningf("failed to unmarshal CNI cache file %s: %v", cacheFile, err)
+ continue
+ }
+ if cachedInfo.Kind != libcni.CNICacheV1 {
+ logrus.Warningf("unknown CNI cache file %s kind %q", cacheFile, cachedInfo.Kind)
+ continue
+ }
+ if cachedInfo.ContainerID != containerID {
+ continue
+ }
+ // Ignore the loopback interface; it's handled separately
+ if cachedInfo.IfName == loIfname && cachedInfo.NetName == loNetname {
+ continue
+ }
+ if cachedInfo.IfName == "" || cachedInfo.NetName == "" {
+ logrus.Warningf("missing CNI cache file %s ifname %q or netname %q", cacheFile, cachedInfo.IfName, cachedInfo.NetName)
+ continue
+ }
+
+ attachments = append(attachments, NetAttachment{
+ Name: cachedInfo.NetName,
+ Ifname: cachedInfo.IfName,
+ })
+ }
+ return attachments, nil
+}
+
+// TearDownPod tears down pod networks. Prefers cached pod attachment information
+// but falls back to given network attachment information.
func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error {
+ if len(podNetwork.Networks) == 0 {
+ attachments, err := plugin.getCachedNetworkInfo(podNetwork.ID)
+ if err == nil && len(attachments) > 0 {
+ podNetwork.Networks = attachments
+ }
+ }
+
if err := plugin.networksAvailable(&podNetwork); err != nil {
return err
}
+ loRt := buildLoopbackRuntimeConf(plugin.cacheDir, &podNetwork)
+ if err := plugin.loNetwork.deleteFromNetwork(loRt, plugin.cniConfig); err != nil {
+ logrus.Errorf("Error while removing pod from CNI lo network: %v", err)
+ // Loopback teardown errors are not fatal
+ }
+
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
- return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error {
- if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig); err != nil {
+ return plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
+ if err := network.deleteFromNetwork(rt, plugin.cniConfig); err != nil {
logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err)
return err
}
@@ -451,19 +673,25 @@ func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error {
// GetPodNetworkStatus returns IP addressing and interface details for all
// networks attached to the pod.
-func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cnitypes.Result, error) {
+func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]NetResult, error) {
plugin.podLock(podNetwork).Lock()
defer plugin.podUnlock(podNetwork)
- results := make([]cnitypes.Result, 0)
- if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error {
- result, err := network.checkNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig, plugin.nsManager)
+ results := make([]NetResult, 0)
+ if err := plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error {
+ result, err := network.checkNetwork(rt, plugin.cniConfig, plugin.nsManager, podNetwork.NetNS)
if err != nil {
logrus.Errorf("Error while checking pod to CNI network %q: %s", network.name, err)
return err
}
if result != nil {
- results = append(results, result)
+ results = append(results, NetResult{
+ Result: result,
+ NetAttachment: NetAttachment{
+ Name: network.name,
+ Ifname: rt.IfName,
+ },
+ })
}
return nil
}); err != nil {
@@ -473,16 +701,9 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn
return results, nil
}
-func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (cnitypes.Result, error) {
- rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig)
- if err != nil {
- logrus.Errorf("Error adding network: %v", err)
- return nil, err
- }
-
- netconf, cninet := network.NetworkConfig, network.CNIConfig
- logrus.Infof("About to add CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type)
- res, err := cninet.AddNetworkList(context.Background(), netconf, rt)
+func (network *cniNetwork) addToNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig) (cnitypes.Result, error) {
+ logrus.Infof("About to add CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
+ res, err := cni.AddNetworkList(context.Background(), network.config, rt)
if err != nil {
logrus.Errorf("Error adding network: %v", err)
return nil, err
@@ -491,18 +712,10 @@ func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork,
return res, nil
}
-func (network *cniNetwork) checkNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig, nsManager *nsManager) (cnitypes.Result, error) {
-
- rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig)
- if err != nil {
- logrus.Errorf("Error checking network: %v", err)
- return nil, err
- }
-
- netconf, cninet := network.NetworkConfig, network.CNIConfig
- logrus.Infof("About to check CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type)
+func (network *cniNetwork) checkNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig, nsManager *nsManager, netns string) (cnitypes.Result, error) {
+ logrus.Infof("About to check CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
- gtet, err := cniversion.GreaterThanOrEqualTo(netconf.CNIVersion, "0.4.0")
+ gtet, err := cniversion.GreaterThanOrEqualTo(network.config.CNIVersion, "0.4.0")
if err != nil {
return nil, err
}
@@ -511,15 +724,15 @@ func (network *cniNetwork) checkNetwork(cacheDir string, podNetwork *PodNetwork,
// When CNIVersion supports Check, use it. Otherwise fall back on what was done initially.
if gtet {
- err = cninet.CheckNetworkList(context.Background(), netconf, rt)
- logrus.Infof("Checking CNI network %s (config version=%v)", netconf.Name, netconf.CNIVersion)
+ err = cni.CheckNetworkList(context.Background(), network.config, rt)
+ logrus.Infof("Checking CNI network %s (config version=%v)", network.name, network.config.CNIVersion)
if err != nil {
logrus.Errorf("Error checking network: %v", err)
return nil, err
}
}
- result, err = cninet.GetNetworkListCachedResult(netconf, rt)
+ result, err = cni.GetNetworkListCachedResult(network.config, rt)
if err != nil {
logrus.Errorf("Error GetNetworkListCachedResult: %v", err)
return nil, err
@@ -528,19 +741,19 @@ func (network *cniNetwork) checkNetwork(cacheDir string, podNetwork *PodNetwork,
}
// result doesn't exist, create one
- logrus.Infof("Checking CNI network %s (config version=%v) nsManager=%v", netconf.Name, netconf.CNIVersion, nsManager)
+ logrus.Infof("Checking CNI network %s (config version=%v) nsManager=%v", network.name, network.config.CNIVersion, nsManager)
var cniInterface *cnicurrent.Interface
ips := []*cnicurrent.IPConfig{}
errs := []error{}
for _, version := range []string{"4", "6"} {
- ip, mac, err := getContainerDetails(nsManager, podNetwork.NetNS, ifName, "-"+version)
+ ip, mac, err := getContainerDetails(nsManager, netns, rt.IfName, "-"+version)
if err == nil {
if cniInterface == nil {
cniInterface = &cnicurrent.Interface{
- Name: ifName,
+ Name: rt.IfName,
Mac: mac.String(),
- Sandbox: podNetwork.NetNS,
+ Sandbox: netns,
}
}
ips = append(ips, &cnicurrent.IPConfig{
@@ -557,25 +770,23 @@ func (network *cniNetwork) checkNetwork(cacheDir string, podNetwork *PodNetwork,
}
result = &cnicurrent.Result{
- CNIVersion: netconf.CNIVersion,
+ CNIVersion: network.config.CNIVersion,
Interfaces: []*cnicurrent.Interface{cniInterface},
IPs: ips,
}
- return result, nil
-}
-
-func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) error {
- rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig)
+ // Result must be the same CNIVersion as the CNI config
+ converted, err := result.GetAsVersion(network.config.CNIVersion)
if err != nil {
- logrus.Errorf("Error deleting network: %v", err)
- return err
+ return nil, err
}
- netconf, cninet := network.NetworkConfig, network.CNIConfig
- logrus.Infof("About to del CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type)
- err = cninet.DelNetworkList(context.Background(), netconf, rt)
- if err != nil {
+ return converted, nil
+}
+
+func (network *cniNetwork) deleteFromNetwork(rt *libcni.RuntimeConf, cni *libcni.CNIConfig) error {
+ logrus.Infof("About to del CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type)
+ if err := cni.DelNetworkList(context.Background(), network.config, rt); err != nil {
logrus.Errorf("Error deleting network: %v", err)
return err
}
@@ -608,6 +819,16 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string,
rt.Args = append(rt.Args, [2]string{"IP", ip})
}
+ // Add the requested static MAC to CNI_ARGS
+ mac := runtimeConfig.MAC
+ if mac != "" {
+ _, err := net.ParseMAC(mac)
+ if err != nil {
+ return nil, fmt.Errorf("unable to parse MAC address %q: %v", mac, err)
+ }
+ rt.Args = append(rt.Args, [2]string{"MAC", mac})
+ }
+
// Set PortMappings in Capabilities
if len(runtimeConfig.PortMappings) != 0 {
rt.CapabilityArgs["portMappings"] = runtimeConfig.PortMappings
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 8709711e0..717ecda33 100644
--- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go
+++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go
@@ -44,6 +44,9 @@ type RuntimeConfig struct {
// with the hostlocal IP allocator. If left unset, an IP will be
// dynamically allocated.
IP string
+ // MAC is a static MAC address to be assigned to the network interface.
+ // If left unset, a MAC will be dynamically allocated.
+ MAC string
// PortMappings is the port mapping of the sandbox.
PortMappings []PortMapping
// Bandwidth is the bandwidth limiting of the pod
@@ -75,9 +78,10 @@ type PodNetwork struct {
// NetNS is the network namespace path of the sandbox.
NetNS string
- // 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
+ // Networks is a list of CNI network names (and optional interface
+ // names) to attach to the sandbox. Leave this list empty to attach the
+ // default network to the sandbox
+ Networks []NetAttachment
// NetworkConfig is configuration specific to a single CNI network.
// It is optional, and can be omitted for some or all specified networks
@@ -85,6 +89,24 @@ type PodNetwork struct {
RuntimeConfig map[string]RuntimeConfig
}
+// NetAttachment describes a container network attachment
+type NetAttachment struct {
+ // NetName contains the name of the CNI network to which the container
+ // should be or is attached
+ Name string
+ // Ifname contains the optional interface name of the attachment
+ Ifname string
+}
+
+// NetResult contains the result the network attachment operation
+type NetResult struct {
+ // Result is the CNI Result
+ Result types.Result
+ // NetAttachment contains the network and interface names of this
+ // network attachment
+ NetAttachment
+}
+
// CNIPlugin is the interface that needs to be implemented by a plugin
type CNIPlugin interface {
// Name returns the plugin's name. This will be used when searching
@@ -98,13 +120,13 @@ type CNIPlugin interface {
// 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.
- SetUpPod(network PodNetwork) ([]types.Result, error)
+ SetUpPod(network PodNetwork) ([]NetResult, error)
// TearDownPod is the method called before a pod's sandbox container will be deleted
TearDownPod(network PodNetwork) error
// Status is the method called to obtain the ipv4 or ipv6 addresses of the pod sandbox
- GetPodNetworkStatus(network PodNetwork) ([]types.Result, error)
+ GetPodNetworkStatus(network PodNetwork) ([]NetResult, error)
// NetworkStatus returns error if the network plugin is in error state
Status() error
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 465e3c5cc..138c546c2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -42,13 +42,14 @@ github.com/containerd/containerd/errdefs
github.com/containerd/continuity/fs
github.com/containerd/continuity/sysx
github.com/containerd/continuity/syscallx
-# github.com/containernetworking/cni v0.7.1
+# github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
github.com/containernetworking/cni/pkg/types
github.com/containernetworking/cni/pkg/types/current
github.com/containernetworking/cni/pkg/version
github.com/containernetworking/cni/libcni
github.com/containernetworking/cni/pkg/invoke
github.com/containernetworking/cni/pkg/types/020
+github.com/containernetworking/cni/pkg/utils
# github.com/containernetworking/plugins v0.8.2
github.com/containernetworking/plugins/pkg/ns
github.com/containernetworking/plugins/pkg/ip
@@ -169,7 +170,7 @@ github.com/coreos/go-systemd/sdjournal
github.com/coreos/go-systemd/journal
# github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f
github.com/coreos/pkg/dlopen
-# github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca
+# github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b
github.com/cri-o/ocicni/pkg/ocicni
# github.com/cyphar/filepath-securejoin v0.2.2
github.com/cyphar/filepath-securejoin