add cni networking support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>docker-19.03
parent
21034f4234
commit
bc7a6f2556
|
@ -58,6 +58,12 @@ type GCConfig struct {
|
||||||
GCPolicy []GCPolicy `toml:"gcpolicy"`
|
GCPolicy []GCPolicy `toml:"gcpolicy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NetworkConfig struct {
|
||||||
|
Mode string `toml:"networkMode"`
|
||||||
|
CNIConfigPath string `toml:"cniConfigPath"`
|
||||||
|
CNIBinaryPath string `toml:"cniBinaryPath"`
|
||||||
|
}
|
||||||
|
|
||||||
type OCIConfig struct {
|
type OCIConfig struct {
|
||||||
Enabled *bool `toml:"enabled"`
|
Enabled *bool `toml:"enabled"`
|
||||||
Labels map[string]string `toml:"labels"`
|
Labels map[string]string `toml:"labels"`
|
||||||
|
@ -66,6 +72,7 @@ type OCIConfig struct {
|
||||||
Rootless bool `toml:"rootless"`
|
Rootless bool `toml:"rootless"`
|
||||||
NoProcessSandbox bool `toml:"noProcessSandbox"`
|
NoProcessSandbox bool `toml:"noProcessSandbox"`
|
||||||
GCConfig
|
GCConfig
|
||||||
|
NetworkConfig
|
||||||
// UserRemapUnsupported is unsupported key for testing. The feature is
|
// UserRemapUnsupported is unsupported key for testing. The feature is
|
||||||
// incomplete and the intention is to make it default without config.
|
// incomplete and the intention is to make it default without config.
|
||||||
UserRemapUnsupported string `toml:"userRemapUnsupported"`
|
UserRemapUnsupported string `toml:"userRemapUnsupported"`
|
||||||
|
@ -78,6 +85,7 @@ type ContainerdConfig struct {
|
||||||
Platforms []string `toml:"platforms"`
|
Platforms []string `toml:"platforms"`
|
||||||
Namespace string `toml:"namespace"`
|
Namespace string `toml:"namespace"`
|
||||||
GCConfig
|
GCConfig
|
||||||
|
NetworkConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type GCPolicy struct {
|
type GCPolicy struct {
|
||||||
|
|
|
@ -350,6 +350,19 @@ func defaultConf() (config.Config, *toml.MetaData, error) {
|
||||||
return cfg, md, nil
|
return cfg, md, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDefaultNetworkConfig(nc config.NetworkConfig) config.NetworkConfig {
|
||||||
|
if nc.Mode == "" {
|
||||||
|
nc.Mode = "auto"
|
||||||
|
}
|
||||||
|
if nc.CNIConfigPath == "" {
|
||||||
|
nc.CNIConfigPath = "/etc/buildkit/cni.json"
|
||||||
|
}
|
||||||
|
if nc.CNIBinaryPath == "" {
|
||||||
|
nc.CNIBinaryPath = "/opt/cni/bin"
|
||||||
|
}
|
||||||
|
return nc
|
||||||
|
}
|
||||||
|
|
||||||
func setDefaultConfig(cfg *config.Config) {
|
func setDefaultConfig(cfg *config.Config) {
|
||||||
orig := *cfg
|
orig := *cfg
|
||||||
|
|
||||||
|
@ -368,6 +381,9 @@ func setDefaultConfig(cfg *config.Config) {
|
||||||
cfg.Workers.Containerd.Platforms = binfmt_misc.SupportedPlatforms()
|
cfg.Workers.Containerd.Platforms = binfmt_misc.SupportedPlatforms()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.Workers.OCI.NetworkConfig = setDefaultNetworkConfig(cfg.Workers.OCI.NetworkConfig)
|
||||||
|
cfg.Workers.Containerd.NetworkConfig = setDefaultNetworkConfig(cfg.Workers.Containerd.NetworkConfig)
|
||||||
|
|
||||||
if system.RunningInUserNS() {
|
if system.RunningInUserNS() {
|
||||||
// if buildkitd is being executed as the mapped-root (not only EUID==0 but also $USER==root)
|
// if buildkitd is being executed as the mapped-root (not only EUID==0 but also $USER==root)
|
||||||
// in a user namespace, we need to enable the rootless mode but
|
// in a user namespace, we need to enable the rootless mode but
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
ctd "github.com/containerd/containerd"
|
ctd "github.com/containerd/containerd"
|
||||||
"github.com/moby/buildkit/cmd/buildkitd/config"
|
"github.com/moby/buildkit/cmd/buildkitd/config"
|
||||||
|
"github.com/moby/buildkit/util/network"
|
||||||
"github.com/moby/buildkit/worker"
|
"github.com/moby/buildkit/worker"
|
||||||
"github.com/moby/buildkit/worker/base"
|
"github.com/moby/buildkit/worker/base"
|
||||||
"github.com/moby/buildkit/worker/containerd"
|
"github.com/moby/buildkit/worker/containerd"
|
||||||
|
@ -69,6 +70,21 @@ func init() {
|
||||||
Value: defaultConf.Workers.Containerd.Namespace,
|
Value: defaultConf.Workers.Containerd.Namespace,
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "containerd-worker-net",
|
||||||
|
Usage: "worker network type (auto, cni or host)",
|
||||||
|
Value: defaultConf.Workers.Containerd.NetworkConfig.Mode,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "containerd-cni-config-path",
|
||||||
|
Usage: "path of cni config file",
|
||||||
|
Value: defaultConf.Workers.Containerd.NetworkConfig.CNIConfigPath,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "containerd-cni-binary-dir",
|
||||||
|
Usage: "path of cni binary files",
|
||||||
|
Value: defaultConf.Workers.Containerd.NetworkConfig.CNIBinaryPath,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultConf.Workers.Containerd.GC == nil || *defaultConf.Workers.Containerd.GC {
|
if defaultConf.Workers.Containerd.GC == nil || *defaultConf.Workers.Containerd.GC {
|
||||||
|
@ -158,6 +174,16 @@ func applyContainerdFlags(c *cli.Context, cfg *config.Config) error {
|
||||||
cfg.Workers.Containerd.GCKeepStorage = c.GlobalInt64("containerd-worker-gc-keepstorage") * 1e6
|
cfg.Workers.Containerd.GCKeepStorage = c.GlobalInt64("containerd-worker-gc-keepstorage") * 1e6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.GlobalIsSet("containerd-worker-net") {
|
||||||
|
cfg.Workers.Containerd.NetworkConfig.Mode = c.GlobalString("containerd-worker-net")
|
||||||
|
}
|
||||||
|
if c.GlobalIsSet("containerd-cni-config-path") {
|
||||||
|
cfg.Workers.Containerd.NetworkConfig.CNIConfigPath = c.GlobalString("containerd-cni-worker-path")
|
||||||
|
}
|
||||||
|
if c.GlobalIsSet("containerd-cni-binary-dir") {
|
||||||
|
cfg.Workers.Containerd.NetworkConfig.CNIBinaryPath = c.GlobalString("containerd-cni-binary-dir")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +200,14 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
|
||||||
|
|
||||||
dns := getDNSConfig(common.config.DNS)
|
dns := getDNSConfig(common.config.DNS)
|
||||||
|
|
||||||
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Namespace, cfg.Labels, dns, ctd.WithTimeout(60*time.Second))
|
nc := network.Opt{
|
||||||
|
Root: common.config.Root,
|
||||||
|
Mode: common.config.Workers.Containerd.NetworkConfig.Mode,
|
||||||
|
CNIConfigPath: common.config.Workers.Containerd.CNIConfigPath,
|
||||||
|
CNIBinaryDir: common.config.Workers.Containerd.CNIBinaryPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, ctd.DefaultSnapshotter, cfg.Namespace, cfg.Labels, dns, nc, ctd.WithTimeout(60*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/containerd/containerd/snapshots/overlay"
|
"github.com/containerd/containerd/snapshots/overlay"
|
||||||
"github.com/moby/buildkit/cmd/buildkitd/config"
|
"github.com/moby/buildkit/cmd/buildkitd/config"
|
||||||
"github.com/moby/buildkit/executor/oci"
|
"github.com/moby/buildkit/executor/oci"
|
||||||
|
"github.com/moby/buildkit/util/network"
|
||||||
"github.com/moby/buildkit/worker"
|
"github.com/moby/buildkit/worker"
|
||||||
"github.com/moby/buildkit/worker/base"
|
"github.com/moby/buildkit/worker/base"
|
||||||
"github.com/moby/buildkit/worker/runc"
|
"github.com/moby/buildkit/worker/runc"
|
||||||
|
@ -53,6 +54,21 @@ func init() {
|
||||||
Name: "oci-worker-platform",
|
Name: "oci-worker-platform",
|
||||||
Usage: "override supported platforms for worker",
|
Usage: "override supported platforms for worker",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "oci-worker-net",
|
||||||
|
Usage: "worker network type (auto, cni or host)",
|
||||||
|
Value: defaultConf.Workers.OCI.NetworkConfig.Mode,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "oci-cni-config-path",
|
||||||
|
Usage: "path of cni config file",
|
||||||
|
Value: defaultConf.Workers.OCI.NetworkConfig.CNIConfigPath,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "oci-cni-binary-dir",
|
||||||
|
Usage: "path of cni binary files",
|
||||||
|
Value: defaultConf.Workers.OCI.NetworkConfig.CNIBinaryPath,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
n := "oci-worker-rootless"
|
n := "oci-worker-rootless"
|
||||||
u := "enable rootless mode"
|
u := "enable rootless mode"
|
||||||
|
@ -154,6 +170,15 @@ func applyOCIFlags(c *cli.Context, cfg *config.Config) error {
|
||||||
cfg.Workers.OCI.GCKeepStorage = c.GlobalInt64("oci-worker-gc-keepstorage") * 1e6
|
cfg.Workers.OCI.GCKeepStorage = c.GlobalInt64("oci-worker-gc-keepstorage") * 1e6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.GlobalIsSet("oci-worker-net") {
|
||||||
|
cfg.Workers.OCI.NetworkConfig.Mode = c.GlobalString("oci-worker-net")
|
||||||
|
}
|
||||||
|
if c.GlobalIsSet("oci-cni-config-path") {
|
||||||
|
cfg.Workers.OCI.NetworkConfig.CNIConfigPath = c.GlobalString("oci-cni-worker-path")
|
||||||
|
}
|
||||||
|
if c.GlobalIsSet("oci-cni-binary-dir") {
|
||||||
|
cfg.Workers.OCI.NetworkConfig.CNIBinaryPath = c.GlobalString("oci-cni-binary-dir")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +219,14 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
|
||||||
|
|
||||||
dns := getDNSConfig(common.config.DNS)
|
dns := getDNSConfig(common.config.DNS)
|
||||||
|
|
||||||
opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, dns)
|
nc := network.Opt{
|
||||||
|
Root: common.config.Root,
|
||||||
|
Mode: common.config.Workers.OCI.NetworkConfig.Mode,
|
||||||
|
CNIConfigPath: common.config.Workers.OCI.CNIConfigPath,
|
||||||
|
CNIBinaryDir: common.config.Workers.OCI.CNIBinaryPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/moby/buildkit/util/network/netns_create"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
netns_create.Handle()
|
||||||
|
|
||||||
syscall.Umask(0)
|
syscall.Umask(0)
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -12,9 +12,11 @@ require (
|
||||||
github.com/containerd/containerd v1.3.0-0.20190426060238-3a3f0aac8819
|
github.com/containerd/containerd v1.3.0-0.20190426060238-3a3f0aac8819
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc
|
||||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
|
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
|
||||||
|
github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645
|
||||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
|
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
|
||||||
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 // indirect
|
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 // indirect
|
||||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd // indirect
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd // indirect
|
||||||
|
github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c // indirect
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||||
github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0
|
github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -23,12 +23,16 @@ github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVl
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
|
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
|
||||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||||
|
github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645 h1:CCxW+4asjsbhMjWAznv/rTLYYI7Mcm6LovkzaPtD3rU=
|
||||||
|
github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645/go.mod h1:2wlRxCQdiBY+OcjNg5x8kI+5mEL1fGt25L4IzQHYJsM=
|
||||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 h1:esQOJREg8nw8aXj6uCN5dfW5cKUBiEJ/+nni1Q/D/sw=
|
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 h1:esQOJREg8nw8aXj6uCN5dfW5cKUBiEJ/+nni1Q/D/sw=
|
||||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||||
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 h1:SKDlsIhYxNE1LO0xwuOR+3QWj3zRibVQu5jWIMQmOfU=
|
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7 h1:SKDlsIhYxNE1LO0xwuOR+3QWj3zRibVQu5jWIMQmOfU=
|
||||||
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
|
||||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||||
|
github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c h1:X6Gxg2GV1i0UhDodKZYrp//lg8h3KANe8l3gtFHoi9M=
|
||||||
|
github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -51,6 +55,7 @@ github.com/docker/go-units v0.3.1 h1:QAFdsA6jLCnglbqE6mUsHuPcJlntY94DkxHf4deHKIU
|
||||||
github.com/docker/go-units v0.3.1/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.1/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e h1:/9dBUVUO865jROD5LfE232z9ssWlBlzIMVW0BaEn8DM=
|
github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e h1:/9dBUVUO865jROD5LfE232z9ssWlBlzIMVW0BaEn8DM=
|
||||||
github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
github.com/docker/libnetwork v0.8.0-dev.2.0.20190604151032-3c26b4e7495e/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
|
@ -77,6 +82,7 @@ github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880 h1:OaRuzt9oCK
|
||||||
github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.0.0-20160207214719-a0d98a5f2880/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c h1:nQcv325vxv2fFHJsOt53eSRf1eINt6vOdYUFfXs4rgk=
|
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c h1:nQcv325vxv2fFHJsOt53eSRf1eINt6vOdYUFfXs4rgk=
|
||||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
|
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8=
|
github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8=
|
||||||
github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
|
github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
|
||||||
|
@ -92,7 +98,9 @@ github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
@ -119,6 +127,7 @@ github.com/sirupsen/logrus v1.0.3/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
|
||||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
@ -175,9 +184,12 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
|
|
@ -198,9 +198,17 @@ VOLUME /var/lib/containerd
|
||||||
VOLUME /run/containerd
|
VOLUME /run/containerd
|
||||||
ENTRYPOINT ["containerd"]
|
ENTRYPOINT ["containerd"]
|
||||||
|
|
||||||
|
FROM alpine AS cni-plugins
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
ENV CNI_VERSION=v0.8.1
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
WORKDIR /opt/cni/bin
|
||||||
|
RUN curl -Ls https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-$TARGETOS-$TARGETARCH-$CNI_VERSION.tgz | tar xzv
|
||||||
|
|
||||||
FROM buildkit-base AS integration-tests
|
FROM buildkit-base AS integration-tests
|
||||||
ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
|
ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
|
||||||
RUN apt-get install -y --no-install-recommends uidmap sudo \
|
RUN apt-get install -y --no-install-recommends uidmap sudo vim iptables \
|
||||||
&& useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \
|
&& useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \
|
||||||
&& echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \
|
&& echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \
|
||||||
&& mkdir -m 0700 -p /run/user/1000 \
|
&& mkdir -m 0700 -p /run/user/1000 \
|
||||||
|
@ -212,9 +220,14 @@ COPY --from=containerd10 /out/containerd* /opt/containerd-1.0/bin/
|
||||||
COPY --from=registry /bin/registry /usr/bin
|
COPY --from=registry /bin/registry /usr/bin
|
||||||
COPY --from=runc /usr/bin/runc /usr/bin
|
COPY --from=runc /usr/bin/runc /usr/bin
|
||||||
COPY --from=containerd /out/containerd* /usr/bin/
|
COPY --from=containerd /out/containerd* /usr/bin/
|
||||||
|
COPY --from=cni-plugins /opt/cni/bin/bridge /opt/cni/bin/host-local /opt/cni/bin
|
||||||
|
COPY hack/fixtures/cni.json /etc/buildkit/cni.json
|
||||||
COPY --from=binaries / /usr/bin/
|
COPY --from=binaries / /usr/bin/
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
FROM integration-tests AS dev-env
|
||||||
|
VOLUME /var/lib/buildkit
|
||||||
|
|
||||||
# To allow running buildkit in a container without CAP_SYS_ADMIN, we need to do either
|
# To allow running buildkit in a container without CAP_SYS_ADMIN, we need to do either
|
||||||
# a) install newuidmap/newgidmap with file capabilities rather than SETUID (requires kernel >= 4.14)
|
# a) install newuidmap/newgidmap with file capabilities rather than SETUID (requires kernel >= 4.14)
|
||||||
# b) install newuidmap/newgidmap >= 20181125 (59c2dabb264ef7b3137f5edb52c0b31d5af0cf76)
|
# b) install newuidmap/newgidmap >= 20181125 (59c2dabb264ef7b3137f5edb52c0b31d5af0cf76)
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/oci"
|
||||||
|
"github.com/containerd/go-cni"
|
||||||
|
"github.com/moby/buildkit/identity"
|
||||||
|
"github.com/moby/buildkit/util/network/netns_create"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCNIProvider(opt Opt) (Provider, error) {
|
||||||
|
if _, err := os.Stat(opt.CNIConfigPath); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read cni config %q", opt.CNIConfigPath)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(opt.CNIBinaryDir); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read cni binary dir %q", opt.CNIBinaryDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
cniHandle, err := cni.New(
|
||||||
|
cni.WithMinNetworkCount(2),
|
||||||
|
cni.WithConfFile(opt.CNIConfigPath),
|
||||||
|
cni.WithPluginDir([]string{opt.CNIBinaryDir}),
|
||||||
|
cni.WithLoNetwork,
|
||||||
|
cni.WithInterfacePrefix(("eth")))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cniProvider{CNI: cniHandle, root: opt.Root}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cniProvider struct {
|
||||||
|
cni.CNI
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cniProvider) New() (Namespace, error) {
|
||||||
|
id := identity.NewID()
|
||||||
|
nsPath := filepath.Join(c.root, "net/cni", id)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(nsPath), 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := netns_create.CreateNetNS(nsPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.CNI.Setup(id, nsPath); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "CNI setup error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cniNS{path: nsPath, id: id, handle: c.CNI}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cniNS struct {
|
||||||
|
handle cni.CNI
|
||||||
|
id string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *cniNS) Set(s *specs.Spec) {
|
||||||
|
oci.WithLinuxNamespace(specs.LinuxNamespace{
|
||||||
|
Type: specs.NetworkNamespace,
|
||||||
|
Path: ns.path,
|
||||||
|
})(nil, nil, nil, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *cniNS) Close() error {
|
||||||
|
err := ns.handle.Remove(ns.id, ns.path)
|
||||||
|
|
||||||
|
if err1 := unix.Unmount(ns.path, unix.MNT_DETACH); err1 != nil {
|
||||||
|
if err1 != syscall.EINVAL && err1 != syscall.ENOENT && err == nil {
|
||||||
|
err = errors.Wrap(err1, "error unmounting network namespace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err1 := os.RemoveAll(filepath.Dir(ns.path)); err1 != nil && !os.IsNotExist(err1) && err == nil {
|
||||||
|
err = errors.Wrap(err, "error removing network namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package netns_create
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const envKey = "BUILDKIT_CREATE_NS_PATH"
|
||||||
|
|
||||||
|
func Handle() {
|
||||||
|
if path := os.Getenv(envKey); path != "" {
|
||||||
|
if err := handle(path); err != nil {
|
||||||
|
log.Printf("%v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(path string) error {
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := unix.Mount("/proc/self/ns/net", path, "none", unix.MS_BIND, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateNetNS(path string) error {
|
||||||
|
cmd := exec.Command("/proc/self/exe")
|
||||||
|
cmd.Env = []string{envKey + "=" + path}
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Cloneflags: unix.CLONE_NEWNET,
|
||||||
|
}
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, string(out))
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -2,19 +2,51 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/moby/buildkit/solver/pb"
|
"github.com/moby/buildkit/solver/pb"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default returns the default network provider set
|
type Opt struct {
|
||||||
func Default() map[pb.NetMode]Provider {
|
Root string
|
||||||
|
Mode string
|
||||||
|
CNIConfigPath string
|
||||||
|
CNIBinaryDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providers returns the network provider set
|
||||||
|
func Providers(opt Opt) (map[pb.NetMode]Provider, error) {
|
||||||
|
var defaultProvider Provider
|
||||||
|
switch opt.Mode {
|
||||||
|
case "cni":
|
||||||
|
cniProvider, err := NewCNIProvider(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultProvider = cniProvider
|
||||||
|
case "host":
|
||||||
|
defaultProvider = NewHostProvider()
|
||||||
|
case "auto", "":
|
||||||
|
if _, err := os.Stat(opt.CNIConfigPath); err == nil {
|
||||||
|
cniProvider, err := NewCNIProvider(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultProvider = cniProvider
|
||||||
|
} else {
|
||||||
|
defaultProvider = NewHostProvider()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("invalid network mode: %q", opt.Mode)
|
||||||
|
}
|
||||||
|
|
||||||
return map[pb.NetMode]Provider{
|
return map[pb.NetMode]Provider{
|
||||||
// FIXME: still uses host if no provider configured
|
pb.NetMode_UNSET: defaultProvider,
|
||||||
pb.NetMode_UNSET: NewHostProvider(),
|
|
||||||
pb.NetMode_HOST: NewHostProvider(),
|
pb.NetMode_HOST: NewHostProvider(),
|
||||||
pb.NetMode_NONE: NewNoneProvider(),
|
pb.NetMode_NONE: NewNoneProvider(),
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider interface for Network
|
// Provider interface for Network
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.10.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
go_import_path: github.com/containerd/go-cni
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -d
|
||||||
|
- go get -u github.com/vbatts/git-validation
|
||||||
|
- go get -u github.com/kunalkushwaha/ltag
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- pushd ..; git clone https://github.com/containerd/project; popd
|
||||||
|
|
||||||
|
script:
|
||||||
|
- DCO_VERBOSITY=-q ../project/script/validate/dco
|
||||||
|
- ../project/script/validate/fileheader ../project/
|
||||||
|
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,60 @@
|
||||||
|
[![Build Status](https://travis-ci.org/containerd/go-cni.svg?branch=master)](https://travis-ci.org/containerd/go-cni)
|
||||||
|
|
||||||
|
# go-cni
|
||||||
|
|
||||||
|
A generic CNI library to provide APIs for CNI plugin interactions. The library provides APIs to:
|
||||||
|
|
||||||
|
- Load CNI network config from different sources
|
||||||
|
- Setup networks for container namespace
|
||||||
|
- Remove networks from container namespace
|
||||||
|
- Query status of CNI network plugin initialization
|
||||||
|
|
||||||
|
go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
id := "123456"
|
||||||
|
netns := "/proc/9999/ns/net"
|
||||||
|
defaultIfName := "eth0"
|
||||||
|
// Initialize library
|
||||||
|
l = gocni.New(gocni.WithMinNetworkCount(2),
|
||||||
|
gocni.WithPluginConfDir("/etc/mycni/net.d"),
|
||||||
|
gocni.WithPluginDir([]string{"/opt/mycni/bin", "/opt/cni/bin"}),
|
||||||
|
gocni.WithDefaultIfName(defaultIfName))
|
||||||
|
|
||||||
|
// Load the cni configuration
|
||||||
|
err:= l.Load(gocni.WithLoNetwork, gocni.WithDefaultConf)
|
||||||
|
if err != nil{
|
||||||
|
log.Errorf("failed to load cni configuration: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup network for namespace.
|
||||||
|
labels := map[string]string{
|
||||||
|
"K8S_POD_NAMESPACE": "namespace1",
|
||||||
|
"K8S_POD_NAME": "pod1",
|
||||||
|
"K8S_POD_INFRA_CONTAINER_ID": id,
|
||||||
|
}
|
||||||
|
result, err := l.Setup(id, netns, gocni.WithLabels(labels))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to setup network for namespace %q: %v",id, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IP of the default interface
|
||||||
|
IP := result.Interfaces[defaultIfName].IPConfigs[0].IP.String()
|
||||||
|
fmt.Printf("IP of the default interface %s:%s", defaultIfName, IP)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project details
|
||||||
|
|
||||||
|
The go-cni is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
||||||
|
As a containerd sub-project, you will find the:
|
||||||
|
|
||||||
|
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
|
||||||
|
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
|
||||||
|
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
information in our [`containerd/project`](https://github.com/containerd/project) repository.
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
cnilibrary "github.com/containernetworking/cni/libcni"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CNI interface {
|
||||||
|
// Setup setup the network for the namespace
|
||||||
|
Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
|
||||||
|
// Remove tears down the network of the namespace.
|
||||||
|
Remove(id string, path string, opts ...NamespaceOpts) error
|
||||||
|
// Load loads the cni network config
|
||||||
|
Load(opts ...CNIOpt) error
|
||||||
|
// Status checks the status of the cni initialization
|
||||||
|
Status() error
|
||||||
|
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
|
||||||
|
GetConfig() *ConfigResult
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigResult struct {
|
||||||
|
PluginDirs []string
|
||||||
|
PluginConfDir string
|
||||||
|
PluginMaxConfNum int
|
||||||
|
Prefix string
|
||||||
|
Networks []*ConfNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfNetwork struct {
|
||||||
|
Config *NetworkConfList
|
||||||
|
IFName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkConfList is a source bytes to string version of cnilibrary.NetworkConfigList
|
||||||
|
type NetworkConfList struct {
|
||||||
|
Name string
|
||||||
|
CNIVersion string
|
||||||
|
Plugins []*NetworkConf
|
||||||
|
Source string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkConf is a source bytes to string conversion of cnilibrary.NetworkConfig
|
||||||
|
type NetworkConf struct {
|
||||||
|
Network *types.NetConf
|
||||||
|
Source string
|
||||||
|
}
|
||||||
|
|
||||||
|
type libcni struct {
|
||||||
|
config
|
||||||
|
|
||||||
|
cniConfig cnilibrary.CNI
|
||||||
|
networkCount int // minimum network plugin configurations needed to initialize cni
|
||||||
|
networks []*Network
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultCNIConfig() *libcni {
|
||||||
|
return &libcni{
|
||||||
|
config: config{
|
||||||
|
pluginDirs: []string{DefaultCNIDir},
|
||||||
|
pluginConfDir: DefaultNetDir,
|
||||||
|
pluginMaxConfNum: DefaultMaxConfNum,
|
||||||
|
prefix: DefaultPrefix,
|
||||||
|
},
|
||||||
|
cniConfig: &cnilibrary.CNIConfig{
|
||||||
|
Path: []string{DefaultCNIDir},
|
||||||
|
},
|
||||||
|
networkCount: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new libcni instance.
|
||||||
|
func New(config ...CNIOpt) (CNI, error) {
|
||||||
|
cni := defaultCNIConfig()
|
||||||
|
var err error
|
||||||
|
for _, c := range config {
|
||||||
|
if err = c(cni); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cni, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads the latest config from cni config files.
|
||||||
|
func (c *libcni) Load(opts ...CNIOpt) error {
|
||||||
|
var err error
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
// Reset the networks on a load operation to ensure
|
||||||
|
// config happens on a clean slate
|
||||||
|
c.reset()
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
if err = o(c); err != nil {
|
||||||
|
return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns the status of CNI initialization.
|
||||||
|
func (c *libcni) Status() error {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
if len(c.networks) < c.networkCount {
|
||||||
|
return ErrCNINotInitialized
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Networks returns all the configured networks.
|
||||||
|
// NOTE: Caller MUST NOT modify anything in the returned array.
|
||||||
|
func (c *libcni) Networks() []*Network {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
return append([]*Network{}, c.networks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup setups the network in the namespace
|
||||||
|
func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
|
||||||
|
if err := c.Status(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ns, err := newNamespace(id, path, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var results []*current.Result
|
||||||
|
for _, network := range c.Networks() {
|
||||||
|
r, err := network.Attach(ns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, r)
|
||||||
|
}
|
||||||
|
return c.GetCNIResultFromResults(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the network config from the namespace
|
||||||
|
func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
|
||||||
|
if err := c.Status(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ns, err := newNamespace(id, path, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, network := range c.Networks() {
|
||||||
|
if err := network.Remove(ns); err != nil {
|
||||||
|
// Based on CNI spec v0.7.0, empty network namespace is allowed to
|
||||||
|
// do best effort cleanup. However, it is not handled consistently
|
||||||
|
// right now:
|
||||||
|
// https://github.com/containernetworking/plugins/issues/210
|
||||||
|
// TODO(random-liu): Remove the error handling when the issue is
|
||||||
|
// fixed and the CNI spec v0.6.0 support is deprecated.
|
||||||
|
if path == "" && strings.Contains(err.Error(), "no such file or directory") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
|
||||||
|
func (c *libcni) GetConfig() *ConfigResult {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
r := &ConfigResult{
|
||||||
|
PluginDirs: c.config.pluginDirs,
|
||||||
|
PluginConfDir: c.config.pluginConfDir,
|
||||||
|
PluginMaxConfNum: c.config.pluginMaxConfNum,
|
||||||
|
Prefix: c.config.prefix,
|
||||||
|
}
|
||||||
|
for _, network := range c.networks {
|
||||||
|
conf := &NetworkConfList{
|
||||||
|
Name: network.config.Name,
|
||||||
|
CNIVersion: network.config.CNIVersion,
|
||||||
|
Source: string(network.config.Bytes),
|
||||||
|
}
|
||||||
|
for _, plugin := range network.config.Plugins {
|
||||||
|
conf.Plugins = append(conf.Plugins, &NetworkConf{
|
||||||
|
Network: plugin.Network,
|
||||||
|
Source: string(plugin.Bytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
r.Networks = append(r.Networks, &ConfNetwork{
|
||||||
|
Config: conf,
|
||||||
|
IFName: network.ifName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *libcni) reset() {
|
||||||
|
c.networks = nil
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCNINotInitialized = errors.New("cni plugin not initialized")
|
||||||
|
ErrInvalidConfig = errors.New("invalid cni config")
|
||||||
|
ErrNotFound = errors.New("not found")
|
||||||
|
ErrRead = errors.New("failed to read config file")
|
||||||
|
ErrInvalidResult = errors.New("invalid result")
|
||||||
|
ErrLoad = errors.New("failed to load cni config")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsCNINotInitialized returns true if the error is due to cni config not being initialized
|
||||||
|
func IsCNINotInitialized(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrCNINotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidConfig returns true if the error is invalid cni config
|
||||||
|
func IsInvalidConfig(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFound returns true if the error is due to a missing config or result
|
||||||
|
func IsNotFound(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReadFailure return true if the error is a config read failure
|
||||||
|
func IsReadFailure(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrRead
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidResult return true if the error is due to invalid cni result
|
||||||
|
func IsInvalidResult(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrInvalidResult
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error {
|
||||||
|
if ipConf == nil {
|
||||||
|
return fmt.Errorf("invalid IP configuration")
|
||||||
|
}
|
||||||
|
if ipConf.Interface != nil && *ipConf.Interface > ifs {
|
||||||
|
return fmt.Errorf("invalid IP configuration with invalid interface %d", *ipConf.Interface)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIfName(prefix string, i int) string {
|
||||||
|
return fmt.Sprintf("%s%d", prefix, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultInterface(prefix string) string {
|
||||||
|
return getIfName(prefix, 0)
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
cnilibrary "github.com/containernetworking/cni/libcni"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
cni cnilibrary.CNI
|
||||||
|
config *cnilibrary.NetworkConfigList
|
||||||
|
ifName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Network) Attach(ns *Namespace) (*current.Result, error) {
|
||||||
|
r, err := n.cni.AddNetworkList(n.config, ns.config(n.ifName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return current.NewResultFromResult(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Network) Remove(ns *Namespace) error {
|
||||||
|
return n.cni.DelNetworkList(n.config, ns.config(n.ifName))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Namespace struct {
|
||||||
|
id string
|
||||||
|
path string
|
||||||
|
capabilityArgs map[string]interface{}
|
||||||
|
args map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNamespace(id, path string, opts ...NamespaceOpts) (*Namespace, error) {
|
||||||
|
ns := &Namespace{
|
||||||
|
id: id,
|
||||||
|
path: path,
|
||||||
|
capabilityArgs: make(map[string]interface{}),
|
||||||
|
args: make(map[string]string),
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
if err := o(ns); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *Namespace) config(ifName string) *cnilibrary.RuntimeConf {
|
||||||
|
c := &cnilibrary.RuntimeConf{
|
||||||
|
ContainerID: ns.id,
|
||||||
|
NetNS: ns.path,
|
||||||
|
IfName: ifName,
|
||||||
|
}
|
||||||
|
for k, v := range ns.args {
|
||||||
|
c.Args = append(c.Args, [2]string{k, v})
|
||||||
|
}
|
||||||
|
c.CapabilityArgs = ns.capabilityArgs
|
||||||
|
return c
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
type NamespaceOpts func(s *Namespace) error
|
||||||
|
|
||||||
|
// Capabilities
|
||||||
|
func WithCapabilityPortMap(portMapping []PortMapping) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
c.capabilityArgs["portMappings"] = portMapping
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCapabilityIPRanges(ipRanges []IPRanges) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
c.capabilityArgs["ipRanges"] = ipRanges
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCapabilityBandWitdh adds support for traffic shaping:
|
||||||
|
// https://github.com/heptio/cni-plugins/tree/master/plugins/meta/bandwidth
|
||||||
|
func WithCapabilityBandWidth(bandWidth BandWidth) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
c.capabilityArgs["bandwidth"] = bandWidth
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCapability(name string, capability interface{}) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
c.capabilityArgs[name] = capability
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args
|
||||||
|
func WithLabels(labels map[string]string) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
for k, v := range labels {
|
||||||
|
c.args[k] = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithArgs(k, v string) NamespaceOpts {
|
||||||
|
return func(c *Namespace) error {
|
||||||
|
c.args[k] = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
cnilibrary "github.com/containernetworking/cni/libcni"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CNIOpt func(c *libcni) error
|
||||||
|
|
||||||
|
// WithInterfacePrefix sets the prefix for network interfaces
|
||||||
|
// e.g. eth or wlan
|
||||||
|
func WithInterfacePrefix(prefix string) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
c.prefix = prefix
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPluginDir can be used to set the locations of
|
||||||
|
// the cni plugin binaries
|
||||||
|
func WithPluginDir(dirs []string) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
c.pluginDirs = dirs
|
||||||
|
c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPluginConfDir can be used to configure the
|
||||||
|
// cni configuration directory.
|
||||||
|
func WithPluginConfDir(dir string) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
c.pluginConfDir = dir
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPluginMaxConfNum can be used to configure the
|
||||||
|
// max cni plugin config file num.
|
||||||
|
func WithPluginMaxConfNum(max int) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
c.pluginMaxConfNum = max
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMinNetworkCount can be used to configure the
|
||||||
|
// minimum networks to be configured and initialized
|
||||||
|
// for the status to report success. By default its 1.
|
||||||
|
func WithMinNetworkCount(count int) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
c.networkCount = count
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLoNetwork can be used to load the loopback
|
||||||
|
// network config.
|
||||||
|
func WithLoNetwork(c *libcni) error {
|
||||||
|
loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
|
||||||
|
"cniVersion": "0.3.1",
|
||||||
|
"name": "cni-loopback",
|
||||||
|
"plugins": [{
|
||||||
|
"type": "loopback"
|
||||||
|
}]
|
||||||
|
}`))
|
||||||
|
|
||||||
|
c.networks = append(c.networks, &Network{
|
||||||
|
cni: c.cniConfig,
|
||||||
|
config: loConfig,
|
||||||
|
ifName: "lo",
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConf can be used to load config directly
|
||||||
|
// from byte.
|
||||||
|
func WithConf(bytes []byte) CNIOpt {
|
||||||
|
return WithConfIndex(bytes, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConfIndex can be used to load config directly
|
||||||
|
// from byte and set the interface name's index.
|
||||||
|
func WithConfIndex(bytes []byte, index int) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
conf, err := cnilibrary.ConfFromBytes(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
confList, err := cnilibrary.ConfListFromConf(conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.networks = append(c.networks, &Network{
|
||||||
|
cni: c.cniConfig,
|
||||||
|
config: confList,
|
||||||
|
ifName: getIfName(c.prefix, index),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConfFile can be used to load network config
|
||||||
|
// from an .conf file. Supported with absolute fileName
|
||||||
|
// with path only.
|
||||||
|
func WithConfFile(fileName string) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
conf, err := cnilibrary.ConfFromFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// upconvert to conf list
|
||||||
|
confList, err := cnilibrary.ConfListFromConf(conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.networks = append(c.networks, &Network{
|
||||||
|
cni: c.cniConfig,
|
||||||
|
config: confList,
|
||||||
|
ifName: getIfName(c.prefix, 0),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConfListFile can be used to load network config
|
||||||
|
// from an .conflist file. Supported with absolute fileName
|
||||||
|
// with path only.
|
||||||
|
func WithConfListFile(fileName string) CNIOpt {
|
||||||
|
return func(c *libcni) error {
|
||||||
|
confList, err := cnilibrary.ConfListFromFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i := len(c.networks)
|
||||||
|
c.networks = append(c.networks, &Network{
|
||||||
|
cni: c.cniConfig,
|
||||||
|
config: confList,
|
||||||
|
ifName: getIfName(c.prefix, i),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDefaultConf can be used to detect the default network
|
||||||
|
// config file from the configured cni config directory and load
|
||||||
|
// it.
|
||||||
|
// Since the CNI spec does not specify a way to detect default networks,
|
||||||
|
// the convention chosen is - the first network configuration in the sorted
|
||||||
|
// list of network conf files as the default network.
|
||||||
|
func WithDefaultConf(c *libcni) error {
|
||||||
|
return loadFromConfDir(c, c.pluginMaxConfNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAllConf can be used to detect all network config
|
||||||
|
// files from the configured cni config directory and load
|
||||||
|
// them.
|
||||||
|
func WithAllConf(c *libcni) error {
|
||||||
|
return loadFromConfDir(c, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadFromConfDir detects network config files from the
|
||||||
|
// configured cni config directory and load them. max is
|
||||||
|
// the maximum network config to load (max i<= 0 means no limit).
|
||||||
|
func loadFromConfDir(c *libcni, max int) error {
|
||||||
|
files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
|
||||||
|
case len(files) == 0:
|
||||||
|
return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// files contains the network config files associated with cni network.
|
||||||
|
// Use lexicographical way as a defined order for network config files.
|
||||||
|
sort.Strings(files)
|
||||||
|
// Since the CNI spec does not specify a way to detect default networks,
|
||||||
|
// the convention chosen is - the first network configuration in the sorted
|
||||||
|
// list of network conf files as the default network and choose the default
|
||||||
|
// interface provided during init as the network interface for this default
|
||||||
|
// network. For every other network use a generated interface id.
|
||||||
|
i := 0
|
||||||
|
var networks []*Network
|
||||||
|
for _, confFile := range files {
|
||||||
|
var confList *cnilibrary.NetworkConfigList
|
||||||
|
if strings.HasSuffix(confFile, ".conflist") {
|
||||||
|
confList, err = cnilibrary.ConfListFromFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conf, err := cnilibrary.ConfFromFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
|
||||||
|
}
|
||||||
|
// Ensure the config has a "type" so we know what plugin to run.
|
||||||
|
// Also catches the case where somebody put a conflist into a conf file.
|
||||||
|
if conf.Network.Type == "" {
|
||||||
|
return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
confList, err = cnilibrary.ConfListFromConf(conf)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to list: %v", confFile, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(confList.Plugins) == 0 {
|
||||||
|
return errors.Wrapf(ErrInvalidConfig, "CNI config list %s has no networks, skipping", confFile)
|
||||||
|
|
||||||
|
}
|
||||||
|
networks = append(networks, &Network{
|
||||||
|
cni: c.cniConfig,
|
||||||
|
config: confList,
|
||||||
|
ifName: getIfName(c.prefix, i),
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
if i == max {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(networks) == 0 {
|
||||||
|
return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
|
||||||
|
}
|
||||||
|
c.networks = append(c.networks, networks...)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IPConfig struct {
|
||||||
|
IP net.IP
|
||||||
|
Gateway net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
type CNIResult struct {
|
||||||
|
Interfaces map[string]*Config
|
||||||
|
DNS []types.DNS
|
||||||
|
Routes []*types.Route
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
IPConfigs []*IPConfig
|
||||||
|
Mac string
|
||||||
|
Sandbox string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCNIResultFromResults returns a structured data containing the
|
||||||
|
// interface configuration for each of the interfaces created in the namespace.
|
||||||
|
// Conforms with
|
||||||
|
// Result:
|
||||||
|
// a) Interfaces list. Depending on the plugin, this can include the sandbox
|
||||||
|
// (eg, container or hypervisor) interface name and/or the host interface
|
||||||
|
// name, the hardware addresses of each interface, and details about the
|
||||||
|
// sandbox (if any) the interface is in.
|
||||||
|
// b) IP configuration assigned to each interface. The IPv4 and/or IPv6 addresses,
|
||||||
|
// gateways, and routes assigned to sandbox and/or host interfaces.
|
||||||
|
// c) DNS information. Dictionary that includes DNS information for nameservers,
|
||||||
|
// domain, search domains and options.
|
||||||
|
func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, error) {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
r := &CNIResult{
|
||||||
|
Interfaces: make(map[string]*Config),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugins may not need to return Interfaces in result if
|
||||||
|
// if there are no multiple interfaces created. In that case
|
||||||
|
// all configs should be applied against default interface
|
||||||
|
r.Interfaces[defaultInterface(c.prefix)] = &Config{}
|
||||||
|
|
||||||
|
// Walk through all the results
|
||||||
|
for _, result := range results {
|
||||||
|
// Walk through all the interface in each result
|
||||||
|
for _, intf := range result.Interfaces {
|
||||||
|
r.Interfaces[intf.Name] = &Config{
|
||||||
|
Mac: intf.Mac,
|
||||||
|
Sandbox: intf.Sandbox,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Walk through all the IPs in the result and attach it to corresponding
|
||||||
|
// interfaces
|
||||||
|
for _, ipConf := range result.IPs {
|
||||||
|
if err := validateInterfaceConfig(ipConf, len(result.Interfaces)); err != nil {
|
||||||
|
return nil, errors.Wrapf(ErrInvalidResult, "failed to valid interface config: %v", err)
|
||||||
|
}
|
||||||
|
name := c.getInterfaceName(result.Interfaces, ipConf)
|
||||||
|
r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs,
|
||||||
|
&IPConfig{IP: ipConf.Address.IP, Gateway: ipConf.Gateway})
|
||||||
|
}
|
||||||
|
r.DNS = append(r.DNS, result.DNS)
|
||||||
|
r.Routes = append(r.Routes, result.Routes...)
|
||||||
|
}
|
||||||
|
if _, ok := r.Interfaces[defaultInterface(c.prefix)]; !ok {
|
||||||
|
return nil, errors.Wrapf(ErrNotFound, "default network not found")
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInterfaceName returns the interface name if the plugins
|
||||||
|
// return the result with associated interfaces. If interface
|
||||||
|
// is not present then default interface name is used
|
||||||
|
func (c *libcni) getInterfaceName(interfaces []*current.Interface,
|
||||||
|
ipConf *current.IPConfig) string {
|
||||||
|
if ipConf.Interface != nil {
|
||||||
|
return interfaces[*ipConf.Interface].Name
|
||||||
|
}
|
||||||
|
return defaultInterface(c.prefix)
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeTmpDir(prefix string) (string, error) {
|
||||||
|
tmpDir, err := ioutil.TempDir(os.TempDir(), prefix)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tmpDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFakeCNIConfig(t *testing.T) (string, string) {
|
||||||
|
cniDir, err := makeTmpDir("fakecni")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create plugin config dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cniConfDir := path.Join(cniDir, "net.d")
|
||||||
|
err = os.MkdirAll(cniConfDir, 0777)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create network config dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkConfig1 := path.Join(cniConfDir, "mocknetwork1.conf")
|
||||||
|
f1, err := os.Create(networkConfig1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create network config %v: %v", f1, err)
|
||||||
|
}
|
||||||
|
networkConfig2 := path.Join(cniConfDir, "mocknetwork2.conf")
|
||||||
|
f2, err := os.Create(networkConfig2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create network config %v: %v", f2, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg1 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin1", "fakecni")
|
||||||
|
_, err = f1.WriteString(cfg1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to write network config file %v: %v", f1, err)
|
||||||
|
}
|
||||||
|
f1.Close()
|
||||||
|
cfg2 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin2", "fakecni")
|
||||||
|
_, err = f2.WriteString(cfg2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to write network config file %v: %v", f2, err)
|
||||||
|
}
|
||||||
|
f2.Close()
|
||||||
|
return cniDir, cniConfDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func tearDownCNIConfig(t *testing.T, confDir string) {
|
||||||
|
err := os.RemoveAll(confDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to cleanup CNI configs: %v", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright The containerd 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 cni
|
||||||
|
|
||||||
|
const (
|
||||||
|
CNIPluginName = "cni"
|
||||||
|
DefaultNetDir = "/etc/cni/net.d"
|
||||||
|
DefaultCNIDir = "/opt/cni/bin"
|
||||||
|
DefaultMaxConfNum = 1
|
||||||
|
VendorCNIDirTemplate = "%s/opt/%s/bin"
|
||||||
|
DefaultPrefix = "eth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
pluginDirs []string
|
||||||
|
pluginConfDir string
|
||||||
|
pluginMaxConfNum int
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortMapping struct {
|
||||||
|
HostPort int32
|
||||||
|
ContainerPort int32
|
||||||
|
Protocol string
|
||||||
|
HostIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPRanges struct {
|
||||||
|
Subnet string
|
||||||
|
RangeStart string
|
||||||
|
RangeEnd string
|
||||||
|
Gateway string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BandWidth defines the ingress/egress rate and burst limits
|
||||||
|
type BandWidth struct {
|
||||||
|
IngressRate uint64
|
||||||
|
IngressBurst uint64
|
||||||
|
EgressRate uint64
|
||||||
|
EgressBurst uint64
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
github.com/stretchr/testify b89eecf5ca5db6d3ba60b237ffe3df7bafb7662f
|
||||||
|
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
|
||||||
|
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
|
||||||
|
github.com/stretchr/objx 8a3f7159479fbc75b30357fbc48f380b7320f08e
|
||||||
|
github.com/containernetworking/cni 142cde0c766cd6055cc7fdfdcb44579c0c9c35bf
|
||||||
|
github.com/pkg/errors v0.8.0
|
|
@ -0,0 +1,202 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
// Copyright 2015 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 libcni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RuntimeConf struct {
|
||||||
|
ContainerID string
|
||||||
|
NetNS string
|
||||||
|
IfName string
|
||||||
|
Args [][2]string
|
||||||
|
// A dictionary of capability-specific data passed by the runtime
|
||||||
|
// to plugins as top-level keys in the 'runtimeConfig' dictionary
|
||||||
|
// of the plugin's stdin data. libcni will ensure that only keys
|
||||||
|
// in this map which match the capabilities of the plugin are passed
|
||||||
|
// to the plugin
|
||||||
|
CapabilityArgs map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkConfig struct {
|
||||||
|
Network *types.NetConf
|
||||||
|
Bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkConfigList struct {
|
||||||
|
Name string
|
||||||
|
CNIVersion string
|
||||||
|
Plugins []*NetworkConfig
|
||||||
|
Bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CNI interface {
|
||||||
|
AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
||||||
|
DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error
|
||||||
|
|
||||||
|
AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
|
||||||
|
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CNIConfig struct {
|
||||||
|
Path []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CNIConfig implements the CNI interface
|
||||||
|
var _ CNI = &CNIConfig{}
|
||||||
|
|
||||||
|
func buildOneConfig(list *NetworkConfigList, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
inject := map[string]interface{}{
|
||||||
|
"name": list.Name,
|
||||||
|
"cniVersion": list.CNIVersion,
|
||||||
|
}
|
||||||
|
// Add previous plugin result
|
||||||
|
if prevResult != nil {
|
||||||
|
inject["prevResult"] = prevResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure every config uses the same name and version
|
||||||
|
orig, err = InjectConf(orig, inject)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return injectRuntimeConfig(orig, rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function takes a libcni RuntimeConf structure and injects values into
|
||||||
|
// a "runtimeConfig" dictionary in the CNI network configuration JSON that
|
||||||
|
// will be passed to the plugin on stdin.
|
||||||
|
//
|
||||||
|
// Only "capabilities arguments" passed by the runtime are currently injected.
|
||||||
|
// These capabilities arguments are filtered through the plugin's advertised
|
||||||
|
// capabilities from its config JSON, and any keys in the CapabilityArgs
|
||||||
|
// matching plugin capabilities are added to the "runtimeConfig" dictionary
|
||||||
|
// sent to the plugin via JSON on stdin. For exmaple, if the plugin's
|
||||||
|
// capabilities include "portMappings", and the CapabilityArgs map includes a
|
||||||
|
// "portMappings" key, that key and its value are added to the "runtimeConfig"
|
||||||
|
// dictionary to be passed to the plugin's stdin.
|
||||||
|
func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
rc := make(map[string]interface{})
|
||||||
|
for capability, supported := range orig.Network.Capabilities {
|
||||||
|
if !supported {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if data, ok := rt.CapabilityArgs[capability]; ok {
|
||||||
|
rc[capability] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rc) > 0 {
|
||||||
|
orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNetworkList executes a sequence of plugins with the ADD command
|
||||||
|
func (c *CNIConfig) AddNetworkList(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
|
||||||
|
var prevResult types.Result
|
||||||
|
for _, net := range list.Plugins {
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newConf, err := buildOneConfig(list, net, prevResult, rt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prevResult, err = invoke.ExecPluginWithResult(pluginPath, newConf.Bytes, c.args("ADD", rt))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelNetworkList executes a sequence of plugins with the DEL command
|
||||||
|
func (c *CNIConfig) DelNetworkList(list *NetworkConfigList, rt *RuntimeConf) error {
|
||||||
|
for i := len(list.Plugins) - 1; i >= 0; i-- {
|
||||||
|
net := list.Plugins[i]
|
||||||
|
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newConf, err := buildOneConfig(list, net, nil, rt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := invoke.ExecPluginWithoutResult(pluginPath, newConf.Bytes, c.args("DEL", rt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNetwork executes the plugin with the ADD command
|
||||||
|
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
net, err = injectRuntimeConfig(net, rt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelNetwork executes the plugin with the DEL command
|
||||||
|
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
|
||||||
|
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
net, err = injectRuntimeConfig(net, rt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionInfo reports which versions of the CNI spec are supported by
|
||||||
|
// the given plugin.
|
||||||
|
func (c *CNIConfig) GetVersionInfo(pluginType string) (version.PluginInfo, error) {
|
||||||
|
pluginPath, err := invoke.FindInPath(pluginType, c.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.GetVersionInfo(pluginPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====
|
||||||
|
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
|
||||||
|
return &invoke.Args{
|
||||||
|
Command: action,
|
||||||
|
ContainerID: rt.ContainerID,
|
||||||
|
NetNS: rt.NetNS,
|
||||||
|
PluginArgs: rt.Args,
|
||||||
|
IfName: rt.IfName,
|
||||||
|
Path: strings.Join(c.Path, string(os.PathListSeparator)),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,259 @@
|
||||||
|
// Copyright 2015 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 libcni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotFoundError struct {
|
||||||
|
Dir string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e NotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoConfigsFoundError struct {
|
||||||
|
Dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e NoConfigsFoundError) Error() string {
|
||||||
|
return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
|
||||||
|
conf := &NetworkConfig{Bytes: bytes}
|
||||||
|
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration: %s", err)
|
||||||
|
}
|
||||||
|
if conf.Network.Type == "" {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration: missing 'type'")
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfFromFile(filename string) (*NetworkConfig, error) {
|
||||||
|
bytes, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
return ConfFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||||
|
rawList := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal(bytes, &rawList); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawName, ok := rawList["name"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: no name")
|
||||||
|
}
|
||||||
|
name, ok := rawName.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cniVersion string
|
||||||
|
rawVersion, ok := rawList["cniVersion"]
|
||||||
|
if ok {
|
||||||
|
cniVersion, ok = rawVersion.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list := &NetworkConfigList{
|
||||||
|
Name: name,
|
||||||
|
CNIVersion: cniVersion,
|
||||||
|
Bytes: bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
var plugins []interface{}
|
||||||
|
plug, ok := rawList["plugins"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
|
||||||
|
}
|
||||||
|
plugins, ok = plug.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
|
||||||
|
}
|
||||||
|
if len(plugins) == 0 {
|
||||||
|
return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
netConf, err := ConfFromBytes(newBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
|
||||||
|
}
|
||||||
|
list.Plugins = append(list.Plugins, netConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfListFromFile(filename string) (*NetworkConfigList, error) {
|
||||||
|
bytes, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %s", filename, err)
|
||||||
|
}
|
||||||
|
return ConfListFromBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfFiles(dir string, extensions []string) ([]string, error) {
|
||||||
|
// In part, adapted from rkt/networking/podenv.go#listFiles
|
||||||
|
files, err := ioutil.ReadDir(dir)
|
||||||
|
switch {
|
||||||
|
case err == nil: // break
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
confFiles := []string{}
|
||||||
|
for _, f := range files {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileExt := filepath.Ext(f.Name())
|
||||||
|
for _, ext := range extensions {
|
||||||
|
if fileExt == ext {
|
||||||
|
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return confFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConf(dir, name string) (*NetworkConfig, error) {
|
||||||
|
files, err := ConfFiles(dir, []string{".conf", ".json"})
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case len(files) == 0:
|
||||||
|
return nil, NoConfigsFoundError{Dir: dir}
|
||||||
|
}
|
||||||
|
sort.Strings(files)
|
||||||
|
|
||||||
|
for _, confFile := range files {
|
||||||
|
conf, err := ConfFromFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conf.Network.Name == name {
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, NotFoundError{dir, name}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfList(dir, name string) (*NetworkConfigList, error) {
|
||||||
|
files, err := ConfFiles(dir, []string{".conflist"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sort.Strings(files)
|
||||||
|
|
||||||
|
for _, confFile := range files {
|
||||||
|
conf, err := ConfListFromFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conf.Name == name {
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and load a network configuration file (instead of list)
|
||||||
|
// from the same name, then upconvert.
|
||||||
|
singleConf, err := LoadConf(dir, name)
|
||||||
|
if err != nil {
|
||||||
|
// A little extra logic so the error makes sense
|
||||||
|
if _, ok := err.(NoConfigsFoundError); len(files) != 0 && ok {
|
||||||
|
// Config lists found but no config files found
|
||||||
|
return nil, NotFoundError{dir, name}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ConfListFromConf(singleConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*NetworkConfig, error) {
|
||||||
|
config := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(original.Bytes, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range newValues {
|
||||||
|
if key == "" {
|
||||||
|
return nil, fmt.Errorf("keys cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil {
|
||||||
|
return nil, fmt.Errorf("key '%s' value must not be nil", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
config[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
newBytes, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConfFromBytes(newBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
|
||||||
|
// with the single network as the only entry in the list.
|
||||||
|
func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
|
||||||
|
// Re-deserialize the config's json, then make a raw map configlist.
|
||||||
|
// This may seem a bit strange, but it's to make the Bytes fields
|
||||||
|
// actually make sense. Otherwise, the generated json is littered with
|
||||||
|
// golang default values.
|
||||||
|
|
||||||
|
rawConfig := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawConfigList := map[string]interface{}{
|
||||||
|
"name": original.Network.Name,
|
||||||
|
"cniVersion": original.Network.CNIVersion,
|
||||||
|
"plugins": []interface{}{rawConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(rawConfigList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ConfListFromBytes(b)
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2015 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 invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CNIArgs interface {
|
||||||
|
// For use with os/exec; i.e., return nil to inherit the
|
||||||
|
// environment from this process
|
||||||
|
AsEnv() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type inherited struct{}
|
||||||
|
|
||||||
|
var inheritArgsFromEnv inherited
|
||||||
|
|
||||||
|
func (_ *inherited) AsEnv() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArgsFromEnv() CNIArgs {
|
||||||
|
return &inheritArgsFromEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args struct {
|
||||||
|
Command string
|
||||||
|
ContainerID string
|
||||||
|
NetNS string
|
||||||
|
PluginArgs [][2]string
|
||||||
|
PluginArgsStr string
|
||||||
|
IfName string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args implements the CNIArgs interface
|
||||||
|
var _ CNIArgs = &Args{}
|
||||||
|
|
||||||
|
func (args *Args) AsEnv() []string {
|
||||||
|
env := os.Environ()
|
||||||
|
pluginArgsStr := args.PluginArgsStr
|
||||||
|
if pluginArgsStr == "" {
|
||||||
|
pluginArgsStr = stringify(args.PluginArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the custom values are first, so any value present in
|
||||||
|
// the process environment won't override them.
|
||||||
|
env = append([]string{
|
||||||
|
"CNI_COMMAND=" + args.Command,
|
||||||
|
"CNI_CONTAINERID=" + args.ContainerID,
|
||||||
|
"CNI_NETNS=" + args.NetNS,
|
||||||
|
"CNI_ARGS=" + pluginArgsStr,
|
||||||
|
"CNI_IFNAME=" + args.IfName,
|
||||||
|
"CNI_PATH=" + args.Path,
|
||||||
|
}, env...)
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from rkt/networking/net_plugin.go
|
||||||
|
func stringify(pluginArgs [][2]string) string {
|
||||||
|
entries := make([]string, len(pluginArgs))
|
||||||
|
|
||||||
|
for i, kv := range pluginArgs {
|
||||||
|
entries[i] = strings.Join(kv[:], "=")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(entries, ";")
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2016 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 invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DelegateAdd(delegatePlugin string, netconf []byte) (types.Result, error) {
|
||||||
|
if os.Getenv("CNI_COMMAND") != "ADD" {
|
||||||
|
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
|
||||||
|
|
||||||
|
pluginPath, err := FindInPath(delegatePlugin, paths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
|
||||||
|
}
|
||||||
|
|
||||||
|
func DelegateDel(delegatePlugin string, netconf []byte) error {
|
||||||
|
if os.Getenv("CNI_COMMAND") != "DEL" {
|
||||||
|
return fmt.Errorf("CNI_COMMAND is not DEL")
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
|
||||||
|
|
||||||
|
pluginPath, err := FindInPath(delegatePlugin, paths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2015 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 invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
|
||||||
|
return defaultPluginExec.WithResult(pluginPath, netconf, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
|
||||||
|
return defaultPluginExec.WithoutResult(pluginPath, netconf, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
||||||
|
return defaultPluginExec.GetVersionInfo(pluginPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultPluginExec = &PluginExec{
|
||||||
|
RawExec: &RawExec{Stderr: os.Stderr},
|
||||||
|
VersionDecoder: &version.PluginDecoder{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginExec struct {
|
||||||
|
RawExec interface {
|
||||||
|
ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error)
|
||||||
|
}
|
||||||
|
VersionDecoder interface {
|
||||||
|
Decode(jsonBytes []byte) (version.PluginInfo, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PluginExec) WithResult(pluginPath string, netconf []byte, args CNIArgs) (types.Result, error) {
|
||||||
|
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin must return result in same version as specified in netconf
|
||||||
|
versionDecoder := &version.ConfigDecoder{}
|
||||||
|
confVersion, err := versionDecoder.Decode(netconf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return version.NewResult(confVersion, stdoutBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PluginExec) WithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
|
||||||
|
_, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionInfo returns the version information available about the plugin.
|
||||||
|
// For recent-enough plugins, it uses the information returned by the VERSION
|
||||||
|
// command. For older plugins which do not recognize that command, it reports
|
||||||
|
// version 0.1.0
|
||||||
|
func (e *PluginExec) GetVersionInfo(pluginPath string) (version.PluginInfo, error) {
|
||||||
|
args := &Args{
|
||||||
|
Command: "VERSION",
|
||||||
|
|
||||||
|
// set fake values required by plugins built against an older version of skel
|
||||||
|
NetNS: "dummy",
|
||||||
|
IfName: "dummy",
|
||||||
|
Path: "dummy",
|
||||||
|
}
|
||||||
|
stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
|
||||||
|
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, stdin, args.AsEnv())
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "unknown CNI_COMMAND: VERSION" {
|
||||||
|
return version.PluginSupports("0.1.0"), nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.VersionDecoder.Decode(stdoutBytes)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2015 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 invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindInPath returns the full path of the plugin by searching in the provided path
|
||||||
|
func FindInPath(plugin string, paths []string) (string, error) {
|
||||||
|
if plugin == "" {
|
||||||
|
return "", fmt.Errorf("no plugin name provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return "", fmt.Errorf("no paths provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
for _, fe := range ExecutableFileExtensions {
|
||||||
|
fullpath := filepath.Join(path, plugin) + fe
|
||||||
|
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
|
||||||
|
return fullpath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// +build darwin dragonfly freebsd linux netbsd opensbd solaris
|
||||||
|
|
||||||
|
package invoke
|
||||||
|
|
||||||
|
// Valid file extensions for plugin executables.
|
||||||
|
var ExecutableFileExtensions = []string{""}
|
18
vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
generated
vendored
Normal file
18
vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2016 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 invoke
|
||||||
|
|
||||||
|
// Valid file extensions for plugin executables.
|
||||||
|
var ExecutableFileExtensions = []string{".exe", ""}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2016 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 invoke
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RawExec struct {
|
||||||
|
Stderr io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
||||||
|
stdout := &bytes.Buffer{}
|
||||||
|
|
||||||
|
c := exec.Cmd{
|
||||||
|
Env: environ,
|
||||||
|
Path: pluginPath,
|
||||||
|
Args: []string{pluginPath},
|
||||||
|
Stdin: bytes.NewBuffer(stdinData),
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: e.Stderr,
|
||||||
|
}
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
return nil, pluginErr(err, stdout.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluginErr(err error, output []byte) error {
|
||||||
|
if _, ok := err.(*exec.ExitError); ok {
|
||||||
|
emsg := types.Error{}
|
||||||
|
if perr := json.Unmarshal(output, &emsg); perr != nil {
|
||||||
|
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
|
||||||
|
}
|
||||||
|
return &emsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright 2016 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 types020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ImplementedSpecVersion string = "0.2.0"
|
||||||
|
|
||||||
|
var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
|
||||||
|
|
||||||
|
// Compatibility types for CNI version 0.1.0 and 0.2.0
|
||||||
|
|
||||||
|
func NewResult(data []byte) (types.Result, error) {
|
||||||
|
result := &Result{}
|
||||||
|
if err := json.Unmarshal(data, result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetResult(r types.Result) (*Result, error) {
|
||||||
|
// We expect version 0.1.0/0.2.0 results
|
||||||
|
result020, err := r.GetAsVersion(ImplementedSpecVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, ok := result020.(*Result)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to convert result")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||||
|
type Result struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
IP4 *IPConfig `json:"ip4,omitempty"`
|
||||||
|
IP6 *IPConfig `json:"ip6,omitempty"`
|
||||||
|
DNS types.DNS `json:"dns,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Version() string {
|
||||||
|
return ImplementedSpecVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||||
|
for _, supportedVersion := range SupportedVersions {
|
||||||
|
if version == supportedVersion {
|
||||||
|
r.CNIVersion = version
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Print() error {
|
||||||
|
data, err := json.MarshalIndent(r, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||||
|
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||||
|
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||||
|
func (r *Result) String() string {
|
||||||
|
var str string
|
||||||
|
if r.IP4 != nil {
|
||||||
|
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||||
|
}
|
||||||
|
if r.IP6 != nil {
|
||||||
|
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPConfig contains values necessary to configure an interface
|
||||||
|
type IPConfig struct {
|
||||||
|
IP net.IPNet
|
||||||
|
Gateway net.IP
|
||||||
|
Routes []types.Route
|
||||||
|
}
|
||||||
|
|
||||||
|
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||||
|
// for our custom IPNet type
|
||||||
|
|
||||||
|
// JSON (un)marshallable types
|
||||||
|
type ipConfig struct {
|
||||||
|
IP types.IPNet `json:"ip"`
|
||||||
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
|
Routes []types.Route `json:"routes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
ipc := ipConfig{
|
||||||
|
IP: types.IPNet(c.IP),
|
||||||
|
Gateway: c.Gateway,
|
||||||
|
Routes: c.Routes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(ipc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||||
|
ipc := ipConfig{}
|
||||||
|
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.IP = net.IPNet(ipc.IP)
|
||||||
|
c.Gateway = ipc.Gateway
|
||||||
|
c.Routes = ipc.Routes
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2015 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshallableBool typedef for builtin bool
|
||||||
|
// because builtin type's methods can't be declared
|
||||||
|
type UnmarshallableBool bool
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Returns boolean true if the string is "1" or "[Tt]rue"
|
||||||
|
// Returns boolean false if the string is "0" or "[Ff]alse"
|
||||||
|
func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
|
||||||
|
s := strings.ToLower(string(data))
|
||||||
|
switch s {
|
||||||
|
case "1", "true":
|
||||||
|
*b = true
|
||||||
|
case "0", "false":
|
||||||
|
*b = false
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshallableString typedef for builtin string
|
||||||
|
type UnmarshallableString string
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Returns the string
|
||||||
|
func (s *UnmarshallableString) UnmarshalText(data []byte) error {
|
||||||
|
*s = UnmarshallableString(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonArgs contains the IgnoreUnknown argument
|
||||||
|
// and must be embedded by all Arg structs
|
||||||
|
type CommonArgs struct {
|
||||||
|
IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeyField is a helper function to receive Values
|
||||||
|
// Values that represent a pointer to a struct
|
||||||
|
func GetKeyField(keyString string, v reflect.Value) reflect.Value {
|
||||||
|
return v.Elem().FieldByName(keyString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalableArgsError is used to indicate error unmarshalling args
|
||||||
|
// from the args-string in the form "K=V;K2=V2;..."
|
||||||
|
type UnmarshalableArgsError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
|
||||||
|
func LoadArgs(args string, container interface{}) error {
|
||||||
|
if args == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
containerValue := reflect.ValueOf(container)
|
||||||
|
|
||||||
|
pairs := strings.Split(args, ";")
|
||||||
|
unknownArgs := []string{}
|
||||||
|
for _, pair := range pairs {
|
||||||
|
kv := strings.Split(pair, "=")
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return fmt.Errorf("ARGS: invalid pair %q", pair)
|
||||||
|
}
|
||||||
|
keyString := kv[0]
|
||||||
|
valueString := kv[1]
|
||||||
|
keyField := GetKeyField(keyString, containerValue)
|
||||||
|
if !keyField.IsValid() {
|
||||||
|
unknownArgs = append(unknownArgs, pair)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keyFieldIface := keyField.Addr().Interface()
|
||||||
|
u, ok := keyFieldIface.(encoding.TextUnmarshaler)
|
||||||
|
if !ok {
|
||||||
|
return UnmarshalableArgsError{fmt.Errorf(
|
||||||
|
"ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler",
|
||||||
|
keyString, reflect.TypeOf(keyFieldIface))}
|
||||||
|
}
|
||||||
|
err := u.UnmarshalText([]byte(valueString))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool()
|
||||||
|
if len(unknownArgs) > 0 && !isIgnoreUnknown {
|
||||||
|
return fmt.Errorf("ARGS: unknown args %q", unknownArgs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
300
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
Normal file
300
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
// Copyright 2016 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 current
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ImplementedSpecVersion string = "0.3.1"
|
||||||
|
|
||||||
|
var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion}
|
||||||
|
|
||||||
|
func NewResult(data []byte) (types.Result, error) {
|
||||||
|
result := &Result{}
|
||||||
|
if err := json.Unmarshal(data, result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetResult(r types.Result) (*Result, error) {
|
||||||
|
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, ok := resultCurrent.(*Result)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to convert result")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultConverters = []struct {
|
||||||
|
versions []string
|
||||||
|
convert func(types.Result) (*Result, error)
|
||||||
|
}{
|
||||||
|
{types020.SupportedVersions, convertFrom020},
|
||||||
|
{SupportedVersions, convertFrom030},
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFrom020(result types.Result) (*Result, error) {
|
||||||
|
oldResult, err := types020.GetResult(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newResult := &Result{
|
||||||
|
CNIVersion: ImplementedSpecVersion,
|
||||||
|
DNS: oldResult.DNS,
|
||||||
|
Routes: []*types.Route{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldResult.IP4 != nil {
|
||||||
|
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||||
|
Version: "4",
|
||||||
|
Address: oldResult.IP4.IP,
|
||||||
|
Gateway: oldResult.IP4.Gateway,
|
||||||
|
})
|
||||||
|
for _, route := range oldResult.IP4.Routes {
|
||||||
|
gw := route.GW
|
||||||
|
if gw == nil {
|
||||||
|
gw = oldResult.IP4.Gateway
|
||||||
|
}
|
||||||
|
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||||
|
Dst: route.Dst,
|
||||||
|
GW: gw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldResult.IP6 != nil {
|
||||||
|
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||||
|
Version: "6",
|
||||||
|
Address: oldResult.IP6.IP,
|
||||||
|
Gateway: oldResult.IP6.Gateway,
|
||||||
|
})
|
||||||
|
for _, route := range oldResult.IP6.Routes {
|
||||||
|
gw := route.GW
|
||||||
|
if gw == nil {
|
||||||
|
gw = oldResult.IP6.Gateway
|
||||||
|
}
|
||||||
|
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||||
|
Dst: route.Dst,
|
||||||
|
GW: gw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newResult.IPs) == 0 {
|
||||||
|
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFrom030(result types.Result) (*Result, error) {
|
||||||
|
newResult, ok := result.(*Result)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to convert result")
|
||||||
|
}
|
||||||
|
newResult.CNIVersion = ImplementedSpecVersion
|
||||||
|
return newResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResultFromResult(result types.Result) (*Result, error) {
|
||||||
|
version := result.Version()
|
||||||
|
for _, converter := range resultConverters {
|
||||||
|
for _, supportedVersion := range converter.versions {
|
||||||
|
if version == supportedVersion {
|
||||||
|
return converter.convert(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unsupported CNI result22 version %q", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||||
|
type Result struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
Interfaces []*Interface `json:"interfaces,omitempty"`
|
||||||
|
IPs []*IPConfig `json:"ips,omitempty"`
|
||||||
|
Routes []*types.Route `json:"routes,omitempty"`
|
||||||
|
DNS types.DNS `json:"dns,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to the older 0.2.0 CNI spec Result type
|
||||||
|
func (r *Result) convertTo020() (*types020.Result, error) {
|
||||||
|
oldResult := &types020.Result{
|
||||||
|
CNIVersion: types020.ImplementedSpecVersion,
|
||||||
|
DNS: r.DNS,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range r.IPs {
|
||||||
|
// Only convert the first IP address of each version as 0.2.0
|
||||||
|
// and earlier cannot handle multiple IP addresses
|
||||||
|
if ip.Version == "4" && oldResult.IP4 == nil {
|
||||||
|
oldResult.IP4 = &types020.IPConfig{
|
||||||
|
IP: ip.Address,
|
||||||
|
Gateway: ip.Gateway,
|
||||||
|
}
|
||||||
|
} else if ip.Version == "6" && oldResult.IP6 == nil {
|
||||||
|
oldResult.IP6 = &types020.IPConfig{
|
||||||
|
IP: ip.Address,
|
||||||
|
Gateway: ip.Gateway,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldResult.IP4 != nil && oldResult.IP6 != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range r.Routes {
|
||||||
|
is4 := route.Dst.IP.To4() != nil
|
||||||
|
if is4 && oldResult.IP4 != nil {
|
||||||
|
oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{
|
||||||
|
Dst: route.Dst,
|
||||||
|
GW: route.GW,
|
||||||
|
})
|
||||||
|
} else if !is4 && oldResult.IP6 != nil {
|
||||||
|
oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{
|
||||||
|
Dst: route.Dst,
|
||||||
|
GW: route.GW,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldResult.IP4 == nil && oldResult.IP6 == nil {
|
||||||
|
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Version() string {
|
||||||
|
return ImplementedSpecVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||||
|
switch version {
|
||||||
|
case "0.3.0", ImplementedSpecVersion:
|
||||||
|
r.CNIVersion = version
|
||||||
|
return r, nil
|
||||||
|
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
|
||||||
|
return r.convertTo020()
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Print() error {
|
||||||
|
data, err := json.MarshalIndent(r, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
|
||||||
|
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
|
||||||
|
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||||
|
func (r *Result) String() string {
|
||||||
|
var str string
|
||||||
|
if len(r.Interfaces) > 0 {
|
||||||
|
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
|
||||||
|
}
|
||||||
|
if len(r.IPs) > 0 {
|
||||||
|
str += fmt.Sprintf("IP:%+v, ", r.IPs)
|
||||||
|
}
|
||||||
|
if len(r.Routes) > 0 {
|
||||||
|
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert this old version result to the current CNI version result
|
||||||
|
func (r *Result) Convert() (*Result, error) {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface contains values about the created interfaces
|
||||||
|
type Interface struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Mac string `json:"mac,omitempty"`
|
||||||
|
Sandbox string `json:"sandbox,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interface) String() string {
|
||||||
|
return fmt.Sprintf("%+v", *i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns a pointer to the int value passed in. Used to
|
||||||
|
// set the IPConfig.Interface field.
|
||||||
|
func Int(v int) *int {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPConfig contains values necessary to configure an IP address on an interface
|
||||||
|
type IPConfig struct {
|
||||||
|
// IP version, either "4" or "6"
|
||||||
|
Version string
|
||||||
|
// Index into Result structs Interfaces list
|
||||||
|
Interface *int
|
||||||
|
Address net.IPNet
|
||||||
|
Gateway net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IPConfig) String() string {
|
||||||
|
return fmt.Sprintf("%+v", *i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON (un)marshallable types
|
||||||
|
type ipConfig struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Interface *int `json:"interface,omitempty"`
|
||||||
|
Address types.IPNet `json:"address"`
|
||||||
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
ipc := ipConfig{
|
||||||
|
Version: c.Version,
|
||||||
|
Interface: c.Interface,
|
||||||
|
Address: types.IPNet(c.Address),
|
||||||
|
Gateway: c.Gateway,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(ipc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||||
|
ipc := ipConfig{}
|
||||||
|
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Version = ipc.Version
|
||||||
|
c.Interface = ipc.Interface
|
||||||
|
c.Address = net.IPNet(ipc.Address)
|
||||||
|
c.Gateway = ipc.Gateway
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
// Copyright 2015 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// like net.IPNet but adds JSON marshalling and unmarshalling
|
||||||
|
type IPNet net.IPNet
|
||||||
|
|
||||||
|
// ParseCIDR takes a string like "10.2.3.1/24" and
|
||||||
|
// return IPNet with "10.2.3.1" and /24 mask
|
||||||
|
func ParseCIDR(s string) (*net.IPNet, error) {
|
||||||
|
ip, ipn, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipn.IP = ip
|
||||||
|
return ipn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n IPNet) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal((*net.IPNet)(&n).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IPNet) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*n = IPNet(*tmp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetConf describes a network.
|
||||||
|
type NetConf struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Capabilities map[string]bool `json:"capabilities,omitempty"`
|
||||||
|
IPAM IPAM `json:"ipam,omitempty"`
|
||||||
|
DNS DNS `json:"dns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPAM struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetConfList describes an ordered list of networks.
|
||||||
|
type NetConfList struct {
|
||||||
|
CNIVersion string `json:"cniVersion,omitempty"`
|
||||||
|
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Plugins []*NetConf `json:"plugins,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultFactoryFunc func([]byte) (Result, error)
|
||||||
|
|
||||||
|
// Result is an interface that provides the result of plugin execution
|
||||||
|
type Result interface {
|
||||||
|
// The highest CNI specification result verison the result supports
|
||||||
|
// without having to convert
|
||||||
|
Version() string
|
||||||
|
|
||||||
|
// Returns the result converted into the requested CNI specification
|
||||||
|
// result version, or an error if conversion failed
|
||||||
|
GetAsVersion(version string) (Result, error)
|
||||||
|
|
||||||
|
// Prints the result in JSON format to stdout
|
||||||
|
Print() error
|
||||||
|
|
||||||
|
// Returns a JSON string representation of the result
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintResult(result Result, version string) error {
|
||||||
|
newResult, err := result.GetAsVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return newResult.Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS contains values interesting for DNS resolvers
|
||||||
|
type DNS struct {
|
||||||
|
Nameservers []string `json:"nameservers,omitempty"`
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
Search []string `json:"search,omitempty"`
|
||||||
|
Options []string `json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Dst net.IPNet
|
||||||
|
GW net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) String() string {
|
||||||
|
return fmt.Sprintf("%+v", *r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Code uint `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Details string `json:"details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
details := ""
|
||||||
|
if e.Details != "" {
|
||||||
|
details = fmt.Sprintf("; %v", e.Details)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v%v", e.Msg, details)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Print() error {
|
||||||
|
return prettyPrint(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||||
|
// for our custom IPNet type
|
||||||
|
|
||||||
|
// JSON (un)marshallable types
|
||||||
|
type route struct {
|
||||||
|
Dst IPNet `json:"dst"`
|
||||||
|
GW net.IP `json:"gw,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) UnmarshalJSON(data []byte) error {
|
||||||
|
rt := route{}
|
||||||
|
if err := json.Unmarshal(data, &rt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Dst = net.IPNet(rt.Dst)
|
||||||
|
r.GW = rt.GW
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Route) MarshalJSON() ([]byte, error) {
|
||||||
|
rt := route{
|
||||||
|
Dst: IPNet(r.Dst),
|
||||||
|
GW: r.GW,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyPrint(obj interface{}) error {
|
||||||
|
data, err := json.MarshalIndent(obj, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, 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")
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2016 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 version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigDecoder can decode the CNI version available in network config data
|
||||||
|
type ConfigDecoder struct{}
|
||||||
|
|
||||||
|
func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) {
|
||||||
|
var conf struct {
|
||||||
|
CNIVersion string `json:"cniVersion"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(jsonBytes, &conf)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("decoding version from network config: %s", err)
|
||||||
|
}
|
||||||
|
if conf.CNIVersion == "" {
|
||||||
|
return "0.1.0", nil
|
||||||
|
}
|
||||||
|
return conf.CNIVersion, nil
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2016 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 version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginInfo reports information about CNI versioning
|
||||||
|
type PluginInfo interface {
|
||||||
|
// SupportedVersions returns one or more CNI spec versions that the plugin
|
||||||
|
// supports. If input is provided in one of these versions, then the plugin
|
||||||
|
// promises to use the same CNI version in its response
|
||||||
|
SupportedVersions() []string
|
||||||
|
|
||||||
|
// Encode writes this CNI version information as JSON to the given Writer
|
||||||
|
Encode(io.Writer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginInfo struct {
|
||||||
|
CNIVersion_ string `json:"cniVersion"`
|
||||||
|
SupportedVersions_ []string `json:"supportedVersions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// pluginInfo implements the PluginInfo interface
|
||||||
|
var _ PluginInfo = &pluginInfo{}
|
||||||
|
|
||||||
|
func (p *pluginInfo) Encode(w io.Writer) error {
|
||||||
|
return json.NewEncoder(w).Encode(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pluginInfo) SupportedVersions() []string {
|
||||||
|
return p.SupportedVersions_
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginSupports returns a new PluginInfo that will report the given versions
|
||||||
|
// as supported
|
||||||
|
func PluginSupports(supportedVersions ...string) PluginInfo {
|
||||||
|
if len(supportedVersions) < 1 {
|
||||||
|
panic("programmer error: you must support at least one version")
|
||||||
|
}
|
||||||
|
return &pluginInfo{
|
||||||
|
CNIVersion_: Current(),
|
||||||
|
SupportedVersions_: supportedVersions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDecoder can decode the response returned by a plugin's VERSION command
|
||||||
|
type PluginDecoder struct{}
|
||||||
|
|
||||||
|
func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
|
||||||
|
var info pluginInfo
|
||||||
|
err := json.Unmarshal(jsonBytes, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding version info: %s", err)
|
||||||
|
}
|
||||||
|
if info.CNIVersion_ == "" {
|
||||||
|
return nil, fmt.Errorf("decoding version info: missing field cniVersion")
|
||||||
|
}
|
||||||
|
if len(info.SupportedVersions_) == 0 {
|
||||||
|
if info.CNIVersion_ == "0.2.0" {
|
||||||
|
return PluginSupports("0.1.0", "0.2.0"), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
49
vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
generated
vendored
Normal file
49
vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2016 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 version
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ErrorIncompatible struct {
|
||||||
|
Config string
|
||||||
|
Supported []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorIncompatible) Details() string {
|
||||||
|
return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorIncompatible) Error() string {
|
||||||
|
return fmt.Sprintf("incompatible CNI versions: %s", e.Details())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reconciler struct{}
|
||||||
|
|
||||||
|
func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
|
||||||
|
return r.CheckRaw(configVersion, pluginInfo.SupportedVersions())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible {
|
||||||
|
for _, supportedVersion := range supportedVersions {
|
||||||
|
if configVersion == supportedVersion {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ErrorIncompatible{
|
||||||
|
Config: configVersion,
|
||||||
|
Supported: supportedVersions,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2016 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 version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Current reports the version of the CNI spec implemented by this library
|
||||||
|
func Current() string {
|
||||||
|
return "0.3.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy PluginInfo describes a plugin that is backwards compatible with the
|
||||||
|
// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0
|
||||||
|
// library ought to work correctly with a plugin that reports support for
|
||||||
|
// Legacy versions.
|
||||||
|
//
|
||||||
|
// Any future CNI spec versions which meet this definition should be added to
|
||||||
|
// this list.
|
||||||
|
var Legacy = PluginSupports("0.1.0", "0.2.0")
|
||||||
|
var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1")
|
||||||
|
|
||||||
|
var resultFactories = []struct {
|
||||||
|
supportedVersions []string
|
||||||
|
newResult types.ResultFactoryFunc
|
||||||
|
}{
|
||||||
|
{current.SupportedVersions, current.NewResult},
|
||||||
|
{types020.SupportedVersions, types020.NewResult},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds a Result object matching the requested version (if any) and asks
|
||||||
|
// that object to parse the plugin result, returning an error if parsing failed.
|
||||||
|
func NewResult(version string, resultBytes []byte) (types.Result, error) {
|
||||||
|
reconciler := &Reconciler{}
|
||||||
|
for _, resultFactory := range resultFactories {
|
||||||
|
err := reconciler.CheckRaw(version, resultFactory.supportedVersions)
|
||||||
|
if err == nil {
|
||||||
|
// Result supports this version
|
||||||
|
return resultFactory.newResult(resultBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unsupported CNI result version %q", version)
|
||||||
|
}
|
|
@ -100,12 +100,21 @@ github.com/containerd/continuity/driver
|
||||||
github.com/containerd/continuity/proto
|
github.com/containerd/continuity/proto
|
||||||
# github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260
|
# github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260
|
||||||
github.com/containerd/fifo
|
github.com/containerd/fifo
|
||||||
|
# github.com/containerd/go-cni v0.0.0-20190610170741-5a4663dad645
|
||||||
|
github.com/containerd/go-cni
|
||||||
# github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
|
# github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
|
||||||
github.com/containerd/go-runc
|
github.com/containerd/go-runc
|
||||||
# github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7
|
# github.com/containerd/ttrpc v0.0.0-20190411181408-699c4e40d1e7
|
||||||
github.com/containerd/ttrpc
|
github.com/containerd/ttrpc
|
||||||
# github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
|
# github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
|
||||||
github.com/containerd/typeurl
|
github.com/containerd/typeurl
|
||||||
|
# github.com/containernetworking/cni v0.6.1-0.20180218032124-142cde0c766c
|
||||||
|
github.com/containernetworking/cni/libcni
|
||||||
|
github.com/containernetworking/cni/pkg/types
|
||||||
|
github.com/containernetworking/cni/pkg/types/current
|
||||||
|
github.com/containernetworking/cni/pkg/invoke
|
||||||
|
github.com/containernetworking/cni/pkg/version
|
||||||
|
github.com/containernetworking/cni/pkg/types/020
|
||||||
# github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
# github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||||
github.com/coreos/go-systemd/daemon
|
github.com/coreos/go-systemd/daemon
|
||||||
# github.com/davecgh/go-spew v1.1.1
|
# github.com/davecgh/go-spew v1.1.1
|
||||||
|
|
|
@ -27,16 +27,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWorkerOpt creates a WorkerOpt.
|
// NewWorkerOpt creates a WorkerOpt.
|
||||||
func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
|
func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt network.Opt, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
|
||||||
opts = append(opts, containerd.WithDefaultNamespace(ns))
|
opts = append(opts, containerd.WithDefaultNamespace(ns))
|
||||||
client, err := containerd.New(address, opts...)
|
client, err := containerd.New(address, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return base.WorkerOpt{}, errors.Wrapf(err, "failed to connect client to %q . make sure containerd is running", address)
|
return base.WorkerOpt{}, errors.Wrapf(err, "failed to connect client to %q . make sure containerd is running", address)
|
||||||
}
|
}
|
||||||
return newContainerd(root, client, snapshotterName, ns, labels, dns)
|
return newContainerd(root, client, snapshotterName, ns, labels, dns, nopt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig) (base.WorkerOpt, error) {
|
func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt network.Opt) (base.WorkerOpt, error) {
|
||||||
if strings.Contains(snapshotterName, "/") {
|
if strings.Contains(snapshotterName, "/") {
|
||||||
return base.WorkerOpt{}, errors.Errorf("bad snapshotter name: %q", snapshotterName)
|
return base.WorkerOpt{}, errors.Errorf("bad snapshotter name: %q", snapshotterName)
|
||||||
}
|
}
|
||||||
|
@ -103,11 +103,16 @@ func newContainerd(root string, client *containerd.Client, snapshotterName, ns s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
np, err := network.Providers(nopt)
|
||||||
|
if err != nil {
|
||||||
|
return base.WorkerOpt{}, err
|
||||||
|
}
|
||||||
|
|
||||||
opt := base.WorkerOpt{
|
opt := base.WorkerOpt{
|
||||||
ID: id,
|
ID: id,
|
||||||
Labels: xlabels,
|
Labels: xlabels,
|
||||||
MetadataStore: md,
|
MetadataStore: md,
|
||||||
Executor: containerdexecutor.New(client, root, "", network.Default(), dns),
|
Executor: containerdexecutor.New(client, root, "", np, dns),
|
||||||
Snapshotter: containerdsnapshot.NewSnapshotter(snapshotterName, client.SnapshotService(snapshotterName), cs, md, ns, gc, nil),
|
Snapshotter: containerdsnapshot.NewSnapshotter(snapshotterName, client.SnapshotService(snapshotterName), cs, md, ns, gc, nil),
|
||||||
ContentStore: cs,
|
ContentStore: cs,
|
||||||
Applier: winlayers.NewFileSystemApplierWithWindows(cs, df),
|
Applier: winlayers.NewFileSystemApplierWithWindows(cs, df),
|
||||||
|
|
|
@ -34,7 +34,7 @@ type SnapshotterFactory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorkerOpt creates a WorkerOpt.
|
// NewWorkerOpt creates a WorkerOpt.
|
||||||
func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, dns *oci.DNSConfig) (base.WorkerOpt, error) {
|
func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt network.Opt, dns *oci.DNSConfig) (base.WorkerOpt, error) {
|
||||||
var opt base.WorkerOpt
|
var opt base.WorkerOpt
|
||||||
name := "runc-" + snFactory.Name
|
name := "runc-" + snFactory.Name
|
||||||
root = filepath.Join(root, name)
|
root = filepath.Join(root, name)
|
||||||
|
@ -45,6 +45,12 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return opt, err
|
return opt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
np, err := network.Providers(nopt)
|
||||||
|
if err != nil {
|
||||||
|
return opt, err
|
||||||
|
}
|
||||||
|
|
||||||
exe, err := runcexecutor.New(runcexecutor.Opt{
|
exe, err := runcexecutor.New(runcexecutor.Opt{
|
||||||
// Root directory
|
// Root directory
|
||||||
Root: filepath.Join(root, "executor"),
|
Root: filepath.Join(root, "executor"),
|
||||||
|
@ -53,7 +59,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc
|
||||||
ProcessMode: processMode,
|
ProcessMode: processMode,
|
||||||
IdentityMapping: idmap,
|
IdentityMapping: idmap,
|
||||||
DNS: dns,
|
DNS: dns,
|
||||||
}, network.Default())
|
}, np)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return opt, err
|
return opt, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue