move most of cni/pkg to plugins/pkg: delete code staying in cni/pkg
This moves the following packages to this repository: * ip * ipam * ns * testutils * utils
This commit is contained in:
parent
8b43e90949
commit
3c436520be
@ -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, ";")
|
|
||||||
}
|
|
@ -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())
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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"))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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"
|
|
@ -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()
|
|
||||||
})
|
|
@ -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{""}
|
|
@ -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", ""}
|
|
@ -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
|
|
||||||
}
|
|
@ -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")))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -21,8 +21,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
@ -20,9 +20,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
@ -18,9 +18,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
228
pkg/skel/skel.go
228
pkg/skel/skel.go
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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",
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`))
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`))
|
|
||||||
})
|
|
||||||
})
|
|
@ -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")
|
|
@ -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")
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ package hwaddr_test
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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",
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
@ -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",
|
|
||||||
},
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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())
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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",
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"]`))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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"))
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -26,8 +26,8 @@ import (
|
|||||||
"github.com/d2g/dhcp4client"
|
"github.com/d2g/dhcp4client"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||||
|
@ -20,9 +20,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
|
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/testutils"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/020"
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -22,14 +22,14 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/utils"
|
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,14 +20,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/testutils"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/020"
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,11 +19,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/testutils"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
|
@ -21,14 +21,14 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"runtime"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/utils/sysctl"
|
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/testutils"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/utils"
|
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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() {
|
func init() {
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"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/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"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"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,11 +19,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/testutils"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"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/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -24,11 +24,11 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TuningConf represents the network tuning configuration.
|
// TuningConf represents the network tuning configuration.
|
||||||
|
@ -17,9 +17,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"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/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
2
test.sh
2
test.sh
@ -10,7 +10,7 @@ source ./build.sh
|
|||||||
|
|
||||||
echo "Running tests"
|
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
|
# user has not provided PKG override
|
||||||
if [ -z "$PKG" ]; then
|
if [ -z "$PKG" ]; then
|
||||||
|
Loading…
x
Reference in New Issue
Block a user