From 96d94008885bec873cb338d1b239fc7ae03565b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Mon, 19 Aug 2019 14:40:18 +0300 Subject: [PATCH 1/3] Use github.com/containerd/go-cni to setup CNI, instead of manually writing that code --- pkg/network/cni/cni.go | 235 +++++------------------------------ pkg/network/cni/types.go | 19 --- pkg/network/docker/docker.go | 10 +- pkg/network/types.go | 5 +- 4 files changed, 34 insertions(+), 235 deletions(-) delete mode 100644 pkg/network/cni/types.go diff --git a/pkg/network/cni/cni.go b/pkg/network/cni/cni.go index 26aec731e..c02e05cd6 100644 --- a/pkg/network/cni/cni.go +++ b/pkg/network/cni/cni.go @@ -3,166 +3,53 @@ package cni import ( "context" "fmt" - "sort" - "strings" - "sync" - "github.com/containernetworking/cni/libcni" - cnitypes "github.com/containernetworking/cni/pkg/types" - cnicurrentapi "github.com/containernetworking/cni/pkg/types/current" + gocni "github.com/containerd/go-cni" log "github.com/sirupsen/logrus" "github.com/weaveworks/ignite/pkg/network" "github.com/weaveworks/ignite/pkg/runtime" ) -// Disclaimer: This package is heavily influenced by -// https://github.com/kubernetes/kubernetes/blob/v1.15.0/pkg/kubelet/dockershim/network/cni/cni.go#L49 +const ( + // TODO: CNIBinDir and CNIConfDir should maybe be globally configurable? -type cniNetworkPlugin struct { - sync.RWMutex - - loNetwork *cniNetwork - defaultNetwork *cniNetwork + // CNIBinDir describes the directory where the CNI binaries are stored + CNIBinDir = "/opt/cni/bin" + // CNIConfDir describes the directory where the CNI plugin's configuration is stored + CNIConfDir = "/etc/cni/net.d" +) +type cniNetworkPlugin struct { + cni gocni.CNI runtime runtime.Interface - confDir string - binDirs []string -} - -type cniNetwork struct { - name string - NetworkConfig *libcni.NetworkConfigList - CNIConfig libcni.CNI } func GetCNINetworkPlugin(runtime runtime.Interface) (network.Plugin, error) { binDirs := []string{CNIBinDir} - plugin := &cniNetworkPlugin{ - runtime: runtime, - defaultNetwork: nil, - loNetwork: getLoNetwork(binDirs), - confDir: CNIConfDir, - binDirs: binDirs, - } - - return plugin, nil -} - -func getLoNetwork(binDirs []string) *cniNetwork { - loConfig, err := libcni.ConfListFromBytes([]byte(loopbackCNIConfig)) + cniInstance, err := gocni.New(gocni.WithMinNetworkCount(2), + gocni.WithPluginConfDir(CNIConfDir), + gocni.WithPluginDir(binDirs)) if err != nil { - // The hardcoded config above should always be valid and unit tests will - // catch this - panic(err) - } - - return &cniNetwork{ - name: "lo", - NetworkConfig: loConfig, - CNIConfig: &libcni.CNIConfig{Path: binDirs}, - } -} - -func getDefaultCNINetwork(confDir string, binDirs []string) (*cniNetwork, error) { - files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"}) - switch { - case err != nil: return nil, err - case len(files) == 0: - return nil, fmt.Errorf("no networks found in %s", confDir) } - sort.Strings(files) - for _, confFile := range files { - var confList *libcni.NetworkConfigList - if strings.HasSuffix(confFile, ".conflist") { - confList, err = libcni.ConfListFromFile(confFile) - if err != nil { - log.Infof("Error loading CNI config list file %s: %v", confFile, err) - continue - } - } else { - conf, err := libcni.ConfFromFile(confFile) - if err != nil { - log.Infof("Error loading CNI config file %s: %v", confFile, err) - continue - } - - // 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 == "" { - log.Infof("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile) - continue - } - - confList, err = libcni.ConfListFromConf(conf) - if err != nil { - log.Infof("Error converting CNI config file %s to list: %v", confFile, err) - continue - } - } - - if len(confList.Plugins) == 0 { - log.Infof("CNI config list %s has no networks, skipping", confFile) - continue - } - - log.Infof("Using CNI configuration file %s", confFile) - - network := &cniNetwork{ - name: confList.Name, - NetworkConfig: confList, - CNIConfig: &libcni.CNIConfig{Path: binDirs}, - } - - return network, nil - } - - return nil, fmt.Errorf("no valid networks found in %s", confDir) -} - -func (plugin *cniNetworkPlugin) syncNetworkConfig() error { - network, err := getDefaultCNINetwork(plugin.confDir, plugin.binDirs) - if err != nil { - return fmt.Errorf("unable to get default CNI network: %v", err) + if err := cniInstance.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil { + log.Errorf("failed to load cni configuration: %v", err) + return nil, err } - plugin.setDefaultNetwork(network) - return nil -} - -func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork { - plugin.RLock() - defer plugin.RUnlock() - return plugin.defaultNetwork -} - -func (plugin *cniNetworkPlugin) setDefaultNetwork(n *cniNetwork) { - plugin.Lock() - defer plugin.Unlock() - plugin.defaultNetwork = n -} - -func (plugin *cniNetworkPlugin) checkInitialized() error { - if plugin.getDefaultNetwork() == nil { - // Sync the network configuration if the plugin is not initialized - if err := plugin.syncNetworkConfig(); err != nil { - return err - } + plugin := &cniNetworkPlugin{ + runtime: runtime, + cni: cniInstance, } - return nil + return plugin, nil } func (plugin *cniNetworkPlugin) Name() network.PluginName { return network.PluginCNI } -func (plugin *cniNetworkPlugin) Status() error { - // Can't set up pods if we don't have any CNI network configs yet - return plugin.checkInitialized() -} - func (plugin *cniNetworkPlugin) PrepareContainerSpec(container *runtime.ContainerConfig) error { // No need for the container runtime to set up networking, as this plugin will do it container.NetworkMode = "none" @@ -170,97 +57,39 @@ func (plugin *cniNetworkPlugin) PrepareContainerSpec(container *runtime.Containe } func (plugin *cniNetworkPlugin) SetupContainerNetwork(containerid string) (*network.Result, error) { - if err := plugin.checkInitialized(); err != nil { - return nil, err - } - netnsPath, err := plugin.runtime.ContainerNetNS(containerid) if err != nil { return nil, fmt.Errorf("CNI failed to retrieve network namespace path: %v", err) } - if _, err = plugin.addToNetwork(plugin.loNetwork, containerid, netnsPath); err != nil { - return nil, err - } - - genericResult, err := plugin.addToNetwork(plugin.getDefaultNetwork(), containerid, netnsPath) - if err != nil { - return nil, err - } - result, err := cnicurrentapi.NewResultFromResult(genericResult) + result, err := plugin.cni.Setup(context.Background(), containerid, netnsPath) if err != nil { + log.Errorf("failed to setup network for namespace %q: %v", containerid, err) return nil, err } + return cniToIgniteResult(result), nil } -func cniToIgniteResult(r *cnicurrentapi.Result) *network.Result { +func cniToIgniteResult(r *gocni.CNIResult) *network.Result { result := &network.Result{} - for _, ip := range r.IPs { - result.Addresses = append(result.Addresses, network.Address{ - IPNet: ip.Address, - Gateway: ip.Gateway, - }) + for _, iface := range r.Interfaces { + for _, ip := range iface.IPConfigs { + result.Addresses = append(result.Addresses, network.Address{ + IP: ip.IP, + Gateway: ip.Gateway, + }) + } } return result } func (plugin *cniNetworkPlugin) RemoveContainerNetwork(containerid string) error { - if err := plugin.checkInitialized(); err != nil { - return err - } - // Lack of namespace should not be fatal on teardown netnsPath, err := plugin.runtime.ContainerNetNS(containerid) if err != nil { log.Infof("CNI failed to retrieve network namespace path: %v", err) } - return plugin.deleteFromNetwork(plugin.getDefaultNetwork(), containerid, netnsPath, nil) -} - -func (plugin *cniNetworkPlugin) addToNetwork(network *cniNetwork, containerID string, netnsPath string) (cnitypes.Result, error) { - rt, err := plugin.buildCNIRuntimeConf(containerID, netnsPath) - if err != nil { - return nil, fmt.Errorf("Error adding network when building cni runtime conf: %v", err) - } - - netConf, cniNet := network.NetworkConfig, network.CNIConfig - log.Debugf("Adding %s to network %s/%s netns %q", containerID, netConf.Plugins[0].Network.Type, netConf.Name, netnsPath) - res, err := cniNet.AddNetworkList(context.Background(), netConf, rt) - if err != nil { - return nil, fmt.Errorf("Error adding %s to network %s/%s: %v", containerID, netConf.Plugins[0].Network.Type, netConf.Name, err) - } - - log.Debugf("Added %s to network %s: %v", containerID, netConf.Name, res) - return res, nil -} - -func (plugin *cniNetworkPlugin) deleteFromNetwork(network *cniNetwork, containerID string, netnsPath string, annotations map[string]string) error { - rt, err := plugin.buildCNIRuntimeConf(containerID, netnsPath) - if err != nil { - return fmt.Errorf("Error deleting network when building cni runtime conf: %v", err) - } - - netConf, cniNet := network.NetworkConfig, network.CNIConfig - log.Debugf("Deleting %s from network %s/%s netns %q", containerID, netConf.Plugins[0].Network.Type, netConf.Name, netnsPath) - err = cniNet.DelNetworkList(context.Background(), netConf, rt) - // The pod may not get deleted successfully at the first time. - // Ignore "no such file or directory" error in case the network has already been deleted in previous attempts. - if err != nil && !strings.Contains(err.Error(), "no such file or directory") { - return fmt.Errorf("Error deleting %s from network %s/%s: %v", containerID, netConf.Plugins[0].Network.Type, netConf.Name, err) - } - - log.Debugf("Deleted %s from network %s/%s", containerID, netConf.Plugins[0].Network.Type, netConf.Name) - return nil -} - -func (plugin *cniNetworkPlugin) buildCNIRuntimeConf(containerID string, netnsPath string) (*libcni.RuntimeConf, error) { - rt := &libcni.RuntimeConf{ - ContainerID: containerID, - NetNS: netnsPath, - IfName: DefaultInterfaceName, - } - - return rt, nil + return plugin.cni.Remove(context.Background(), containerid, netnsPath) } diff --git a/pkg/network/cni/types.go b/pkg/network/cni/types.go deleted file mode 100644 index 0c5886129..000000000 --- a/pkg/network/cni/types.go +++ /dev/null @@ -1,19 +0,0 @@ -package cni - -const ( - // DefaultInterfaceName describes the interface name that the CNI network plugin will set up - DefaultInterfaceName = "eth0" - // CNIBinDir describes the directory where the CNI binaries are stored - CNIBinDir = "/opt/cni/bin" - // CNIConfDir describes the directory where the CNI plugin's configuration is stored - // TODO: CNIBinDir and CNIConfDir should maybe be globally configurable? - CNIConfDir = "/etc/cni/net.d" - - loopbackCNIConfig = `{ - "cniVersion": "0.2.0", - "name": "cni-loopback", - "plugins":[{ - "type": "loopback" - }] -}` -) diff --git a/pkg/network/docker/docker.go b/pkg/network/docker/docker.go index e9f8a8335..d321f477f 100644 --- a/pkg/network/docker/docker.go +++ b/pkg/network/docker/docker.go @@ -35,10 +35,7 @@ func (plugin *dockerNetworkPlugin) SetupContainerNetwork(containerID string) (*n return &network.Result{ Addresses: []network.Address{ { - IPNet: net.IPNet{ - IP: result.IPAddress, - Mask: net.IPv4Mask(255, 255, 0, 0), - }, + IP: result.IPAddress, // TODO: Make this auto-detect if the gateway is not using the standard setup Gateway: net.IPv4(result.IPAddress[0], result.IPAddress[1], result.IPAddress[2], 1), }, @@ -50,8 +47,3 @@ func (*dockerNetworkPlugin) RemoveContainerNetwork(_ string) error { // no-op for docker, this is handled automatically return nil } - -func (*dockerNetworkPlugin) Status() error { - // no-op, we assume the bridge to be working :) - return nil -} diff --git a/pkg/network/types.go b/pkg/network/types.go index fab50cbf3..d2b11a161 100644 --- a/pkg/network/types.go +++ b/pkg/network/types.go @@ -21,9 +21,6 @@ type Plugin interface { // RemoveContainerNetwork is the method called before a container using the network plugin can be deleted RemoveContainerNetwork(containerID string) error - - // Status returns error if the network plugin is in error state - Status() error } type Result struct { @@ -31,7 +28,7 @@ type Result struct { } type Address struct { - net.IPNet + IP net.IP Gateway net.IP } From 0a189276fb5e69ea749bd4c99ab28c46f6986ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Mon, 19 Aug 2019 14:40:30 +0300 Subject: [PATCH 2/3] autogenerated vendor --- go.mod | 3 +- go.sum | 3 + .../github.com/containerd/go-cni/.travis.yml | 22 ++ vendor/github.com/containerd/go-cni/LICENSE | 201 ++++++++++++++ vendor/github.com/containerd/go-cni/README.md | 60 +++++ vendor/github.com/containerd/go-cni/cni.go | 220 ++++++++++++++++ vendor/github.com/containerd/go-cni/errors.go | 55 ++++ vendor/github.com/containerd/go-cni/helper.go | 41 +++ .../github.com/containerd/go-cni/namespace.go | 77 ++++++ .../containerd/go-cni/namespace_opts.go | 67 +++++ vendor/github.com/containerd/go-cni/opts.go | 245 ++++++++++++++++++ vendor/github.com/containerd/go-cni/result.go | 106 ++++++++ .../github.com/containerd/go-cni/testutils.go | 78 ++++++ vendor/github.com/containerd/go-cni/types.go | 55 ++++ .../github.com/containerd/go-cni/vendor.conf | 6 + vendor/modules.txt | 2 + 16 files changed, 1240 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/containerd/go-cni/.travis.yml create mode 100644 vendor/github.com/containerd/go-cni/LICENSE create mode 100644 vendor/github.com/containerd/go-cni/README.md create mode 100644 vendor/github.com/containerd/go-cni/cni.go create mode 100644 vendor/github.com/containerd/go-cni/errors.go create mode 100644 vendor/github.com/containerd/go-cni/helper.go create mode 100644 vendor/github.com/containerd/go-cni/namespace.go create mode 100644 vendor/github.com/containerd/go-cni/namespace_opts.go create mode 100644 vendor/github.com/containerd/go-cni/opts.go create mode 100644 vendor/github.com/containerd/go-cni/result.go create mode 100644 vendor/github.com/containerd/go-cni/testutils.go create mode 100644 vendor/github.com/containerd/go-cni/types.go create mode 100644 vendor/github.com/containerd/go-cni/vendor.conf diff --git a/go.mod b/go.mod index e1e9baa89..5de927f2b 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,10 @@ require ( github.com/containerd/containerd v1.3.0-beta.1 github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 // indirect github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c // indirect + github.com/containerd/go-cni v0.0.0-20190813230227-49fbd9b210f3 github.com/containerd/ttrpc v0.0.0-20190613183316-1fb3814edf44 // indirect github.com/containerd/typeurl v0.0.0-20190515163108-7312978f2987 // indirect - github.com/containernetworking/cni v0.7.1 + github.com/containernetworking/cni v0.7.1 // indirect github.com/containers/image v2.0.0+incompatible github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/docker/distribution v2.7.1+incompatible // indirect diff --git a/go.sum b/go.sum index 91df75af2..2b4837cc3 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 h1:tN9D97v5A github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c h1:KFbqHhDeaHM7IfFtXHfUHMDaUStpM2YwBR+iJCIOsKk= github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-cni v0.0.0-20190813230227-49fbd9b210f3 h1:owkX+hC6Inv1XUep/pAjF7qJpnZWjbtETw5r1DVYFPo= +github.com/containerd/go-cni v0.0.0-20190813230227-49fbd9b210f3/go.mod h1:2wlRxCQdiBY+OcjNg5x8kI+5mEL1fGt25L4IzQHYJsM= github.com/containerd/ttrpc v0.0.0-20190613183316-1fb3814edf44 h1:vG5QXCUakUhR2CRI44aD3joCWcvb5mfZRxcwVqBVGeU= github.com/containerd/ttrpc v0.0.0-20190613183316-1fb3814edf44/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20190515163108-7312978f2987 h1:Qaux2AYCIF3t3gxqjFHDJbxWPhMphgBruE8ygIRHtBA= @@ -286,6 +288,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/containerd/go-cni/.travis.yml b/vendor/github.com/containerd/go-cni/.travis.yml new file mode 100644 index 000000000..61bae2150 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/.travis.yml @@ -0,0 +1,22 @@ +language: go +go: + - 1.12.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) diff --git a/vendor/github.com/containerd/go-cni/LICENSE b/vendor/github.com/containerd/go-cni/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/LICENSE @@ -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. diff --git a/vendor/github.com/containerd/go-cni/README.md b/vendor/github.com/containerd/go-cni/README.md new file mode 100644 index 000000000..1bd2f0013 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/README.md @@ -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. diff --git a/vendor/github.com/containerd/go-cni/cni.go b/vendor/github.com/containerd/go-cni/cni.go new file mode 100644 index 000000000..8acc83b33 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/cni.go @@ -0,0 +1,220 @@ +/* + 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 ( + "context" + "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(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error) + // Remove tears down the network of the namespace. + Remove(ctx context.Context, 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(ctx context.Context, 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(ctx, 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(ctx context.Context, 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(ctx, 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 +} diff --git a/vendor/github.com/containerd/go-cni/errors.go b/vendor/github.com/containerd/go-cni/errors.go new file mode 100644 index 000000000..28761711e --- /dev/null +++ b/vendor/github.com/containerd/go-cni/errors.go @@ -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 +} diff --git a/vendor/github.com/containerd/go-cni/helper.go b/vendor/github.com/containerd/go-cni/helper.go new file mode 100644 index 000000000..088cb9bc5 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/helper.go @@ -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 (nil)") + } + if ipConf.Interface != nil && *ipConf.Interface > ifs { + return fmt.Errorf("invalid IP configuration (interface number %d is > number of interfaces %d)", *ipConf.Interface, ifs) + } + 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) +} diff --git a/vendor/github.com/containerd/go-cni/namespace.go b/vendor/github.com/containerd/go-cni/namespace.go new file mode 100644 index 000000000..ff14b01c1 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/namespace.go @@ -0,0 +1,77 @@ +/* + 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 ( + "context" + + 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(ctx context.Context, ns *Namespace) (*current.Result, error) { + r, err := n.cni.AddNetworkList(ctx, n.config, ns.config(n.ifName)) + if err != nil { + return nil, err + } + return current.NewResultFromResult(r) +} + +func (n *Network) Remove(ctx context.Context, ns *Namespace) error { + return n.cni.DelNetworkList(ctx, 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 +} diff --git a/vendor/github.com/containerd/go-cni/namespace_opts.go b/vendor/github.com/containerd/go-cni/namespace_opts.go new file mode 100644 index 000000000..e8092e85e --- /dev/null +++ b/vendor/github.com/containerd/go-cni/namespace_opts.go @@ -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 + } +} diff --git a/vendor/github.com/containerd/go-cni/opts.go b/vendor/github.com/containerd/go-cni/opts.go new file mode 100644 index 000000000..5222df1e9 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/opts.go @@ -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 CNI config list: %v", confFile, err) + } + } + if len(confList.Plugins) == 0 { + return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %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 +} diff --git a/vendor/github.com/containerd/go-cni/result.go b/vendor/github.com/containerd/go-cni/result.go new file mode 100644 index 000000000..c2ac94864 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/result.go @@ -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, "invalid 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 for: %s", defaultInterface(c.prefix)) + } + 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) +} diff --git a/vendor/github.com/containerd/go-cni/testutils.go b/vendor/github.com/containerd/go-cni/testutils.go new file mode 100644 index 000000000..d9453c8d9 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/testutils.go @@ -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) + } +} diff --git a/vendor/github.com/containerd/go-cni/types.go b/vendor/github.com/containerd/go-cni/types.go new file mode 100644 index 000000000..8583050e4 --- /dev/null +++ b/vendor/github.com/containerd/go-cni/types.go @@ -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 +} diff --git a/vendor/github.com/containerd/go-cni/vendor.conf b/vendor/github.com/containerd/go-cni/vendor.conf new file mode 100644 index 000000000..31d06e0fc --- /dev/null +++ b/vendor/github.com/containerd/go-cni/vendor.conf @@ -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 v0.7.1 +github.com/pkg/errors v0.8.0 diff --git a/vendor/modules.txt b/vendor/modules.txt index 621bc84f3..c0347b803 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -85,6 +85,8 @@ github.com/containerd/continuity/sysx github.com/containerd/continuity/syscallx # github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c github.com/containerd/fifo +# github.com/containerd/go-cni v0.0.0-20190813230227-49fbd9b210f3 +github.com/containerd/go-cni # github.com/containerd/ttrpc v0.0.0-20190613183316-1fb3814edf44 github.com/containerd/ttrpc # github.com/containerd/typeurl v0.0.0-20190515163108-7312978f2987 From fbc060bbdb2c261b6380c63954a4ba8fd9682666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Mon, 19 Aug 2019 15:15:34 +0300 Subject: [PATCH 3/3] Initialize the CNI plugin first when using it --- pkg/network/cni/cni.go | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/pkg/network/cni/cni.go b/pkg/network/cni/cni.go index c02e05cd6..467cc63bc 100644 --- a/pkg/network/cni/cni.go +++ b/pkg/network/cni/cni.go @@ -3,6 +3,7 @@ package cni import ( "context" "fmt" + "sync" gocni "github.com/containerd/go-cni" log "github.com/sirupsen/logrus" @@ -22,6 +23,7 @@ const ( type cniNetworkPlugin struct { cni gocni.CNI runtime runtime.Interface + once *sync.Once } func GetCNINetworkPlugin(runtime runtime.Interface) (network.Plugin, error) { @@ -33,17 +35,11 @@ func GetCNINetworkPlugin(runtime runtime.Interface) (network.Plugin, error) { return nil, err } - if err := cniInstance.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil { - log.Errorf("failed to load cni configuration: %v", err) - return nil, err - } - - plugin := &cniNetworkPlugin{ + return &cniNetworkPlugin{ runtime: runtime, cni: cniInstance, - } - - return plugin, nil + once: &sync.Once{}, + }, nil } func (plugin *cniNetworkPlugin) Name() network.PluginName { @@ -57,6 +53,10 @@ func (plugin *cniNetworkPlugin) PrepareContainerSpec(container *runtime.Containe } func (plugin *cniNetworkPlugin) SetupContainerNetwork(containerid string) (*network.Result, error) { + if err := plugin.initialize(); err != nil { + return nil, err + } + netnsPath, err := plugin.runtime.ContainerNetNS(containerid) if err != nil { return nil, fmt.Errorf("CNI failed to retrieve network namespace path: %v", err) @@ -71,6 +71,15 @@ func (plugin *cniNetworkPlugin) SetupContainerNetwork(containerid string) (*netw return cniToIgniteResult(result), nil } +func (plugin *cniNetworkPlugin) initialize() (err error) { + plugin.once.Do(func() { + if err = plugin.cni.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil { + log.Errorf("failed to load cni configuration: %v", err) + } + }) + return +} + func cniToIgniteResult(r *gocni.CNIResult) *network.Result { result := &network.Result{} for _, iface := range r.Interfaces { @@ -85,6 +94,10 @@ func cniToIgniteResult(r *gocni.CNIResult) *network.Result { } func (plugin *cniNetworkPlugin) RemoveContainerNetwork(containerid string) error { + if err := plugin.initialize(); err != nil { + return err + } + // Lack of namespace should not be fatal on teardown netnsPath, err := plugin.runtime.ContainerNetNS(containerid) if err != nil {