testing: adds basic test of backwards compatibility
authorGabe Rosenhouse <grosenhouse@pivotal.io>
Mon, 3 Oct 2016 04:43:51 +0000 (21:43 -0700)
committerGabe Rosenhouse <grosenhouse@pivotal.io>
Mon, 3 Oct 2016 05:18:53 +0000 (22:18 -0700)
libcni/backwards_compatibility_test.go [new file with mode: 0644]
pkg/version/legacy_examples/examples.go [new file with mode: 0644]
pkg/version/legacy_examples/legacy_examples_suite_test.go [new file with mode: 0644]
pkg/version/legacy_examples/legacy_examples_test.go [new file with mode: 0644]

diff --git a/libcni/backwards_compatibility_test.go b/libcni/backwards_compatibility_test.go
new file mode 100644 (file)
index 0000000..c13b23a
--- /dev/null
@@ -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 libcni_test
+
+import (
+       "fmt"
+       "os"
+       "path/filepath"
+
+       "github.com/containernetworking/cni/libcni"
+       "github.com/containernetworking/cni/pkg/version/legacy_examples"
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Backwards compatibility", func() {
+       It("correctly handles the response from a legacy plugin", func() {
+               example := legacy_examples.V010
+               pluginPath, err := example.Build()
+               Expect(err).NotTo(HaveOccurred())
+
+               netConf, err := libcni.ConfFromBytes([]byte(fmt.Sprintf(
+                       `{ "name": "old-thing", "type": "%s" }`, example.Name)))
+               Expect(err).NotTo(HaveOccurred())
+
+               runtimeConf := &libcni.RuntimeConf{
+                       ContainerID: "some-container-id",
+                       NetNS:       "/some/netns/path",
+                       IfName:      "eth0",
+               }
+
+               cniConfig := &libcni.CNIConfig{Path: []string{filepath.Dir(pluginPath)}}
+
+               result, err := cniConfig.AddNetwork(netConf, runtimeConf)
+               Expect(err).NotTo(HaveOccurred())
+
+               Expect(result).To(Equal(legacy_examples.ExpectedResult))
+
+               Expect(os.RemoveAll(pluginPath)).To(Succeed())
+       })
+})
diff --git a/pkg/version/legacy_examples/examples.go b/pkg/version/legacy_examples/examples.go
new file mode 100644 (file)
index 0000000..5716231
--- /dev/null
@@ -0,0 +1,138 @@
+// 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 contains sample code from prior versions of
+// the CNI library, for use in verifying backwards compatibility.
+package legacy_examples
+
+import (
+       "io/ioutil"
+       "net"
+       "path/filepath"
+       "sync"
+
+       "github.com/containernetworking/cni/pkg/types"
+       "github.com/containernetworking/cni/pkg/version/testhelpers"
+)
+
+// An Example is a Git reference to the CNI repo and a Golang CNI plugin that
+// builds against that version of the repo.
+//
+// By convention, every Example plugin returns an ADD result that is
+// semantically equivalent to the ExpectedResult.
+type Example struct {
+       Name          string
+       CNIRepoGitRef string
+       PluginSource  string
+}
+
+var buildDir = ""
+var buildDirLock sync.Mutex
+
+func ensureBuildDirExists() error {
+       buildDirLock.Lock()
+       defer buildDirLock.Unlock()
+
+       if buildDir != "" {
+               return nil
+       }
+
+       var err error
+       buildDir, err = ioutil.TempDir("", "cni-example-plugins")
+       return err
+}
+
+// Build builds the example, returning the path to the binary
+func (e Example) Build() (string, error) {
+       if err := ensureBuildDirExists(); err != nil {
+               return "", err
+       }
+
+       outBinPath := filepath.Join(buildDir, e.Name)
+
+       if err := testhelpers.BuildAt([]byte(e.PluginSource), e.CNIRepoGitRef, outBinPath); err != nil {
+               return "", err
+       }
+       return outBinPath, nil
+}
+
+// V010 acts like a CNI plugin from the v0.1.0 era
+var V010 = Example{
+       Name:          "example_v010",
+       CNIRepoGitRef: "2c482f4",
+       PluginSource: `package main
+
+import (
+       "net"
+
+       "github.com/containernetworking/cni/pkg/skel"
+       "github.com/containernetworking/cni/pkg/types"
+)
+
+var result = types.Result{
+       IP4: &types.IPConfig{
+               IP: net.IPNet{
+                       IP:   net.ParseIP("10.1.2.3"),
+                       Mask: net.CIDRMask(24, 32),
+               },
+               Gateway: net.ParseIP("10.1.2.1"),
+               Routes: []types.Route{
+                       types.Route{
+                               Dst: net.IPNet{
+                                       IP:   net.ParseIP("0.0.0.0"),
+                                       Mask: net.CIDRMask(0, 32),
+                               },
+                               GW: net.ParseIP("10.1.0.1"),
+                       },
+               },
+       },
+       DNS: types.DNS{
+               Nameservers: []string{"8.8.8.8"},
+               Domain:      "example.com",
+       },
+}
+
+func c(_ *skel.CmdArgs) error { result.Print(); return nil }
+
+func main() { skel.PluginMain(c, c) }
+`,
+}
+
+// ExpectedResult is the current representation of the plugin result
+// that is expected from each of the examples.
+//
+// As we change the CNI spec, the Result type and this value may change.
+// The text of the example plugins should not.
+var ExpectedResult = &types.Result{
+       IP4: &types.IPConfig{
+               IP: net.IPNet{
+                       IP:   net.ParseIP("10.1.2.3"),
+                       Mask: net.CIDRMask(24, 32),
+               },
+               Gateway: net.ParseIP("10.1.2.1"),
+               Routes: []types.Route{
+                       types.Route{
+                               Dst: net.IPNet{
+                                       IP:   net.ParseIP("0.0.0.0"),
+                                       Mask: net.CIDRMask(0, 32),
+                               },
+                               GW: net.ParseIP("10.1.0.1"),
+                       },
+               },
+       },
+       DNS: types.DNS{
+               Nameservers: []string{"8.8.8.8"},
+               Domain:      "example.com",
+       },
+}
diff --git a/pkg/version/legacy_examples/legacy_examples_suite_test.go b/pkg/version/legacy_examples/legacy_examples_suite_test.go
new file mode 100644 (file)
index 0000000..a126531
--- /dev/null
@@ -0,0 +1,27 @@
+// 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_test
+
+import (
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+
+       "testing"
+)
+
+func TestLegacyExamples(t *testing.T) {
+       RegisterFailHandler(Fail)
+       RunSpecs(t, "LegacyExamples Suite")
+}
diff --git a/pkg/version/legacy_examples/legacy_examples_test.go b/pkg/version/legacy_examples/legacy_examples_test.go
new file mode 100644 (file)
index 0000000..4115105
--- /dev/null
@@ -0,0 +1,36 @@
+// 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_test
+
+import (
+       "os"
+       "path/filepath"
+
+       "github.com/containernetworking/cni/pkg/version/legacy_examples"
+       . "github.com/onsi/ginkgo"
+       . "github.com/onsi/gomega"
+)
+
+var _ = Describe("The v0.1.0 Example", func() {
+       It("builds ok", func() {
+               example := legacy_examples.V010
+               pluginPath, err := example.Build()
+               Expect(err).NotTo(HaveOccurred())
+
+               Expect(filepath.Base(pluginPath)).To(Equal(example.Name))
+
+               Expect(os.RemoveAll(pluginPath)).To(Succeed())
+       })
+})