123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- // 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 (
- "context"
- "fmt"
- "os"
- "github.com/containernetworking/cni/pkg/types"
- "github.com/containernetworking/cni/pkg/version"
- )
- // Exec is an interface encapsulates all operations that deal with finding
- // and executing a CNI plugin. Tests may provide a fake implementation
- // to avoid writing fake plugins to temporary directories during the test.
- type Exec interface {
- ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error)
- FindInPath(plugin string, paths []string) (string, error)
- Decode(jsonBytes []byte) (version.PluginInfo, error)
- }
- // For example, a testcase could pass an instance of the following fakeExec
- // object to ExecPluginWithResult() to verify the incoming stdin and environment
- // and provide a tailored response:
- //
- //import (
- // "encoding/json"
- // "path"
- // "strings"
- //)
- //
- //type fakeExec struct {
- // version.PluginDecoder
- //}
- //
- //func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
- // net := &types.NetConf{}
- // err := json.Unmarshal(stdinData, net)
- // if err != nil {
- // return nil, fmt.Errorf("failed to unmarshal configuration: %v", err)
- // }
- // pluginName := path.Base(pluginPath)
- // if pluginName != net.Type {
- // return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type)
- // }
- // for _, e := range environ {
- // // Check environment for forced failure request
- // parts := strings.Split(e, "=")
- // if len(parts) > 0 && parts[0] == "FAIL" {
- // return nil, fmt.Errorf("failed to execute plugin %s", pluginName)
- // }
- // }
- // return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil
- //}
- //
- //func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
- // if len(paths) > 0 {
- // return path.Join(paths[0], plugin), nil
- // }
- // return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths)
- //}
- func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {
- if exec == nil {
- exec = defaultExec
- }
- stdoutBytes, err := exec.ExecPlugin(ctx, 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 ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error {
- if exec == nil {
- exec = defaultExec
- }
- _, err := exec.ExecPlugin(ctx, 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 GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) {
- if exec == nil {
- exec = defaultExec
- }
- 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 := exec.ExecPlugin(ctx, 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 exec.Decode(stdoutBytes)
- }
- // DefaultExec is an object that implements the Exec interface which looks
- // for and executes plugins from disk.
- type DefaultExec struct {
- *RawExec
- version.PluginDecoder
- }
- // DefaultExec implements the Exec interface
- var _ Exec = &DefaultExec{}
- var defaultExec = &DefaultExec{
- RawExec: &RawExec{Stderr: os.Stderr},
- }
|