github.com/mccv1r0/cni@v0.7.0-alpha1/pkg/invoke/exec.go (about) 1 // Copyright 2015 CNI authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package invoke 16 17 import ( 18 "fmt" 19 "os" 20 21 "github.com/containernetworking/cni/pkg/types" 22 "github.com/containernetworking/cni/pkg/version" 23 ) 24 25 // Exec is an interface encapsulates all operations that deal with finding 26 // and executing a CNI plugin. Tests may provide a fake implementation 27 // to avoid writing fake plugins to temporary directories during the test. 28 type Exec interface { 29 ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) 30 FindInPath(plugin string, paths []string) (string, error) 31 Decode(jsonBytes []byte) (version.PluginInfo, error) 32 } 33 34 // For example, a testcase could pass an instance of the following fakeExec 35 // object to ExecPluginWithResult() to verify the incoming stdin and environment 36 // and provide a tailored response: 37 // 38 //import ( 39 // "encoding/json" 40 // "path" 41 // "strings" 42 //) 43 // 44 //type fakeExec struct { 45 // version.PluginDecoder 46 //} 47 // 48 //func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { 49 // net := &types.NetConf{} 50 // err := json.Unmarshal(stdinData, net) 51 // if err != nil { 52 // return nil, fmt.Errorf("failed to unmarshal configuration: %v", err) 53 // } 54 // pluginName := path.Base(pluginPath) 55 // if pluginName != net.Type { 56 // return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type) 57 // } 58 // for _, e := range environ { 59 // // Check environment for forced failure request 60 // parts := strings.Split(e, "=") 61 // if len(parts) > 0 && parts[0] == "FAIL" { 62 // return nil, fmt.Errorf("failed to execute plugin %s", pluginName) 63 // } 64 // } 65 // return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil 66 //} 67 // 68 //func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) { 69 // if len(paths) > 0 { 70 // return path.Join(paths[0], plugin), nil 71 // } 72 // return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths) 73 //} 74 75 func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) { 76 if exec == nil { 77 exec = defaultExec 78 } 79 80 stdoutBytes, err := exec.ExecPlugin(pluginPath, netconf, args.AsEnv()) 81 if err != nil { 82 return nil, err 83 } 84 85 // Plugin must return result in same version as specified in netconf 86 versionDecoder := &version.ConfigDecoder{} 87 confVersion, err := versionDecoder.Decode(netconf) 88 if err != nil { 89 return nil, err 90 } 91 92 return version.NewResult(confVersion, stdoutBytes) 93 } 94 95 func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs, exec Exec) error { 96 if exec == nil { 97 exec = defaultExec 98 } 99 _, err := exec.ExecPlugin(pluginPath, netconf, args.AsEnv()) 100 return err 101 } 102 103 // GetVersionInfo returns the version information available about the plugin. 104 // For recent-enough plugins, it uses the information returned by the VERSION 105 // command. For older plugins which do not recognize that command, it reports 106 // version 0.1.0 107 func GetVersionInfo(pluginPath string, exec Exec) (version.PluginInfo, error) { 108 if exec == nil { 109 exec = defaultExec 110 } 111 args := &Args{ 112 Command: "VERSION", 113 114 // set fake values required by plugins built against an older version of skel 115 NetNS: "dummy", 116 IfName: "dummy", 117 Path: "dummy", 118 } 119 stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current())) 120 stdoutBytes, err := exec.ExecPlugin(pluginPath, stdin, args.AsEnv()) 121 if err != nil { 122 if err.Error() == "unknown CNI_COMMAND: VERSION" { 123 return version.PluginSupports("0.1.0"), nil 124 } 125 return nil, err 126 } 127 128 return exec.Decode(stdoutBytes) 129 } 130 131 // DefaultExec is an object that implements the Exec interface which looks 132 // for and executes plugins from disk. 133 type DefaultExec struct { 134 *RawExec 135 version.PluginDecoder 136 } 137 138 // DefaultExec implements the Exec interface 139 var _ Exec = &DefaultExec{} 140 141 var defaultExec = &DefaultExec{ 142 RawExec: &RawExec{Stderr: os.Stderr}, 143 }