import (
"fmt"
"os"
+ "os/exec"
"path/filepath"
+ "strings"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/version/legacy_examples"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
)
var _ = Describe("Backwards compatibility", func() {
Expect(os.RemoveAll(pluginPath)).To(Succeed())
})
+
+ It("correctly handles the request from a runtime with an older libcni", func() {
+ // We need to be root (or have CAP_SYS_ADMIN...)
+ if os.Geteuid() != 0 {
+ Fail("must be run as root")
+ }
+
+ example := legacy_examples.V010_Runtime
+
+ binPath, err := example.Build()
+ Expect(err).NotTo(HaveOccurred())
+
+ for _, configName := range example.NetConfs {
+ configStr, ok := legacy_examples.NetConfs[configName]
+ if !ok {
+ Fail("Invalid config name " + configName)
+ }
+
+ cmd := exec.Command(binPath, pluginDirs...)
+ cmd.Stdin = strings.NewReader(configStr)
+
+ session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).NotTo(HaveOccurred())
+
+ Eventually(session).Should(gexec.Exit(0))
+ }
+ })
})
package libcni_test
import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
RunSpecs(t, "Libcni Suite")
}
-const packagePath = "github.com/containernetworking/cni/plugins/test/noop"
+var plugins = map[string]string{
+ "noop": "github.com/containernetworking/cni/plugins/test/noop",
+ "ptp": "github.com/containernetworking/cni/plugins/main/ptp",
+ "host-local": "github.com/containernetworking/cni/plugins/ipam/host-local",
+}
-var pathToPlugin string
+var pluginPaths map[string]string
+var pluginDirs []string // array of plugin dirs
var _ = SynchronizedBeforeSuite(func() []byte {
- var err error
- pathToPlugin, err = gexec.Build(packagePath)
- Expect(err).NotTo(HaveOccurred())
- return []byte(pathToPlugin)
+ dirs := make([]string, 0, len(plugins))
+
+ for name, packagePath := range plugins {
+ execPath, err := gexec.Build(packagePath)
+ Expect(err).NotTo(HaveOccurred())
+ dirs = append(dirs, fmt.Sprintf("%s=%s", name, execPath))
+ }
+
+ return []byte(strings.Join(dirs, ":"))
}, func(crossNodeData []byte) {
- pathToPlugin = string(crossNodeData)
+ pluginPaths = make(map[string]string)
+ for _, str := range strings.Split(string(crossNodeData), ":") {
+ kvs := strings.SplitN(str, "=", 2)
+ if len(kvs) != 2 {
+ Fail("Invalid inter-node data...")
+ }
+ pluginPaths[kvs[0]] = kvs[1]
+ pluginDirs = append(pluginDirs, filepath.Dir(kvs[1]))
+ }
})
var _ = SynchronizedAfterSuite(func() {}, func() {
--- /dev/null
+// 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 legacy_examples
+
+// An ExampleRuntime is a small program that uses libcni to invoke a network plugin.
+// It should call ADD and DELETE, verifying all intermediate steps
+// and data structures.
+type ExampleRuntime struct {
+ Example
+ NetConfs []string // The network configuration names to pass
+}
+
+// NetConfs are various versioned network configuration files. Examples should
+// specify which version they expect
+var NetConfs = map[string]string{
+ "unversioned": `{
+ "name": "default",
+ "type": "ptp",
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.1.2.0/24"
+ }
+}`,
+ "0.1.0": `{
+ "cniVersion": "0.1.0",
+ "name": "default",
+ "type": "ptp",
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.1.2.0/24"
+ }
+}`,
+}
+
+// V010_Runtime creates a simple ptp network configuration, then
+// executes libcni against the currently-built plugins.
+var V010_Runtime = ExampleRuntime{
+ NetConfs: []string{"unversioned", "0.1.0"},
+ Example: Example{
+ Name: "example_invoker_v010",
+ CNIRepoGitRef: "c0d34c69", //version with ns.Do
+ PluginSource: `package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+
+ "github.com/containernetworking/cni/pkg/ns"
+ "github.com/containernetworking/cni/libcni"
+)
+
+func main(){
+ code := exec()
+ os.Exit(code)
+}
+
+func exec() int {
+ confBytes, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ fmt.Printf("could not read netconfig from stdin: %+v", err)
+ return 1
+ }
+
+ netConf, err := libcni.ConfFromBytes(confBytes)
+ if err != nil {
+ fmt.Printf("could not parse netconfig: %+v", err)
+ return 1
+ }
+ fmt.Printf("Parsed network configuration: %+v\n", netConf.Network)
+
+ if len(os.Args) == 1 {
+ fmt.Printf("Expect CNI plugin paths in argv")
+ return 1
+ }
+
+ targetNs, err := ns.NewNS()
+ if err != nil {
+ fmt.Printf("Could not create ns: %+v", err)
+ return 1
+ }
+ defer targetNs.Close()
+
+ ifName := "eth0"
+
+ runtimeConf := &libcni.RuntimeConf{
+ ContainerID: "some-container-id",
+ NetNS: targetNs.Path(),
+ IfName: ifName,
+ }
+
+ cniConfig := &libcni.CNIConfig{Path: os.Args[1:]}
+
+ result, err := cniConfig.AddNetwork(netConf, runtimeConf)
+ if err != nil {
+ fmt.Printf("AddNetwork failed: %+v", err)
+ return 2
+ }
+ fmt.Printf("AddNetwork result: %+v", result)
+
+ expectedIP := result.IP4.IP
+
+ err = targetNs.Do(func(ns.NetNS) error {
+ netif, err := net.InterfaceByName(ifName)
+ if err != nil {
+ return fmt.Errorf("could not retrieve interface: %v", err)
+ }
+
+ addrs, err := netif.Addrs()
+ if err != nil {
+ return fmt.Errorf("could not retrieve addresses, %+v", err)
+ }
+
+ found := false
+ for _, addr := range addrs {
+ if addr.String() == expectedIP.String() {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return fmt.Errorf("Far-side link did not have expected address %s", expectedIP)
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Println(err)
+ return 4
+ }
+
+ err = cniConfig.DelNetwork(netConf, runtimeConf)
+ if err != nil {
+ fmt.Printf("DelNetwork failed: %v", err)
+ return 5
+ }
+
+ err = targetNs.Do(func(ns.NetNS) error {
+ _, err := net.InterfaceByName(ifName)
+ if err == nil {
+ return fmt.Errorf("interface was not deleted")
+ }
+ return nil
+ })
+ if err != nil {
+ fmt.Println(err)
+ return 6
+ }
+
+ return 0
+}
+`,
+ },
+}