github.com/mccv1r0/cni@v0.7.0-alpha1/plugins/test/noop/main.go (about) 1 // Copyright 2016 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 /* 16 Noop plugin is a CNI plugin designed for use as a test-double. 17 18 When calling, set the CNI_ARGS env var equal to the path of a file containing 19 the JSON encoding of a Debug. 20 */ 21 22 package main 23 24 import ( 25 "encoding/json" 26 "errors" 27 "fmt" 28 "io/ioutil" 29 "os" 30 "strings" 31 32 "github.com/containernetworking/cni/pkg/skel" 33 "github.com/containernetworking/cni/pkg/types" 34 "github.com/containernetworking/cni/pkg/types/current" 35 "github.com/containernetworking/cni/pkg/version" 36 noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" 37 ) 38 39 type NetConf struct { 40 types.NetConf 41 DebugFile string `json:"debugFile"` 42 PrevResult *current.Result `json:"prevResult,omitempty"` 43 } 44 45 func loadConf(bytes []byte) (*NetConf, error) { 46 n := &NetConf{} 47 if err := json.Unmarshal(bytes, n); err != nil { 48 return nil, fmt.Errorf("failed to load netconf: %v %q", err, string(bytes)) 49 } 50 return n, nil 51 } 52 53 // parse extra args i.e. FOO=BAR;ABC=123 54 func parseExtraArgs(args string) (map[string]string, error) { 55 m := make(map[string]string) 56 if len(args) == 0 { 57 return m, nil 58 } 59 60 items := strings.Split(args, ";") 61 for _, item := range items { 62 kv := strings.Split(item, "=") 63 if len(kv) != 2 { 64 return nil, fmt.Errorf("CNI_ARGS invalid key/value pair: %s\n", kv) 65 } 66 m[kv[0]] = kv[1] 67 } 68 return m, nil 69 } 70 71 func getConfig(stdinData []byte, args string) (string, *NetConf, error) { 72 netConf, err := loadConf(stdinData) 73 if err != nil { 74 return "", nil, err 75 } 76 77 extraArgs, err := parseExtraArgs(args) 78 if err != nil { 79 return "", nil, err 80 } 81 82 debugFilePath, ok := extraArgs["DEBUG"] 83 if !ok { 84 debugFilePath = netConf.DebugFile 85 } 86 87 return debugFilePath, netConf, nil 88 } 89 90 func debugBehavior(args *skel.CmdArgs, command string) error { 91 debugFilePath, netConf, err := getConfig(args.StdinData, args.Args) 92 if err != nil { 93 return err 94 } 95 96 if debugFilePath == "" { 97 fmt.Printf(`{}`) 98 os.Stderr.WriteString("CNI_ARGS or config empty, no debug behavior\n") 99 return nil 100 } 101 102 debug, err := noop_debug.ReadDebug(debugFilePath) 103 if err != nil { 104 return err 105 } 106 107 debug.CmdArgs = *args 108 debug.Command = command 109 110 if debug.ReportResult == "" { 111 debug.ReportResult = fmt.Sprintf(` { "result": %q }`, noop_debug.EmptyReportResultMessage) 112 } 113 114 err = debug.WriteDebug(debugFilePath) 115 if err != nil { 116 return err 117 } 118 119 os.Stderr.WriteString(debug.ReportStderr) 120 121 if debug.ReportError != "" { 122 return errors.New(debug.ReportError) 123 } else if debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS" { 124 prevResult := netConf.PrevResult 125 if debug.ReportResult == "INJECT-DNS" { 126 prevResult, err = current.NewResultFromResult(netConf.PrevResult) 127 if err != nil { 128 return err 129 } 130 prevResult.DNS.Nameservers = []string{"1.2.3.4"} 131 } 132 133 // Must print the prevResult as the CNIVersion of the config 134 newResult, err := prevResult.GetAsVersion(netConf.CNIVersion) 135 if err != nil { 136 return fmt.Errorf("failed to convert result to config %q: %v", netConf.CNIVersion, err) 137 } 138 resultBytes, err := json.Marshal(newResult) 139 if err != nil { 140 return fmt.Errorf("failed to marshal new result: %v", err) 141 } 142 os.Stdout.WriteString(string(resultBytes)) 143 } else { 144 os.Stdout.WriteString(debug.ReportResult) 145 } 146 147 return nil 148 } 149 150 func debugGetSupportedVersions(stdinData []byte) []string { 151 vers := []string{"0.-42.0", "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"} 152 cniArgs := os.Getenv("CNI_ARGS") 153 if cniArgs == "" { 154 return vers 155 } 156 157 debugFilePath, _, err := getConfig(stdinData, cniArgs) 158 if err != nil { 159 panic("test setup error: unable to get debug file path: " + err.Error()) 160 } 161 162 debug, err := noop_debug.ReadDebug(debugFilePath) 163 if err != nil { 164 panic("test setup error: unable to read debug file: " + err.Error()) 165 } 166 if debug.ReportVersionSupport == nil { 167 return vers 168 } 169 return debug.ReportVersionSupport 170 } 171 172 func cmdAdd(args *skel.CmdArgs) error { 173 return debugBehavior(args, "ADD") 174 } 175 176 func cmdGet(args *skel.CmdArgs) error { 177 return debugBehavior(args, "GET") 178 } 179 180 func cmdDel(args *skel.CmdArgs) error { 181 return debugBehavior(args, "DEL") 182 } 183 184 func saveStdin() ([]byte, error) { 185 // Read original stdin 186 stdinData, err := ioutil.ReadAll(os.Stdin) 187 if err != nil { 188 return nil, err 189 } 190 191 // Make a new pipe for stdin, and write original stdin data to it 192 r, w, err := os.Pipe() 193 if err != nil { 194 return nil, err 195 } 196 if _, err := w.Write(stdinData); err != nil { 197 return nil, err 198 } 199 if err := w.Close(); err != nil { 200 return nil, err 201 } 202 203 os.Stdin = r 204 return stdinData, nil 205 } 206 207 func main() { 208 // Grab and read stdin before pkg/skel gets it 209 stdinData, err := saveStdin() 210 if err != nil { 211 panic("test setup error: unable to read stdin: " + err.Error()) 212 } 213 214 supportedVersions := debugGetSupportedVersions(stdinData) 215 skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.PluginSupports(supportedVersions...), "CNI nnop plugin v0.7.0") 216 }