mirror of
https://github.com/rancher/plugins.git
synced 2025-09-17 16:05:07 +00:00
vendor: add CNI invoke, ip, ipam, and hwaddr packages
This commit is contained in:
79
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
Normal file
79
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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, ";")
|
||||
}
|
53
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
Normal file
53
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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())
|
||||
}
|
95
vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
generated
vendored
Normal file
95
vendor/github.com/containernetworking/cni/pkg/invoke/exec.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// 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)
|
||||
}
|
157
vendor/github.com/containernetworking/cni/pkg/invoke/exec_test.go
generated
vendored
Normal file
157
vendor/github.com/containernetworking/cni/pkg/invoke/exec_test.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
// 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.0" }`)
|
||||
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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
27
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/cni_args.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/cni_args.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fakes
|
||||
|
||||
type CNIArgs struct {
|
||||
AsEnvCall struct {
|
||||
Returns struct {
|
||||
Env []string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *CNIArgs) AsEnv() []string {
|
||||
return a.AsEnvCall.Returns.Env
|
||||
}
|
36
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/raw_exec.go
generated
vendored
Normal file
36
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/raw_exec.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package 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
|
||||
}
|
34
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/version_decoder.go
generated
vendored
Normal file
34
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/version_decoder.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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
|
||||
}
|
43
vendor/github.com/containernetworking/cni/pkg/invoke/find.go
generated
vendored
Normal file
43
vendor/github.com/containernetworking/cni/pkg/invoke/find.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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)
|
||||
}
|
103
vendor/github.com/containernetworking/cni/pkg/invoke/find_test.go
generated
vendored
Normal file
103
vendor/github.com/containernetworking/cni/pkg/invoke/find_test.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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)))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
107
vendor/github.com/containernetworking/cni/pkg/invoke/get_version_integration_test.go
generated
vendored
Normal file
107
vendor/github.com/containernetworking/cni/pkg/invoke/get_version_integration_test.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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"
|
45
vendor/github.com/containernetworking/cni/pkg/invoke/invoke_suite_test.go
generated
vendored
Normal file
45
vendor/github.com/containernetworking/cni/pkg/invoke/invoke_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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()
|
||||
})
|
20
vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
generated
vendored
Normal file
20
vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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{""}
|
18
vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
generated
vendored
Normal file
18
vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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", ""}
|
63
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
Normal file
63
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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
|
||||
}
|
123
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec_test.go
generated
vendored
Normal file
123
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec_test.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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.0"}`)
|
||||
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")))
|
||||
})
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user