diff --git a/pkg/invoke/args.go b/pkg/invoke/args.go deleted file mode 100644 index ba9d0c3b..00000000 --- a/pkg/invoke/args.go +++ /dev/null @@ -1,79 +0,0 @@ -// 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) - } - - env = append(env, - "CNI_COMMAND="+args.Command, - "CNI_CONTAINERID="+args.ContainerID, - "CNI_NETNS="+args.NetNS, - "CNI_ARGS="+pluginArgsStr, - "CNI_IFNAME="+args.IfName, - "CNI_PATH="+args.Path) - 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, ";") -} diff --git a/pkg/invoke/delegate.go b/pkg/invoke/delegate.go deleted file mode 100644 index c78a69ee..00000000 --- a/pkg/invoke/delegate.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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()) -} diff --git a/pkg/invoke/exec.go b/pkg/invoke/exec.go deleted file mode 100644 index fc47e7c8..00000000 --- a/pkg/invoke/exec.go +++ /dev/null @@ -1,95 +0,0 @@ -// 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) -} diff --git a/pkg/invoke/exec_test.go b/pkg/invoke/exec_test.go deleted file mode 100644 index 33ffc2de..00000000 --- a/pkg/invoke/exec_test.go +++ /dev/null @@ -1,157 +0,0 @@ -// 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_test - -import ( - "encoding/json" - "errors" - - "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/invoke/fakes" - "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/version" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Executing a plugin, unit tests", func() { - var ( - pluginExec *invoke.PluginExec - rawExec *fakes.RawExec - versionDecoder *fakes.VersionDecoder - - pluginPath string - netconf []byte - cniargs *fakes.CNIArgs - ) - - BeforeEach(func() { - rawExec = &fakes.RawExec{} - rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`) - - versionDecoder = &fakes.VersionDecoder{} - versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0") - - pluginExec = &invoke.PluginExec{ - RawExec: rawExec, - VersionDecoder: versionDecoder, - } - pluginPath = "/some/plugin/path" - netconf = []byte(`{ "some": "stdin", "cniVersion": "0.3.1" }`) - cniargs = &fakes.CNIArgs{} - cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"} - }) - - Describe("returning a result", func() { - It("unmarshals the result bytes into the Result type", func() { - r, err := pluginExec.WithResult(pluginPath, netconf, cniargs) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4")) - }) - - It("passes its arguments through to the rawExec", func() { - pluginExec.WithResult(pluginPath, netconf, cniargs) - Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath)) - Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf)) - Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"})) - }) - - Context("when the rawExec fails", func() { - BeforeEach(func() { - rawExec.ExecPluginCall.Returns.Error = errors.New("banana") - }) - It("returns the error", func() { - _, err := pluginExec.WithResult(pluginPath, netconf, cniargs) - Expect(err).To(MatchError("banana")) - }) - }) - }) - - Describe("without returning a result", func() { - It("passes its arguments through to the rawExec", func() { - pluginExec.WithoutResult(pluginPath, netconf, cniargs) - Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath)) - Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf)) - Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"})) - }) - - Context("when the rawExec fails", func() { - BeforeEach(func() { - rawExec.ExecPluginCall.Returns.Error = errors.New("banana") - }) - It("returns the error", func() { - err := pluginExec.WithoutResult(pluginPath, netconf, cniargs) - Expect(err).To(MatchError("banana")) - }) - }) - }) - - Describe("discovering the plugin version", func() { - BeforeEach(func() { - rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`) - }) - - It("execs the plugin with the command VERSION", func() { - pluginExec.GetVersionInfo(pluginPath) - Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath)) - Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION")) - expectedStdin, _ := json.Marshal(map[string]string{"cniVersion": version.Current()}) - Expect(rawExec.ExecPluginCall.Received.StdinData).To(MatchJSON(expectedStdin)) - }) - - It("decodes and returns the version info", func() { - versionInfo, err := pluginExec.GetVersionInfo(pluginPath) - Expect(err).NotTo(HaveOccurred()) - Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"})) - Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`)) - }) - - Context("when the rawExec fails", func() { - BeforeEach(func() { - rawExec.ExecPluginCall.Returns.Error = errors.New("banana") - }) - It("returns the error", func() { - _, err := pluginExec.GetVersionInfo(pluginPath) - Expect(err).To(MatchError("banana")) - }) - }) - - Context("when the plugin is too old to recognize the VERSION command", func() { - BeforeEach(func() { - rawExec.ExecPluginCall.Returns.Error = errors.New("unknown CNI_COMMAND: VERSION") - }) - - It("interprets the error as a 0.1.0 version", func() { - versionInfo, err := pluginExec.GetVersionInfo(pluginPath) - Expect(err).NotTo(HaveOccurred()) - Expect(versionInfo.SupportedVersions()).To(ConsistOf("0.1.0")) - }) - - It("sets dummy values for env vars required by very old plugins", func() { - pluginExec.GetVersionInfo(pluginPath) - - env := rawExec.ExecPluginCall.Received.Environ - Expect(env).To(ContainElement("CNI_NETNS=dummy")) - Expect(env).To(ContainElement("CNI_IFNAME=dummy")) - Expect(env).To(ContainElement("CNI_PATH=dummy")) - }) - }) - }) -}) diff --git a/pkg/invoke/fakes/cni_args.go b/pkg/invoke/fakes/cni_args.go deleted file mode 100644 index 5b1ba29e..00000000 --- a/pkg/invoke/fakes/cni_args.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 fakes - -type CNIArgs struct { - AsEnvCall struct { - Returns struct { - Env []string - } - } -} - -func (a *CNIArgs) AsEnv() []string { - return a.AsEnvCall.Returns.Env -} diff --git a/pkg/invoke/fakes/raw_exec.go b/pkg/invoke/fakes/raw_exec.go deleted file mode 100644 index 5432cdf7..00000000 --- a/pkg/invoke/fakes/raw_exec.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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 fakes - -type RawExec struct { - ExecPluginCall struct { - Received struct { - PluginPath string - StdinData []byte - Environ []string - } - Returns struct { - ResultBytes []byte - Error error - } - } -} - -func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { - e.ExecPluginCall.Received.PluginPath = pluginPath - e.ExecPluginCall.Received.StdinData = stdinData - e.ExecPluginCall.Received.Environ = environ - return e.ExecPluginCall.Returns.ResultBytes, e.ExecPluginCall.Returns.Error -} diff --git a/pkg/invoke/fakes/version_decoder.go b/pkg/invoke/fakes/version_decoder.go deleted file mode 100644 index 72d29733..00000000 --- a/pkg/invoke/fakes/version_decoder.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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 fakes - -import "github.com/containernetworking/cni/pkg/version" - -type VersionDecoder struct { - DecodeCall struct { - Received struct { - JSONBytes []byte - } - Returns struct { - PluginInfo version.PluginInfo - Error error - } - } -} - -func (e *VersionDecoder) Decode(jsonData []byte) (version.PluginInfo, error) { - e.DecodeCall.Received.JSONBytes = jsonData - return e.DecodeCall.Returns.PluginInfo, e.DecodeCall.Returns.Error -} diff --git a/pkg/invoke/find.go b/pkg/invoke/find.go deleted file mode 100644 index e815404c..00000000 --- a/pkg/invoke/find.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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) -} diff --git a/pkg/invoke/find_test.go b/pkg/invoke/find_test.go deleted file mode 100644 index 58543131..00000000 --- a/pkg/invoke/find_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// 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_test - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/containernetworking/cni/pkg/invoke" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("FindInPath", func() { - var ( - multiplePaths []string - pluginName string - plugin2NameWithExt string - plugin2NameWithoutExt string - pluginDir string - anotherTempDir string - ) - - BeforeEach(func() { - tempDir, err := ioutil.TempDir("", "cni-find") - Expect(err).NotTo(HaveOccurred()) - - plugin, err := ioutil.TempFile(tempDir, "a-cni-plugin") - Expect(err).NotTo(HaveOccurred()) - - plugin2Name := "a-plugin-with-extension" + invoke.ExecutableFileExtensions[0] - plugin2, err := os.Create(filepath.Join(tempDir, plugin2Name)) - Expect(err).NotTo(HaveOccurred()) - - anotherTempDir, err = ioutil.TempDir("", "nothing-here") - Expect(err).NotTo(HaveOccurred()) - - multiplePaths = []string{anotherTempDir, tempDir} - pluginDir, pluginName = filepath.Split(plugin.Name()) - _, plugin2NameWithExt = filepath.Split(plugin2.Name()) - plugin2NameWithoutExt = strings.Split(plugin2NameWithExt, ".")[0] - }) - - AfterEach(func() { - os.RemoveAll(pluginDir) - os.RemoveAll(anotherTempDir) - }) - - Context("when multiple paths are provided", func() { - It("returns only the path to the plugin", func() { - pluginPath, err := invoke.FindInPath(pluginName, multiplePaths) - Expect(err).NotTo(HaveOccurred()) - Expect(pluginPath).To(Equal(filepath.Join(pluginDir, pluginName))) - }) - }) - - Context("when a plugin name without its file name extension is provided", func() { - It("returns the path to the plugin, including its extension", func() { - pluginPath, err := invoke.FindInPath(plugin2NameWithoutExt, multiplePaths) - Expect(err).NotTo(HaveOccurred()) - Expect(pluginPath).To(Equal(filepath.Join(pluginDir, plugin2NameWithExt))) - }) - }) - - Context("when an error occurs", func() { - Context("when no paths are provided", func() { - It("returns an error noting no paths were provided", func() { - _, err := invoke.FindInPath(pluginName, []string{}) - Expect(err).To(MatchError("no paths provided")) - }) - }) - - Context("when no plugin is provided", func() { - It("returns an error noting the plugin name wasn't found", func() { - _, err := invoke.FindInPath("", multiplePaths) - Expect(err).To(MatchError("no plugin name provided")) - }) - }) - - Context("when the plugin cannot be found", func() { - It("returns an error noting the path", func() { - pathsWithNothing := []string{anotherTempDir} - _, err := invoke.FindInPath(pluginName, pathsWithNothing) - Expect(err).To(MatchError(fmt.Sprintf("failed to find plugin %q in path %s", pluginName, pathsWithNothing))) - }) - }) - }) -}) diff --git a/pkg/invoke/get_version_integration_test.go b/pkg/invoke/get_version_integration_test.go deleted file mode 100644 index 7e58a9be..00000000 --- a/pkg/invoke/get_version_integration_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// 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_test - -import ( - "io/ioutil" - "os" - "path/filepath" - - "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/version" - "github.com/containernetworking/cni/pkg/version/testhelpers" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" -) - -var _ = Describe("GetVersion, integration tests", func() { - var ( - pluginDir string - pluginPath string - ) - - BeforeEach(func() { - pluginDir, err := ioutil.TempDir("", "plugins") - Expect(err).NotTo(HaveOccurred()) - pluginPath = filepath.Join(pluginDir, "test-plugin") - }) - - AfterEach(func() { - Expect(os.RemoveAll(pluginDir)).To(Succeed()) - }) - - DescribeTable("correctly reporting plugin versions", - func(gitRef string, pluginSource string, expectedVersions version.PluginInfo) { - Expect(testhelpers.BuildAt([]byte(pluginSource), gitRef, pluginPath)).To(Succeed()) - versionInfo, err := invoke.GetVersionInfo(pluginPath) - Expect(err).NotTo(HaveOccurred()) - - Expect(versionInfo.SupportedVersions()).To(ConsistOf(expectedVersions.SupportedVersions())) - }, - - Entry("historical: before VERSION was introduced", - git_ref_v010, plugin_source_no_custom_versions, - version.PluginSupports("0.1.0"), - ), - - Entry("historical: when VERSION was introduced but plugins couldn't customize it", - git_ref_v020_no_custom_versions, plugin_source_no_custom_versions, - version.PluginSupports("0.1.0", "0.2.0"), - ), - - Entry("historical: when plugins started reporting their own version list", - git_ref_v020_custom_versions, plugin_source_v020_custom_versions, - version.PluginSupports("0.2.0", "0.999.0"), - ), - - // this entry tracks the current behavior. Before you change it, ensure - // that its previous behavior is captured in the most recent "historical" entry - Entry("current", - "HEAD", plugin_source_v020_custom_versions, - version.PluginSupports("0.2.0", "0.999.0"), - ), - ) -}) - -// a 0.2.0 plugin that can report its own versions -const plugin_source_v020_custom_versions = `package main - -import ( - "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/version" - "fmt" -) - -func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil } - -func main() { skel.PluginMain(c, c, version.PluginSupports("0.2.0", "0.999.0")) } -` -const git_ref_v020_custom_versions = "bf31ed15" - -// a minimal 0.1.0 / 0.2.0 plugin that cannot report it's own version support -const plugin_source_no_custom_versions = `package main - -import "github.com/containernetworking/cni/pkg/skel" -import "fmt" - -func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil } - -func main() { skel.PluginMain(c, c) } -` - -const git_ref_v010 = "2c482f4" -const git_ref_v020_no_custom_versions = "349d66d" diff --git a/pkg/invoke/invoke_suite_test.go b/pkg/invoke/invoke_suite_test.go deleted file mode 100644 index 7285878a..00000000 --- a/pkg/invoke/invoke_suite_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// 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_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" - - "testing" -) - -func TestInvoke(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Invoke Suite") -} - -const packagePath = "github.com/containernetworking/cni/plugins/test/noop" - -var pathToPlugin string - -var _ = SynchronizedBeforeSuite(func() []byte { - var err error - pathToPlugin, err = gexec.Build(packagePath) - Expect(err).NotTo(HaveOccurred()) - return []byte(pathToPlugin) -}, func(crossNodeData []byte) { - pathToPlugin = string(crossNodeData) -}) - -var _ = SynchronizedAfterSuite(func() {}, func() { - gexec.CleanupBuildArtifacts() -}) diff --git a/pkg/invoke/os_unix.go b/pkg/invoke/os_unix.go deleted file mode 100644 index bab5737a..00000000 --- a/pkg/invoke/os_unix.go +++ /dev/null @@ -1,20 +0,0 @@ -// 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{""} diff --git a/pkg/invoke/os_windows.go b/pkg/invoke/os_windows.go deleted file mode 100644 index 7665125b..00000000 --- a/pkg/invoke/os_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -// 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", ""} diff --git a/pkg/invoke/raw_exec.go b/pkg/invoke/raw_exec.go deleted file mode 100644 index d1bd860d..00000000 --- a/pkg/invoke/raw_exec.go +++ /dev/null @@ -1,63 +0,0 @@ -// 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 { - return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr) - } - details := "" - if emsg.Details != "" { - details = fmt.Sprintf("; %v", emsg.Details) - } - return fmt.Errorf("%v%v", emsg.Msg, details) - } - - return err -} diff --git a/pkg/invoke/raw_exec_test.go b/pkg/invoke/raw_exec_test.go deleted file mode 100644 index 5d759f24..00000000 --- a/pkg/invoke/raw_exec_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// 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_test - -import ( - "bytes" - "io/ioutil" - "os" - - "github.com/containernetworking/cni/pkg/invoke" - - noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("RawExec", func() { - var ( - debugFileName string - debug *noop_debug.Debug - environ []string - stdin []byte - execer *invoke.RawExec - ) - - const reportResult = `{ "some": "result" }` - - BeforeEach(func() { - debugFile, err := ioutil.TempFile("", "cni_debug") - Expect(err).NotTo(HaveOccurred()) - Expect(debugFile.Close()).To(Succeed()) - debugFileName = debugFile.Name() - - debug = &noop_debug.Debug{ - ReportResult: reportResult, - ReportStderr: "some stderr message", - } - Expect(debug.WriteDebug(debugFileName)).To(Succeed()) - - environ = []string{ - "CNI_COMMAND=ADD", - "CNI_CONTAINERID=some-container-id", - "CNI_ARGS=DEBUG=" + debugFileName, - "CNI_NETNS=/some/netns/path", - "CNI_PATH=/some/bin/path", - "CNI_IFNAME=some-eth0", - } - stdin = []byte(`{"some":"stdin-json", "cniVersion": "0.3.1"}`) - execer = &invoke.RawExec{} - }) - - AfterEach(func() { - Expect(os.Remove(debugFileName)).To(Succeed()) - }) - - It("runs the plugin with the given stdin and environment", func() { - _, err := execer.ExecPlugin(pathToPlugin, stdin, environ) - Expect(err).NotTo(HaveOccurred()) - - debug, err := noop_debug.ReadDebug(debugFileName) - Expect(err).NotTo(HaveOccurred()) - Expect(debug.Command).To(Equal("ADD")) - Expect(debug.CmdArgs.StdinData).To(Equal(stdin)) - Expect(debug.CmdArgs.Netns).To(Equal("/some/netns/path")) - }) - - It("returns the resulting stdout as bytes", func() { - resultBytes, err := execer.ExecPlugin(pathToPlugin, stdin, environ) - Expect(err).NotTo(HaveOccurred()) - - Expect(resultBytes).To(BeEquivalentTo(reportResult)) - }) - - Context("when the Stderr writer is set", func() { - var stderrBuffer *bytes.Buffer - - BeforeEach(func() { - stderrBuffer = &bytes.Buffer{} - execer.Stderr = stderrBuffer - }) - - It("forwards any stderr bytes to the Stderr writer", func() { - _, err := execer.ExecPlugin(pathToPlugin, stdin, environ) - Expect(err).NotTo(HaveOccurred()) - - Expect(stderrBuffer.String()).To(Equal("some stderr message")) - }) - }) - - Context("when the plugin errors", func() { - BeforeEach(func() { - debug.ReportError = "banana" - Expect(debug.WriteDebug(debugFileName)).To(Succeed()) - }) - - It("wraps and returns the error", func() { - _, err := execer.ExecPlugin(pathToPlugin, stdin, environ) - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("banana")) - }) - }) - - Context("when the system is unable to execute the plugin", func() { - It("returns the error", func() { - _, err := execer.ExecPlugin("/tmp/some/invalid/plugin/path", stdin, environ) - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("/tmp/some/invalid/plugin/path"))) - }) - }) -}) diff --git a/pkg/ip/link.go b/pkg/ip/link.go index a9842627..fb8d3d50 100644 --- a/pkg/ip/link.go +++ b/pkg/ip/link.go @@ -21,8 +21,8 @@ import ( "net" "os" - "github.com/containernetworking/cni/pkg/ns" - "github.com/containernetworking/cni/pkg/utils/hwaddr" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/utils/hwaddr" "github.com/vishvananda/netlink" ) diff --git a/pkg/ip/link_test.go b/pkg/ip/link_test.go index 23182a54..a20a1582 100644 --- a/pkg/ip/link_test.go +++ b/pkg/ip/link_test.go @@ -23,8 +23,8 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ns" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index b76780f0..54f80c0e 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -20,9 +20,9 @@ import ( "os" "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/ip" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ip" "github.com/vishvananda/netlink" ) diff --git a/pkg/ipam/ipam_test.go b/pkg/ipam/ipam_test.go index 2d27825d..844867f2 100644 --- a/pkg/ipam/ipam_test.go +++ b/pkg/ipam/ipam_test.go @@ -18,9 +18,9 @@ import ( "net" "syscall" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" diff --git a/pkg/ns/ns_test.go b/pkg/ns/ns_test.go index 44ed2728..e47517da 100644 --- a/pkg/ns/ns_test.go +++ b/pkg/ns/ns_test.go @@ -21,7 +21,7 @@ import ( "os" "path/filepath" - "github.com/containernetworking/cni/pkg/ns" + "github.com/containernetworking/plugins/pkg/ns" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "golang.org/x/sys/unix" diff --git a/pkg/skel/skel.go b/pkg/skel/skel.go deleted file mode 100644 index 8644c25e..00000000 --- a/pkg/skel/skel.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2014-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 skel provides skeleton code for a CNI plugin. -// In particular, it implements argument parsing and validation. -package skel - -import ( - "fmt" - "io" - "io/ioutil" - "log" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/version" -) - -// CmdArgs captures all the arguments passed in to the plugin -// via both env vars and stdin -type CmdArgs struct { - ContainerID string - Netns string - IfName string - Args string - Path string - StdinData []byte -} - -type dispatcher struct { - Getenv func(string) string - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - - ConfVersionDecoder version.ConfigDecoder - VersionReconciler version.Reconciler -} - -type reqForCmdEntry map[string]bool - -func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) { - var cmd, contID, netns, ifName, args, path string - - vars := []struct { - name string - val *string - reqForCmd reqForCmdEntry - }{ - { - "CNI_COMMAND", - &cmd, - reqForCmdEntry{ - "ADD": true, - "DEL": true, - }, - }, - { - "CNI_CONTAINERID", - &contID, - reqForCmdEntry{ - "ADD": false, - "DEL": false, - }, - }, - { - "CNI_NETNS", - &netns, - reqForCmdEntry{ - "ADD": true, - "DEL": false, - }, - }, - { - "CNI_IFNAME", - &ifName, - reqForCmdEntry{ - "ADD": true, - "DEL": true, - }, - }, - { - "CNI_ARGS", - &args, - reqForCmdEntry{ - "ADD": false, - "DEL": false, - }, - }, - { - "CNI_PATH", - &path, - reqForCmdEntry{ - "ADD": true, - "DEL": true, - }, - }, - } - - argsMissing := false - for _, v := range vars { - *v.val = t.Getenv(v.name) - if *v.val == "" { - if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { - fmt.Fprintf(t.Stderr, "%v env variable missing\n", v.name) - argsMissing = true - } - } - } - - if argsMissing { - return "", nil, fmt.Errorf("required env variables missing") - } - - stdinData, err := ioutil.ReadAll(t.Stdin) - if err != nil { - return "", nil, fmt.Errorf("error reading from stdin: %v", err) - } - - cmdArgs := &CmdArgs{ - ContainerID: contID, - Netns: netns, - IfName: ifName, - Args: args, - Path: path, - StdinData: stdinData, - } - return cmd, cmdArgs, nil -} - -func createTypedError(f string, args ...interface{}) *types.Error { - return &types.Error{ - Code: 100, - Msg: fmt.Sprintf(f, args...), - } -} - -func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error { - configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) - if err != nil { - return err - } - verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) - if verErr != nil { - return &types.Error{ - Code: types.ErrIncompatibleCNIVersion, - Msg: "incompatible CNI versions", - Details: verErr.Details(), - } - } - return toCall(cmdArgs) -} - -func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error { - cmd, cmdArgs, err := t.getCmdArgsFromEnv() - if err != nil { - return createTypedError(err.Error()) - } - - switch cmd { - case "ADD": - err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) - case "DEL": - err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) - case "VERSION": - err = versionInfo.Encode(t.Stdout) - default: - return createTypedError("unknown CNI_COMMAND: %v", cmd) - } - - if err != nil { - if e, ok := err.(*types.Error); ok { - // don't wrap Error in Error - return e - } - return createTypedError(err.Error()) - } - return nil -} - -// PluginMainWithError is the core "main" for a plugin. It accepts -// callback functions for add and del CNI commands and returns an error. -// -// The caller must also specify what CNI spec versions the plugin supports. -// -// It is the responsibility of the caller to check for non-nil error return. -// -// For a plugin to comply with the CNI spec, it must print any error to stdout -// as JSON and then exit with nonzero status code. -// -// To let this package automatically handle errors and call os.Exit(1) for you, -// use PluginMain() instead. -func PluginMainWithError(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error { - return (&dispatcher{ - Getenv: os.Getenv, - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - }).pluginMain(cmdAdd, cmdDel, versionInfo) -} - -// PluginMain is the core "main" for a plugin which includes automatic error handling. -// -// The caller must also specify what CNI spec versions the plugin supports. -// -// When an error occurs in either cmdAdd or cmdDel, PluginMain will print the error -// as JSON to stdout and call os.Exit(1). -// -// To have more control over error handling, use PluginMainWithError() instead. -func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) { - if e := PluginMainWithError(cmdAdd, cmdDel, versionInfo); e != nil { - if err := e.Print(); err != nil { - log.Print("Error writing error JSON to stdout: ", err) - } - os.Exit(1) - } -} diff --git a/pkg/skel/skel_suite_test.go b/pkg/skel/skel_suite_test.go deleted file mode 100644 index df5a8e77..00000000 --- a/pkg/skel/skel_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 skel - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestSkel(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Skel Suite") -} diff --git a/pkg/skel/skel_test.go b/pkg/skel/skel_test.go deleted file mode 100644 index ad293084..00000000 --- a/pkg/skel/skel_test.go +++ /dev/null @@ -1,346 +0,0 @@ -// 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 skel - -import ( - "bytes" - "errors" - "strings" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/version" - - "github.com/containernetworking/cni/pkg/testutils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" -) - -type fakeCmd struct { - CallCount int - Returns struct { - Error error - } - Received struct { - CmdArgs *CmdArgs - } -} - -func (c *fakeCmd) Func(args *CmdArgs) error { - c.CallCount++ - c.Received.CmdArgs = args - return c.Returns.Error -} - -var _ = Describe("dispatching to the correct callback", func() { - var ( - environment map[string]string - stdinData string - stdout, stderr *bytes.Buffer - cmdAdd, cmdDel *fakeCmd - dispatch *dispatcher - expectedCmdArgs *CmdArgs - versionInfo version.PluginInfo - ) - - BeforeEach(func() { - environment = map[string]string{ - "CNI_COMMAND": "ADD", - "CNI_CONTAINERID": "some-container-id", - "CNI_NETNS": "/some/netns/path", - "CNI_IFNAME": "eth0", - "CNI_ARGS": "some;extra;args", - "CNI_PATH": "/some/cni/path", - } - - stdinData = `{ "some": "config", "cniVersion": "9.8.7" }` - stdout = &bytes.Buffer{} - stderr = &bytes.Buffer{} - versionInfo = version.PluginSupports("9.8.7") - dispatch = &dispatcher{ - Getenv: func(key string) string { return environment[key] }, - Stdin: strings.NewReader(stdinData), - Stdout: stdout, - Stderr: stderr, - } - cmdAdd = &fakeCmd{} - cmdDel = &fakeCmd{} - expectedCmdArgs = &CmdArgs{ - ContainerID: "some-container-id", - Netns: "/some/netns/path", - IfName: "eth0", - Args: "some;extra;args", - Path: "/some/cni/path", - StdinData: []byte(stdinData), - } - }) - - var envVarChecker = func(envVar string, isRequired bool) { - delete(environment, envVar) - - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - if isRequired { - Expect(err).To(Equal(&types.Error{ - Code: 100, - Msg: "required env variables missing", - })) - Expect(stderr.String()).To(ContainSubstring(envVar + " env variable missing\n")) - } else { - Expect(err).NotTo(HaveOccurred()) - } - } - - Context("when the CNI_COMMAND is ADD", func() { - It("extracts env vars and stdin data and calls cmdAdd", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(cmdAdd.CallCount).To(Equal(1)) - Expect(cmdDel.CallCount).To(Equal(0)) - Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs)) - }) - - It("does not call cmdDel", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(cmdDel.CallCount).To(Equal(0)) - }) - - DescribeTable("required / optional env vars", envVarChecker, - Entry("command", "CNI_COMMAND", true), - Entry("container id", "CNI_CONTAINERID", false), - Entry("net ns", "CNI_NETNS", true), - Entry("if name", "CNI_IFNAME", true), - Entry("args", "CNI_ARGS", false), - Entry("path", "CNI_PATH", true), - ) - - Context("when multiple required env vars are missing", func() { - BeforeEach(func() { - delete(environment, "CNI_NETNS") - delete(environment, "CNI_IFNAME") - delete(environment, "CNI_PATH") - }) - - It("reports that all of them are missing, not just the first", func() { - Expect(dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)).NotTo(Succeed()) - log := stderr.String() - Expect(log).To(ContainSubstring("CNI_NETNS env variable missing\n")) - Expect(log).To(ContainSubstring("CNI_IFNAME env variable missing\n")) - Expect(log).To(ContainSubstring("CNI_PATH env variable missing\n")) - - }) - }) - - Context("when the stdin data is missing the required cniVersion config", func() { - BeforeEach(func() { - dispatch.Stdin = strings.NewReader(`{ "some": "config" }`) - }) - - Context("when the plugin supports version 0.1.0", func() { - BeforeEach(func() { - versionInfo = version.PluginSupports("0.1.0") - expectedCmdArgs.StdinData = []byte(`{ "some": "config" }`) - }) - - It("infers the config is 0.1.0 and calls the cmdAdd callback", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - Expect(err).NotTo(HaveOccurred()) - - Expect(cmdAdd.CallCount).To(Equal(1)) - Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs)) - }) - }) - - Context("when the plugin does not support 0.1.0", func() { - BeforeEach(func() { - versionInfo = version.PluginSupports("4.3.2") - }) - - It("immediately returns a useful error", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes - Expect(err.Msg).To(Equal("incompatible CNI versions")) - Expect(err.Details).To(Equal(`config is "0.1.0", plugin supports ["4.3.2"]`)) - }) - - It("does not call either callback", func() { - dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - Expect(cmdAdd.CallCount).To(Equal(0)) - Expect(cmdDel.CallCount).To(Equal(0)) - }) - }) - }) - }) - - Context("when the CNI_COMMAND is DEL", func() { - BeforeEach(func() { - environment["CNI_COMMAND"] = "DEL" - }) - - It("calls cmdDel with the env vars and stdin data", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(cmdDel.CallCount).To(Equal(1)) - Expect(cmdDel.Received.CmdArgs).To(Equal(expectedCmdArgs)) - }) - - It("does not call cmdAdd", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(cmdAdd.CallCount).To(Equal(0)) - }) - - DescribeTable("required / optional env vars", envVarChecker, - Entry("command", "CNI_COMMAND", true), - Entry("container id", "CNI_CONTAINERID", false), - Entry("net ns", "CNI_NETNS", false), - Entry("if name", "CNI_IFNAME", true), - Entry("args", "CNI_ARGS", false), - Entry("path", "CNI_PATH", true), - ) - }) - - Context("when the CNI_COMMAND is VERSION", func() { - BeforeEach(func() { - environment["CNI_COMMAND"] = "VERSION" - }) - - It("prints the version to stdout", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(stdout).To(MatchJSON(`{ - "cniVersion": "0.3.1", - "supportedVersions": ["9.8.7"] - }`)) - }) - - It("does not call cmdAdd or cmdDel", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(cmdAdd.CallCount).To(Equal(0)) - Expect(cmdDel.CallCount).To(Equal(0)) - }) - - DescribeTable("VERSION does not need the usual env vars", envVarChecker, - Entry("command", "CNI_COMMAND", true), - Entry("container id", "CNI_CONTAINERID", false), - Entry("net ns", "CNI_NETNS", false), - Entry("if name", "CNI_IFNAME", false), - Entry("args", "CNI_ARGS", false), - Entry("path", "CNI_PATH", false), - ) - - Context("when the stdin is empty", func() { - BeforeEach(func() { - dispatch.Stdin = strings.NewReader("") - }) - - It("succeeds without error", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).NotTo(HaveOccurred()) - Expect(stdout).To(MatchJSON(`{ - "cniVersion": "0.3.1", - "supportedVersions": ["9.8.7"] - }`)) - }) - }) - }) - - Context("when the CNI_COMMAND is unrecognized", func() { - BeforeEach(func() { - environment["CNI_COMMAND"] = "NOPE" - }) - - It("does not call any cmd callback", func() { - dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(cmdAdd.CallCount).To(Equal(0)) - Expect(cmdDel.CallCount).To(Equal(0)) - }) - - It("returns an error", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).To(Equal(&types.Error{ - Code: 100, - Msg: "unknown CNI_COMMAND: NOPE", - })) - }) - }) - - Context("when stdin cannot be read", func() { - BeforeEach(func() { - dispatch.Stdin = &testutils.BadReader{} - }) - - It("does not call any cmd callback", func() { - dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(cmdAdd.CallCount).To(Equal(0)) - Expect(cmdDel.CallCount).To(Equal(0)) - }) - - It("wraps and returns the error", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).To(Equal(&types.Error{ - Code: 100, - Msg: "error reading from stdin: banana", - })) - }) - }) - - Context("when the callback returns an error", func() { - Context("when it is a typed Error", func() { - BeforeEach(func() { - cmdAdd.Returns.Error = &types.Error{ - Code: 1234, - Msg: "insufficient something", - } - }) - - It("returns the error as-is", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).To(Equal(&types.Error{ - Code: 1234, - Msg: "insufficient something", - })) - }) - }) - - Context("when it is an unknown error", func() { - BeforeEach(func() { - cmdAdd.Returns.Error = errors.New("potato") - }) - - It("wraps and returns the error", func() { - err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo) - - Expect(err).To(Equal(&types.Error{ - Code: 100, - Msg: "potato", - })) - }) - }) - }) -}) diff --git a/pkg/types/020/types.go b/pkg/types/020/types.go deleted file mode 100644 index 2833aba7..00000000 --- a/pkg/types/020/types.go +++ /dev/null @@ -1,135 +0,0 @@ -// 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 -} diff --git a/pkg/types/020/types_suite_test.go b/pkg/types/020/types_suite_test.go deleted file mode 100644 index 095d73e2..00000000 --- a/pkg/types/020/types_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestTypes010(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "0.1.0/0.2.0 Types Suite") -} diff --git a/pkg/types/020/types_test.go b/pkg/types/020/types_test.go deleted file mode 100644 index 4f08ca49..00000000 --- a/pkg/types/020/types_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// 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_test - -import ( - "io/ioutil" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Ensures compatibility with the 0.1.0/0.2.0 spec", func() { - It("correctly encodes a 0.1.0/0.2.0 Result", func() { - ipv4, err := types.ParseCIDR("1.2.3.30/24") - Expect(err).NotTo(HaveOccurred()) - Expect(ipv4).NotTo(BeNil()) - - routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24") - Expect(err).NotTo(HaveOccurred()) - Expect(routev4).NotTo(BeNil()) - Expect(routegwv4).NotTo(BeNil()) - - ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64") - Expect(err).NotTo(HaveOccurred()) - Expect(ipv6).NotTo(BeNil()) - - routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80") - Expect(err).NotTo(HaveOccurred()) - Expect(routev6).NotTo(BeNil()) - Expect(routegwv6).NotTo(BeNil()) - - // Set every field of the struct to ensure source compatibility - res := types020.Result{ - CNIVersion: types020.ImplementedSpecVersion, - IP4: &types020.IPConfig{ - IP: *ipv4, - Gateway: net.ParseIP("1.2.3.1"), - Routes: []types.Route{ - {Dst: *routev4, GW: routegwv4}, - }, - }, - IP6: &types020.IPConfig{ - IP: *ipv6, - Gateway: net.ParseIP("abcd:1234:ffff::1"), - Routes: []types.Route{ - {Dst: *routev6, GW: routegwv6}, - }, - }, - DNS: types.DNS{ - Nameservers: []string{"1.2.3.4", "1::cafe"}, - Domain: "acompany.com", - Search: []string{"somedomain.com", "otherdomain.net"}, - Options: []string{"foo", "bar"}, - }, - } - - Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}")) - - // Redirect stdout to capture JSON result - oldStdout := os.Stdout - r, w, err := os.Pipe() - Expect(err).NotTo(HaveOccurred()) - - os.Stdout = w - err = res.Print() - w.Close() - Expect(err).NotTo(HaveOccurred()) - - // parse the result - out, err := ioutil.ReadAll(r) - os.Stdout = oldStdout - Expect(err).NotTo(HaveOccurred()) - - Expect(string(out)).To(Equal(`{ - "cniVersion": "0.2.0", - "ip4": { - "ip": "1.2.3.30/24", - "gateway": "1.2.3.1", - "routes": [ - { - "dst": "15.5.6.0/24", - "gw": "15.5.6.8" - } - ] - }, - "ip6": { - "ip": "abcd:1234:ffff::cdde/64", - "gateway": "abcd:1234:ffff::1", - "routes": [ - { - "dst": "1111:dddd::/80", - "gw": "1111:dddd::aaaa" - } - ] - }, - "dns": { - "nameservers": [ - "1.2.3.4", - "1::cafe" - ], - "domain": "acompany.com", - "search": [ - "somedomain.com", - "otherdomain.net" - ], - "options": [ - "foo", - "bar" - ] - } -}`)) - }) -}) diff --git a/pkg/types/args.go b/pkg/types/args.go deleted file mode 100644 index 66dcf9ea..00000000 --- a/pkg/types/args.go +++ /dev/null @@ -1,101 +0,0 @@ -// 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) -} - -// 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 - } - - u := keyField.Addr().Interface().(encoding.TextUnmarshaler) - 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 -} diff --git a/pkg/types/args_test.go b/pkg/types/args_test.go deleted file mode 100644 index 3a53d9a4..00000000 --- a/pkg/types/args_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// 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 types_test - -import ( - "reflect" - - . "github.com/containernetworking/cni/pkg/types" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" -) - -var _ = Describe("UnmarshallableBool UnmarshalText", func() { - DescribeTable("string to bool detection should succeed in all cases", - func(inputs []string, expected bool) { - for _, s := range inputs { - var ub UnmarshallableBool - err := ub.UnmarshalText([]byte(s)) - Expect(err).ToNot(HaveOccurred()) - Expect(ub).To(Equal(UnmarshallableBool(expected))) - } - }, - Entry("parse to true", []string{"True", "true", "1"}, true), - Entry("parse to false", []string{"False", "false", "0"}, false), - ) - - Context("When passed an invalid value", func() { - It("should result in an error", func() { - var ub UnmarshallableBool - err := ub.UnmarshalText([]byte("invalid")) - Expect(err).To(HaveOccurred()) - }) - }) -}) - -var _ = Describe("UnmarshallableString UnmarshalText", func() { - DescribeTable("string to string detection should succeed in all cases", - func(inputs []string, expected string) { - for _, s := range inputs { - var us UnmarshallableString - err := us.UnmarshalText([]byte(s)) - Expect(err).ToNot(HaveOccurred()) - Expect(string(us)).To(Equal(expected)) - } - }, - Entry("parse empty string", []string{""}, ""), - Entry("parse non-empty string", []string{"notempty"}, "notempty"), - ) -}) - -var _ = Describe("GetKeyField", func() { - type testcontainer struct { - Valid string `json:"valid,omitempty"` - } - var ( - container = testcontainer{Valid: "valid"} - containerInterface = func(i interface{}) interface{} { return i }(&container) - containerValue = reflect.ValueOf(containerInterface) - ) - Context("When a valid field is provided", func() { - It("should return the correct field", func() { - field := GetKeyField("Valid", containerValue) - Expect(field.String()).To(Equal("valid")) - }) - }) -}) - -var _ = Describe("LoadArgs", func() { - Context("When no arguments are passed", func() { - It("LoadArgs should succeed", func() { - err := LoadArgs("", struct{}{}) - Expect(err).NotTo(HaveOccurred()) - }) - }) - - Context("When unknown arguments are passed and ignored", func() { - It("LoadArgs should succeed", func() { - ca := CommonArgs{} - err := LoadArgs("IgnoreUnknown=True;Unk=nown", &ca) - Expect(err).NotTo(HaveOccurred()) - }) - }) - - Context("When unknown arguments are passed and not ignored", func() { - It("LoadArgs should fail", func() { - ca := CommonArgs{} - err := LoadArgs("Unk=nown", &ca) - Expect(err).To(HaveOccurred()) - }) - }) - - Context("When unknown arguments are passed and explicitly not ignored", func() { - It("LoadArgs should fail", func() { - ca := CommonArgs{} - err := LoadArgs("IgnoreUnknown=0, Unk=nown", &ca) - Expect(err).To(HaveOccurred()) - }) - }) - - Context("When known arguments are passed", func() { - It("LoadArgs should succeed", func() { - ca := CommonArgs{} - err := LoadArgs("IgnoreUnknown=1", &ca) - Expect(err).NotTo(HaveOccurred()) - }) - }) -}) diff --git a/pkg/types/current/types.go b/pkg/types/current/types.go deleted file mode 100644 index b5715fe6..00000000 --- a/pkg/types/current/types.go +++ /dev/null @@ -1,296 +0,0 @@ -// 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", - Interface: -1, - 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", - Interface: -1, - 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) -} - -// 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 -} diff --git a/pkg/types/current/types_suite_test.go b/pkg/types/current/types_suite_test.go deleted file mode 100644 index e05c1ff1..00000000 --- a/pkg/types/current/types_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestTypesCurrent(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Current Types Suite") -} diff --git a/pkg/types/current/types_test.go b/pkg/types/current/types_test.go deleted file mode 100644 index afc68670..00000000 --- a/pkg/types/current/types_test.go +++ /dev/null @@ -1,215 +0,0 @@ -// 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_test - -import ( - "io/ioutil" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func testResult() *current.Result { - ipv4, err := types.ParseCIDR("1.2.3.30/24") - Expect(err).NotTo(HaveOccurred()) - Expect(ipv4).NotTo(BeNil()) - - routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24") - Expect(err).NotTo(HaveOccurred()) - Expect(routev4).NotTo(BeNil()) - Expect(routegwv4).NotTo(BeNil()) - - ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64") - Expect(err).NotTo(HaveOccurred()) - Expect(ipv6).NotTo(BeNil()) - - routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80") - Expect(err).NotTo(HaveOccurred()) - Expect(routev6).NotTo(BeNil()) - Expect(routegwv6).NotTo(BeNil()) - - // Set every field of the struct to ensure source compatibility - return ¤t.Result{ - CNIVersion: "0.3.1", - Interfaces: []*current.Interface{ - { - Name: "eth0", - Mac: "00:11:22:33:44:55", - Sandbox: "/proc/3553/ns/net", - }, - }, - IPs: []*current.IPConfig{ - { - Version: "4", - Interface: 0, - Address: *ipv4, - Gateway: net.ParseIP("1.2.3.1"), - }, - { - Version: "6", - Interface: 0, - Address: *ipv6, - Gateway: net.ParseIP("abcd:1234:ffff::1"), - }, - }, - Routes: []*types.Route{ - {Dst: *routev4, GW: routegwv4}, - {Dst: *routev6, GW: routegwv6}, - }, - DNS: types.DNS{ - Nameservers: []string{"1.2.3.4", "1::cafe"}, - Domain: "acompany.com", - Search: []string{"somedomain.com", "otherdomain.net"}, - Options: []string{"foo", "bar"}, - }, - } -} - -var _ = Describe("Current types operations", func() { - It("correctly encodes a 0.3.x Result", func() { - res := testResult() - - Expect(res.String()).To(Equal("Interfaces:[{Name:eth0 Mac:00:11:22:33:44:55 Sandbox:/proc/3553/ns/net}], IP:[{Version:4 Interface:0 Address:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1} {Version:6 Interface:0 Address:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1}], Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8} {Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}], DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}")) - - // Redirect stdout to capture JSON result - oldStdout := os.Stdout - r, w, err := os.Pipe() - Expect(err).NotTo(HaveOccurred()) - - os.Stdout = w - err = res.Print() - w.Close() - Expect(err).NotTo(HaveOccurred()) - - // parse the result - out, err := ioutil.ReadAll(r) - os.Stdout = oldStdout - Expect(err).NotTo(HaveOccurred()) - - Expect(string(out)).To(Equal(`{ - "cniVersion": "0.3.1", - "interfaces": [ - { - "name": "eth0", - "mac": "00:11:22:33:44:55", - "sandbox": "/proc/3553/ns/net" - } - ], - "ips": [ - { - "version": "4", - "address": "1.2.3.30/24", - "gateway": "1.2.3.1" - }, - { - "version": "6", - "address": "abcd:1234:ffff::cdde/64", - "gateway": "abcd:1234:ffff::1" - } - ], - "routes": [ - { - "dst": "15.5.6.0/24", - "gw": "15.5.6.8" - }, - { - "dst": "1111:dddd::/80", - "gw": "1111:dddd::aaaa" - } - ], - "dns": { - "nameservers": [ - "1.2.3.4", - "1::cafe" - ], - "domain": "acompany.com", - "search": [ - "somedomain.com", - "otherdomain.net" - ], - "options": [ - "foo", - "bar" - ] - } -}`)) - }) - - It("correctly encodes a 0.1.0 Result", func() { - res, err := testResult().GetAsVersion("0.1.0") - Expect(err).NotTo(HaveOccurred()) - - Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}")) - - // Redirect stdout to capture JSON result - oldStdout := os.Stdout - r, w, err := os.Pipe() - Expect(err).NotTo(HaveOccurred()) - - os.Stdout = w - err = res.Print() - w.Close() - Expect(err).NotTo(HaveOccurred()) - - // parse the result - out, err := ioutil.ReadAll(r) - os.Stdout = oldStdout - Expect(err).NotTo(HaveOccurred()) - - Expect(string(out)).To(Equal(`{ - "cniVersion": "0.2.0", - "ip4": { - "ip": "1.2.3.30/24", - "gateway": "1.2.3.1", - "routes": [ - { - "dst": "15.5.6.0/24", - "gw": "15.5.6.8" - } - ] - }, - "ip6": { - "ip": "abcd:1234:ffff::cdde/64", - "gateway": "abcd:1234:ffff::1", - "routes": [ - { - "dst": "1111:dddd::/80", - "gw": "1111:dddd::aaaa" - } - ] - }, - "dns": { - "nameservers": [ - "1.2.3.4", - "1::cafe" - ], - "domain": "acompany.com", - "search": [ - "somedomain.com", - "otherdomain.net" - ], - "options": [ - "foo", - "bar" - ] - } -}`)) - }) -}) diff --git a/pkg/types/types.go b/pkg/types/types.go deleted file mode 100644 index 3263015a..00000000 --- a/pkg/types/types.go +++ /dev/null @@ -1,185 +0,0 @@ -// 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 struct { - Type string `json:"type,omitempty"` - } `json:"ipam,omitempty"` - DNS DNS `json:"dns"` -} - -// 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 { - return e.Msg -} - -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") diff --git a/pkg/types/types_suite_test.go b/pkg/types/types_suite_test.go deleted file mode 100644 index 2b178cee..00000000 --- a/pkg/types/types_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 types_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestTypes(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Types Suite") -} diff --git a/pkg/utils/hwaddr/hwaddr_test.go b/pkg/utils/hwaddr/hwaddr_test.go index b77ccd89..ab89a58e 100644 --- a/pkg/utils/hwaddr/hwaddr_test.go +++ b/pkg/utils/hwaddr/hwaddr_test.go @@ -17,7 +17,7 @@ package hwaddr_test import ( "net" - "github.com/containernetworking/cni/pkg/utils/hwaddr" + "github.com/containernetworking/plugins/pkg/utils/hwaddr" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" diff --git a/pkg/version/conf.go b/pkg/version/conf.go deleted file mode 100644 index 3cca58bb..00000000 --- a/pkg/version/conf.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 -} diff --git a/pkg/version/conf_test.go b/pkg/version/conf_test.go deleted file mode 100644 index 881c57ad..00000000 --- a/pkg/version/conf_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// 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_test - -import ( - "github.com/containernetworking/cni/pkg/version" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Decoding the version of network config", func() { - var ( - decoder *version.ConfigDecoder - configBytes []byte - ) - - BeforeEach(func() { - decoder = &version.ConfigDecoder{} - configBytes = []byte(`{ "cniVersion": "4.3.2" }`) - }) - - Context("when the version is explict", func() { - It("returns the version", func() { - version, err := decoder.Decode(configBytes) - Expect(err).NotTo(HaveOccurred()) - - Expect(version).To(Equal("4.3.2")) - }) - }) - - Context("when the version is not present in the config", func() { - BeforeEach(func() { - configBytes = []byte(`{ "not-a-version-field": "foo" }`) - }) - - It("assumes the config is version 0.1.0", func() { - version, err := decoder.Decode(configBytes) - Expect(err).NotTo(HaveOccurred()) - - Expect(version).To(Equal("0.1.0")) - }) - }) - - Context("when the config data is malformed", func() { - BeforeEach(func() { - configBytes = []byte(`{{{`) - }) - - It("returns a useful error", func() { - _, err := decoder.Decode(configBytes) - Expect(err).To(MatchError(HavePrefix( - "decoding version from network config: invalid character", - ))) - }) - }) -}) diff --git a/pkg/version/legacy_examples/example_runtime.go b/pkg/version/legacy_examples/example_runtime.go deleted file mode 100644 index a461981f..00000000 --- a/pkg/version/legacy_examples/example_runtime.go +++ /dev/null @@ -1,167 +0,0 @@ -// 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 -} -`, - }, -} diff --git a/pkg/version/legacy_examples/examples.go b/pkg/version/legacy_examples/examples.go deleted file mode 100644 index 1bf406b3..00000000 --- a/pkg/version/legacy_examples/examples.go +++ /dev/null @@ -1,139 +0,0 @@ -// 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/types/020" - "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 = &types020.Result{ - IP4: &types020.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 deleted file mode 100644 index a126531d..00000000 --- a/pkg/version/legacy_examples/legacy_examples_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index 41151056..00000000 --- a/pkg/version/legacy_examples/legacy_examples_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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()) - }) -}) diff --git a/pkg/version/plugin.go b/pkg/version/plugin.go deleted file mode 100644 index 8a467281..00000000 --- a/pkg/version/plugin.go +++ /dev/null @@ -1,81 +0,0 @@ -// 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 -} diff --git a/pkg/version/plugin_test.go b/pkg/version/plugin_test.go deleted file mode 100644 index 124288fd..00000000 --- a/pkg/version/plugin_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// 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_test - -import ( - "github.com/containernetworking/cni/pkg/version" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Decoding versions reported by a plugin", func() { - var ( - decoder *version.PluginDecoder - versionStdout []byte - ) - - BeforeEach(func() { - decoder = &version.PluginDecoder{} - versionStdout = []byte(`{ - "cniVersion": "some-library-version", - "supportedVersions": [ "some-version", "some-other-version" ] - }`) - }) - - It("returns a PluginInfo that represents the given json bytes", func() { - pluginInfo, err := decoder.Decode(versionStdout) - Expect(err).NotTo(HaveOccurred()) - Expect(pluginInfo).NotTo(BeNil()) - Expect(pluginInfo.SupportedVersions()).To(Equal([]string{ - "some-version", - "some-other-version", - })) - }) - - Context("when the bytes cannot be decoded as json", func() { - BeforeEach(func() { - versionStdout = []byte(`{{{`) - }) - - It("returns a meaningful error", func() { - _, err := decoder.Decode(versionStdout) - Expect(err).To(MatchError("decoding version info: invalid character '{' looking for beginning of object key string")) - }) - }) - - Context("when the json bytes are missing the required CNIVersion field", func() { - BeforeEach(func() { - versionStdout = []byte(`{ "supportedVersions": [ "foo" ] }`) - }) - - It("returns a meaningful error", func() { - _, err := decoder.Decode(versionStdout) - Expect(err).To(MatchError("decoding version info: missing field cniVersion")) - }) - }) - - Context("when there are no supported versions", func() { - BeforeEach(func() { - versionStdout = []byte(`{ "cniVersion": "0.2.0" }`) - }) - - It("assumes that the supported versions are 0.1.0 and 0.2.0", func() { - pluginInfo, err := decoder.Decode(versionStdout) - Expect(err).NotTo(HaveOccurred()) - Expect(pluginInfo).NotTo(BeNil()) - Expect(pluginInfo.SupportedVersions()).To(Equal([]string{ - "0.1.0", - "0.2.0", - })) - }) - }) - -}) diff --git a/pkg/version/reconcile.go b/pkg/version/reconcile.go deleted file mode 100644 index 25c3810b..00000000 --- a/pkg/version/reconcile.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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, - } -} diff --git a/pkg/version/reconcile_test.go b/pkg/version/reconcile_test.go deleted file mode 100644 index 0c964cea..00000000 --- a/pkg/version/reconcile_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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_test - -import ( - "github.com/containernetworking/cni/pkg/version" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Reconcile versions of net config with versions supported by plugins", func() { - var ( - reconciler *version.Reconciler - pluginInfo version.PluginInfo - ) - - BeforeEach(func() { - reconciler = &version.Reconciler{} - pluginInfo = version.PluginSupports("1.2.3", "4.3.2") - }) - - It("succeeds if the config version is supported by the plugin", func() { - err := reconciler.Check("4.3.2", pluginInfo) - Expect(err).NotTo(HaveOccurred()) - }) - - Context("when the config version is not supported by the plugin", func() { - It("returns a helpful error", func() { - err := reconciler.Check("0.1.0", pluginInfo) - - Expect(err).To(Equal(&version.ErrorIncompatible{ - Config: "0.1.0", - Supported: []string{"1.2.3", "4.3.2"}, - })) - - Expect(err.Error()).To(Equal(`incompatible CNI versions: config is "0.1.0", plugin supports ["1.2.3" "4.3.2"]`)) - }) - }) -}) diff --git a/pkg/version/testhelpers/testhelpers.go b/pkg/version/testhelpers/testhelpers.go deleted file mode 100644 index 773d0120..00000000 --- a/pkg/version/testhelpers/testhelpers.go +++ /dev/null @@ -1,156 +0,0 @@ -// 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 testhelpers supports testing of CNI components of different versions -// -// For example, to build a plugin against an old version of the CNI library, -// we can pass the plugin's source and the old git commit reference to BuildAt. -// We could then test how the built binary responds when called by the latest -// version of this library. -package testhelpers - -import ( - "fmt" - "io/ioutil" - "math/rand" - "os" - "os/exec" - "path/filepath" - "strings" - "time" -) - -const packageBaseName = "github.com/containernetworking/cni" - -func run(cmd *exec.Cmd) error { - out, err := cmd.CombinedOutput() - if err != nil { - command := strings.Join(cmd.Args, " ") - return fmt.Errorf("running %q: %s", command, out) - } - return nil -} - -func goBuildEnviron(gopath string) []string { - environ := os.Environ() - for i, kvp := range environ { - if strings.HasPrefix(kvp, "GOPATH=") { - environ[i] = "GOPATH=" + gopath - return environ - } - } - environ = append(environ, "GOPATH="+gopath) - return environ -} - -func buildGoProgram(gopath, packageName, outputFilePath string) error { - cmd := exec.Command("go", "build", "-o", outputFilePath, packageName) - cmd.Env = goBuildEnviron(gopath) - return run(cmd) -} - -func createSingleFilePackage(gopath, packageName string, fileContents []byte) error { - dirName := filepath.Join(gopath, "src", packageName) - err := os.MkdirAll(dirName, 0700) - if err != nil { - return err - } - - return ioutil.WriteFile(filepath.Join(dirName, "main.go"), fileContents, 0600) -} - -func removePackage(gopath, packageName string) error { - dirName := filepath.Join(gopath, "src", packageName) - return os.RemoveAll(dirName) -} - -func isRepoRoot(path string) bool { - _, err := ioutil.ReadDir(filepath.Join(path, ".git")) - return (err == nil) && (filepath.Base(path) == "cni") -} - -func LocateCurrentGitRepo() (string, error) { - dir, err := os.Getwd() - if err != nil { - return "", err - } - - for i := 0; i < 5; i++ { - if isRepoRoot(dir) { - return dir, nil - } - - dir, err = filepath.Abs(filepath.Dir(dir)) - if err != nil { - return "", fmt.Errorf("abs(dir(%q)): %s", dir, err) - } - } - - return "", fmt.Errorf("unable to find cni repo root, landed at %q", dir) -} - -func gitCloneThisRepo(cloneDestination string) error { - err := os.MkdirAll(cloneDestination, 0700) - if err != nil { - return err - } - - currentGitRepo, err := LocateCurrentGitRepo() - if err != nil { - return err - } - - return run(exec.Command("git", "clone", currentGitRepo, cloneDestination)) -} - -func gitCheckout(localRepo string, gitRef string) error { - return run(exec.Command("git", "-C", localRepo, "checkout", gitRef)) -} - -// BuildAt builds the go programSource using the version of the CNI library -// at gitRef, and saves the resulting binary file at outputFilePath -func BuildAt(programSource []byte, gitRef string, outputFilePath string) error { - tempGoPath, err := ioutil.TempDir("", "cni-git-") - if err != nil { - return err - } - defer os.RemoveAll(tempGoPath) - - cloneDestination := filepath.Join(tempGoPath, "src", packageBaseName) - err = gitCloneThisRepo(cloneDestination) - if err != nil { - return err - } - - err = gitCheckout(cloneDestination, gitRef) - if err != nil { - return err - } - - rand.Seed(time.Now().UnixNano()) - testPackageName := fmt.Sprintf("test-package-%x", rand.Int31()) - - err = createSingleFilePackage(tempGoPath, testPackageName, programSource) - if err != nil { - return err - } - defer removePackage(tempGoPath, testPackageName) - - err = buildGoProgram(tempGoPath, testPackageName, outputFilePath) - if err != nil { - return err - } - - return nil -} diff --git a/pkg/version/testhelpers/testhelpers_suite_test.go b/pkg/version/testhelpers/testhelpers_suite_test.go deleted file mode 100644 index 72f65f9c..00000000 --- a/pkg/version/testhelpers/testhelpers_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 testhelpers_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestTesthelpers(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Testhelpers Suite") -} diff --git a/pkg/version/testhelpers/testhelpers_test.go b/pkg/version/testhelpers/testhelpers_test.go deleted file mode 100644 index 3473cd59..00000000 --- a/pkg/version/testhelpers/testhelpers_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// 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 testhelpers_test - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - - "github.com/containernetworking/cni/pkg/version/testhelpers" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("BuildAt", func() { - var ( - gitRef string - outputFilePath string - outputDir string - programSource []byte - ) - BeforeEach(func() { - programSource = []byte(`package main - -import "github.com/containernetworking/cni/pkg/skel" - -func c(_ *skel.CmdArgs) error { return nil } - -func main() { skel.PluginMain(c, c) } -`) - gitRef = "f4364185253" - - var err error - outputDir, err = ioutil.TempDir("", "bin") - Expect(err).NotTo(HaveOccurred()) - outputFilePath = filepath.Join(outputDir, "some-binary") - }) - - AfterEach(func() { - Expect(os.RemoveAll(outputDir)).To(Succeed()) - }) - - It("builds the provided source code using the CNI library at the given git ref", func() { - Expect(outputFilePath).NotTo(BeAnExistingFile()) - - err := testhelpers.BuildAt(programSource, gitRef, outputFilePath) - Expect(err).NotTo(HaveOccurred()) - - Expect(outputFilePath).To(BeAnExistingFile()) - - cmd := exec.Command(outputFilePath) - cmd.Env = []string{"CNI_COMMAND=VERSION"} - output, err := cmd.CombinedOutput() - Expect(err).To(BeAssignableToTypeOf(&exec.ExitError{})) - Expect(output).To(ContainSubstring("unknown CNI_COMMAND: VERSION")) - }) -}) - -var _ = Describe("LocateCurrentGitRepo", func() { - It("returns the path to the root of the CNI git repo", func() { - path, err := testhelpers.LocateCurrentGitRepo() - Expect(err).NotTo(HaveOccurred()) - - AssertItIsTheCNIRepoRoot(path) - }) - - Context("when run from a different directory", func() { - BeforeEach(func() { - os.Chdir("..") - }) - - It("still finds the CNI repo root", func() { - path, err := testhelpers.LocateCurrentGitRepo() - Expect(err).NotTo(HaveOccurred()) - - AssertItIsTheCNIRepoRoot(path) - }) - }) -}) - -func AssertItIsTheCNIRepoRoot(path string) { - Expect(path).To(BeADirectory()) - files, err := ioutil.ReadDir(path) - Expect(err).NotTo(HaveOccurred()) - - names := []string{} - for _, file := range files { - names = append(names, file.Name()) - } - - Expect(names).To(ContainElement("SPEC.md")) - Expect(names).To(ContainElement("libcni")) - Expect(names).To(ContainElement("cnitool")) -} diff --git a/pkg/version/version.go b/pkg/version/version.go deleted file mode 100644 index efe8ea87..00000000 --- a/pkg/version/version.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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) -} diff --git a/pkg/version/version_suite_test.go b/pkg/version/version_suite_test.go deleted file mode 100644 index 25d503c8..00000000 --- a/pkg/version/version_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestVersion(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Version Suite") -} diff --git a/plugins/ipam/dhcp/lease.go b/plugins/ipam/dhcp/lease.go index a202720d..ca34eec4 100644 --- a/plugins/ipam/dhcp/lease.go +++ b/plugins/ipam/dhcp/lease.go @@ -26,8 +26,8 @@ import ( "github.com/d2g/dhcp4client" "github.com/vishvananda/netlink" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/plugins/pkg/ns" ) // RFC 2131 suggests using exponential backoff, starting with 4sec diff --git a/plugins/ipam/host-local/backend/allocator/allocator.go b/plugins/ipam/host-local/backend/allocator/allocator.go index 87b4fa19..56b8bdf0 100644 --- a/plugins/ipam/host-local/backend/allocator/allocator.go +++ b/plugins/ipam/host-local/backend/allocator/allocator.go @@ -20,9 +20,9 @@ import ( "net" "os" - "github.com/containernetworking/cni/pkg/ip" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" ) diff --git a/plugins/ipam/host-local/host_local_test.go b/plugins/ipam/host-local/host_local_test.go index f1578d09..5e2c69c4 100644 --- a/plugins/ipam/host-local/host_local_test.go +++ b/plugins/ipam/host-local/host_local_test.go @@ -23,10 +23,10 @@ import ( "strings" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/020" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" diff --git a/plugins/main/bridge/bridge.go b/plugins/main/bridge/bridge.go index 876cb5bb..83468d9c 100644 --- a/plugins/main/bridge/bridge.go +++ b/plugins/main/bridge/bridge.go @@ -22,14 +22,14 @@ import ( "runtime" "syscall" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ipam" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/utils" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ipam" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/utils" "github.com/vishvananda/netlink" ) diff --git a/plugins/main/bridge/bridge_test.go b/plugins/main/bridge/bridge_test.go index 65f083b7..8fe56fc0 100644 --- a/plugins/main/bridge/bridge_test.go +++ b/plugins/main/bridge/bridge_test.go @@ -20,14 +20,13 @@ import ( "strings" "syscall" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/020" "github.com/containernetworking/cni/pkg/types/current" - - "github.com/containernetworking/cni/pkg/utils/hwaddr" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + "github.com/containernetworking/plugins/pkg/utils/hwaddr" "github.com/vishvananda/netlink" diff --git a/plugins/main/ipvlan/ipvlan.go b/plugins/main/ipvlan/ipvlan.go index 4fba4327..b9c7b1b3 100644 --- a/plugins/main/ipvlan/ipvlan.go +++ b/plugins/main/ipvlan/ipvlan.go @@ -20,13 +20,13 @@ import ( "fmt" "runtime" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ipam" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ipam" + "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" ) diff --git a/plugins/main/ipvlan/ipvlan_test.go b/plugins/main/ipvlan/ipvlan_test.go index 41a56256..a2b92179 100644 --- a/plugins/main/ipvlan/ipvlan_test.go +++ b/plugins/main/ipvlan/ipvlan_test.go @@ -19,11 +19,11 @@ import ( "net" "syscall" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" "github.com/vishvananda/netlink" diff --git a/plugins/main/loopback/loopback.go b/plugins/main/loopback/loopback.go index 6441ba5c..08c84a5d 100644 --- a/plugins/main/loopback/loopback.go +++ b/plugins/main/loopback/loopback.go @@ -15,10 +15,10 @@ package main import ( - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" ) diff --git a/plugins/main/loopback/loopback_test.go b/plugins/main/loopback/loopback_test.go index f71595b0..c5dec3cb 100644 --- a/plugins/main/loopback/loopback_test.go +++ b/plugins/main/loopback/loopback_test.go @@ -20,7 +20,7 @@ import ( "os/exec" "strings" - "github.com/containernetworking/cni/pkg/ns" + "github.com/containernetworking/plugins/pkg/ns" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 0d34a4c7..4ee9abb4 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -21,14 +21,14 @@ import ( "net" "runtime" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ipam" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/utils/sysctl" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ipam" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/utils/sysctl" "github.com/vishvananda/netlink" ) diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index e17b14f3..7ae26f30 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -19,12 +19,12 @@ import ( "net" "syscall" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/utils/hwaddr" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + "github.com/containernetworking/plugins/pkg/utils/hwaddr" "github.com/vishvananda/netlink" diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index f90bafbc..af93312d 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -24,14 +24,14 @@ import ( "github.com/vishvananda/netlink" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ipam" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/utils" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ipam" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/utils" ) func init() { diff --git a/plugins/main/ptp/ptp_test.go b/plugins/main/ptp/ptp_test.go index b30fd31c..a562af7e 100644 --- a/plugins/main/ptp/ptp_test.go +++ b/plugins/main/ptp/ptp_test.go @@ -15,9 +15,9 @@ package main import ( - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" "github.com/vishvananda/netlink" diff --git a/plugins/main/vlan/vlan.go b/plugins/main/vlan/vlan.go index 56735ee1..b9aa4fee 100644 --- a/plugins/main/vlan/vlan.go +++ b/plugins/main/vlan/vlan.go @@ -20,13 +20,13 @@ import ( "fmt" "runtime" - "github.com/containernetworking/cni/pkg/ip" - "github.com/containernetworking/cni/pkg/ipam" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ip" + "github.com/containernetworking/plugins/pkg/ipam" + "github.com/containernetworking/plugins/pkg/ns" "github.com/vishvananda/netlink" ) diff --git a/plugins/main/vlan/vlan_test.go b/plugins/main/vlan/vlan_test.go index c46aa739..06005bb5 100644 --- a/plugins/main/vlan/vlan_test.go +++ b/plugins/main/vlan/vlan_test.go @@ -19,11 +19,11 @@ import ( "net" "syscall" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" "github.com/vishvananda/netlink" diff --git a/plugins/meta/flannel/flannel_test.go b/plugins/meta/flannel/flannel_test.go index a4a8bc7b..0380729c 100644 --- a/plugins/meta/flannel/flannel_test.go +++ b/plugins/meta/flannel/flannel_test.go @@ -18,9 +18,9 @@ import ( "io/ioutil" "os" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" diff --git a/plugins/meta/tuning/tuning.go b/plugins/meta/tuning/tuning.go index 9d8e9002..98a242f6 100644 --- a/plugins/meta/tuning/tuning.go +++ b/plugins/meta/tuning/tuning.go @@ -24,11 +24,11 @@ import ( "path/filepath" "strings" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ns" ) // TuningConf represents the network tuning configuration. diff --git a/plugins/sample/sample_test.go b/plugins/sample/sample_test.go index 9d2688cc..d0b64982 100644 --- a/plugins/sample/sample_test.go +++ b/plugins/sample/sample_test.go @@ -17,9 +17,9 @@ package main import ( "fmt" - "github.com/containernetworking/cni/pkg/ns" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/testutils" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) diff --git a/test.sh b/test.sh index 82253f3a..06888aa8 100755 --- a/test.sh +++ b/test.sh @@ -10,7 +10,7 @@ source ./build.sh echo "Running tests" -TESTABLE="plugins/ipam/dhcp plugins/ipam/host-local plugins/ipam/host-local/backend/allocator plugins/main/loopback plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/meta/flannel plugins/main/vlan plugins/sample" +TESTABLE="plugins/ipam/dhcp plugins/ipam/host-local plugins/ipam/host-local/backend/allocator plugins/main/loopback plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/meta/flannel plugins/main/vlan plugins/sample pkg/ip pkg/ipam pkg/ns pkg/utils pkg/utils/hwaddr pkg/utils/sysctl" # user has not provided PKG override if [ -z "$PKG" ]; then