Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testing: test invocation of newer plugins with an older libcni #307

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcni/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ var _ = Describe("Invoking the plugin", func() {
}
Expect(debug.WriteDebug(debugFilePath)).To(Succeed())

cniBinPath = filepath.Dir(pathToPlugin)
cniBinPath = filepath.Dir(pluginPaths["noop"])
pluginConfig = `{ "type": "noop", "some-key": "some-value", "cniVersion": "0.2.0" }`
cniConfig = libcni.CNIConfig{Path: []string{cniBinPath}}
netConfig = &libcni.NetworkConfig{
Expand Down
30 changes: 30 additions & 0 deletions libcni/backwards_compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ package libcni_test
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() {
Expand Down Expand Up @@ -50,4 +53,31 @@ 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))
}
})
})
36 changes: 29 additions & 7 deletions libcni/libcni_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
package libcni_test

import (
"fmt"
"path/filepath"
"strings"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
Expand All @@ -27,17 +31,35 @@ func TestLibcni(t *testing.T) {
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() {
Expand Down
167 changes: 167 additions & 0 deletions pkg/version/legacy_examples/example_runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// 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
}
`,
},
}