github.com/mccv1r0/cni@v0.7.0-alpha1/libcni/api_test.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 package libcni_test 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net" 22 "os" 23 "path/filepath" 24 25 "github.com/containernetworking/cni/libcni" 26 "github.com/containernetworking/cni/pkg/skel" 27 "github.com/containernetworking/cni/pkg/types" 28 "github.com/containernetworking/cni/pkg/types/current" 29 noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug" 30 31 . "github.com/onsi/ginkgo" 32 . "github.com/onsi/gomega" 33 ) 34 35 type pluginInfo struct { 36 debugFilePath string 37 debug *noop_debug.Debug 38 config string 39 stdinData []byte 40 } 41 42 type portMapping struct { 43 HostPort int `json:"hostPort"` 44 ContainerPort int `json:"containerPort"` 45 Protocol string `json:"protocol"` 46 } 47 48 func stringInList(s string, list []string) bool { 49 for _, item := range list { 50 if s == item { 51 return true 52 } 53 } 54 return false 55 } 56 57 func newPluginInfo(configValue, prevResult string, injectDebugFilePath bool, result string, runtimeConfig map[string]interface{}, capabilities []string) pluginInfo { 58 debugFile, err := ioutil.TempFile("", "cni_debug") 59 Expect(err).NotTo(HaveOccurred()) 60 Expect(debugFile.Close()).To(Succeed()) 61 debugFilePath := debugFile.Name() 62 63 debug := &noop_debug.Debug{ 64 ReportResult: result, 65 } 66 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 67 68 // config is what would be in the plugin's on-disk configuration 69 // without runtime injected keys 70 config := fmt.Sprintf(`{"type": "noop", "some-key": "%s"`, configValue) 71 if prevResult != "" { 72 config += fmt.Sprintf(`, "prevResult": %s`, prevResult) 73 } 74 if injectDebugFilePath { 75 config += fmt.Sprintf(`, "debugFile": %q`, debugFilePath) 76 } 77 if len(capabilities) > 0 { 78 config += `, "capabilities": {` 79 for i, c := range capabilities { 80 if i > 0 { 81 config += ", " 82 } 83 config += fmt.Sprintf(`"%s": true`, c) 84 } 85 config += "}" 86 } 87 config += "}" 88 89 // stdinData is what the runtime should pass to the plugin's stdin, 90 // including injected keys like 'name', 'cniVersion', and 'runtimeConfig' 91 newConfig := make(map[string]interface{}) 92 err = json.Unmarshal([]byte(config), &newConfig) 93 Expect(err).NotTo(HaveOccurred()) 94 newConfig["name"] = "some-list" 95 newConfig["cniVersion"] = current.ImplementedSpecVersion 96 97 // Only include standard runtime config and capability args that this plugin advertises 98 newRuntimeConfig := make(map[string]interface{}) 99 for key, value := range runtimeConfig { 100 if stringInList(key, capabilities) { 101 newRuntimeConfig[key] = value 102 } 103 } 104 if len(newRuntimeConfig) > 0 { 105 newConfig["runtimeConfig"] = newRuntimeConfig 106 } 107 108 stdinData, err := json.Marshal(newConfig) 109 Expect(err).NotTo(HaveOccurred()) 110 111 return pluginInfo{ 112 debugFilePath: debugFilePath, 113 debug: debug, 114 config: config, 115 stdinData: stdinData, 116 } 117 } 118 119 func resultCacheFilePath(cacheDirPath, netName, containerID string) string { 120 return filepath.Join(cacheDirPath, "results", netName+"-"+containerID) 121 } 122 123 var _ = Describe("Invoking plugins", func() { 124 var cacheDirPath string 125 126 BeforeEach(func() { 127 var err error 128 cacheDirPath, err = ioutil.TempDir("", "cni_cachedir") 129 Expect(err).NotTo(HaveOccurred()) 130 }) 131 132 AfterEach(func() { 133 Expect(os.RemoveAll(cacheDirPath)).To(Succeed()) 134 }) 135 136 Describe("Capabilities", func() { 137 var ( 138 debugFilePath string 139 debug *noop_debug.Debug 140 pluginConfig []byte 141 cniConfig *libcni.CNIConfig 142 runtimeConfig *libcni.RuntimeConf 143 netConfig *libcni.NetworkConfig 144 ) 145 146 BeforeEach(func() { 147 debugFile, err := ioutil.TempFile("", "cni_debug") 148 Expect(err).NotTo(HaveOccurred()) 149 Expect(debugFile.Close()).To(Succeed()) 150 debugFilePath = debugFile.Name() 151 152 debug = &noop_debug.Debug{} 153 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 154 155 pluginConfig = []byte(fmt.Sprintf(`{ 156 "type": "noop", 157 "name": "apitest", 158 "cniVersion": "%s", 159 "capabilities": { 160 "portMappings": true, 161 "somethingElse": true, 162 "noCapability": false 163 } 164 }`, current.ImplementedSpecVersion)) 165 netConfig, err = libcni.ConfFromBytes(pluginConfig) 166 Expect(err).NotTo(HaveOccurred()) 167 168 cniConfig = libcni.NewCNIConfig([]string{filepath.Dir(pluginPaths["noop"])}, nil) 169 170 runtimeConfig = &libcni.RuntimeConf{ 171 ContainerID: "some-container-id", 172 NetNS: "/some/netns/path", 173 IfName: "some-eth0", 174 Args: [][2]string{{"DEBUG", debugFilePath}}, 175 CapabilityArgs: map[string]interface{}{ 176 "portMappings": []portMapping{ 177 {HostPort: 8080, ContainerPort: 80, Protocol: "tcp"}, 178 }, 179 "somethingElse": []string{"foobar", "baz"}, 180 "noCapability": true, 181 "notAdded": []bool{true, false}, 182 }, 183 CacheDir: cacheDirPath, 184 } 185 }) 186 187 AfterEach(func() { 188 Expect(os.RemoveAll(debugFilePath)).To(Succeed()) 189 }) 190 191 It("adds correct runtime config for capabilities to stdin", func() { 192 _, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 193 Expect(err).NotTo(HaveOccurred()) 194 195 debug, err = noop_debug.ReadDebug(debugFilePath) 196 Expect(err).NotTo(HaveOccurred()) 197 Expect(debug.Command).To(Equal("ADD")) 198 199 conf := make(map[string]interface{}) 200 err = json.Unmarshal(debug.CmdArgs.StdinData, &conf) 201 Expect(err).NotTo(HaveOccurred()) 202 203 // We expect runtimeConfig keys only for portMappings and somethingElse 204 rawRc := conf["runtimeConfig"] 205 rc, ok := rawRc.(map[string]interface{}) 206 Expect(ok).To(Equal(true)) 207 expectedKeys := []string{"portMappings", "somethingElse"} 208 Expect(len(rc)).To(Equal(len(expectedKeys))) 209 for _, key := range expectedKeys { 210 _, ok := rc[key] 211 Expect(ok).To(Equal(true)) 212 } 213 }) 214 215 It("adds no runtimeConfig when the plugin advertises no used capabilities", func() { 216 // Replace CapabilityArgs with ones we know the plugin 217 // doesn't support 218 runtimeConfig.CapabilityArgs = map[string]interface{}{ 219 "portMappings22": []portMapping{ 220 {HostPort: 8080, ContainerPort: 80, Protocol: "tcp"}, 221 }, 222 "somethingElse22": []string{"foobar", "baz"}, 223 } 224 225 _, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 226 Expect(err).NotTo(HaveOccurred()) 227 228 debug, err = noop_debug.ReadDebug(debugFilePath) 229 Expect(err).NotTo(HaveOccurred()) 230 Expect(debug.Command).To(Equal("ADD")) 231 232 conf := make(map[string]interface{}) 233 err = json.Unmarshal(debug.CmdArgs.StdinData, &conf) 234 Expect(err).NotTo(HaveOccurred()) 235 236 // No intersection of plugin capabilities and CapabilityArgs, 237 // so plugin should not receive a "runtimeConfig" key 238 _, ok := conf["runtimeConfig"] 239 Expect(ok).Should(BeFalse()) 240 }) 241 }) 242 243 Describe("Invoking a single plugin", func() { 244 var ( 245 debugFilePath string 246 debug *noop_debug.Debug 247 cniBinPath string 248 pluginConfig string 249 cniConfig *libcni.CNIConfig 250 netConfig *libcni.NetworkConfig 251 runtimeConfig *libcni.RuntimeConf 252 253 expectedCmdArgs skel.CmdArgs 254 ) 255 256 BeforeEach(func() { 257 debugFile, err := ioutil.TempFile("", "cni_debug") 258 Expect(err).NotTo(HaveOccurred()) 259 Expect(debugFile.Close()).To(Succeed()) 260 debugFilePath = debugFile.Name() 261 262 debug = &noop_debug.Debug{ 263 ReportResult: `{ 264 "cniVersion": "0.4.0", 265 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 266 "dns": {} 267 }`, 268 } 269 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 270 271 portMappings := []portMapping{ 272 {HostPort: 8080, ContainerPort: 80, Protocol: "tcp"}, 273 } 274 275 cniBinPath = filepath.Dir(pluginPaths["noop"]) 276 pluginConfig = fmt.Sprintf(`{ 277 "type": "noop", 278 "name": "apitest", 279 "some-key": "some-value", 280 "cniVersion": "%s", 281 "capabilities": { "portMappings": true } 282 }`, current.ImplementedSpecVersion) 283 cniConfig = libcni.NewCNIConfig([]string{cniBinPath}, nil) 284 netConfig, err = libcni.ConfFromBytes([]byte(pluginConfig)) 285 Expect(err).NotTo(HaveOccurred()) 286 runtimeConfig = &libcni.RuntimeConf{ 287 ContainerID: "some-container-id", 288 NetNS: "/some/netns/path", 289 IfName: "some-eth0", 290 CacheDir: cacheDirPath, 291 Args: [][2]string{{"DEBUG", debugFilePath}}, 292 CapabilityArgs: map[string]interface{}{ 293 "portMappings": portMappings, 294 }, 295 } 296 297 // inject runtime args into the expected plugin config 298 conf := make(map[string]interface{}) 299 err = json.Unmarshal([]byte(pluginConfig), &conf) 300 Expect(err).NotTo(HaveOccurred()) 301 conf["runtimeConfig"] = map[string]interface{}{ 302 "portMappings": portMappings, 303 } 304 newBytes, err := json.Marshal(conf) 305 Expect(err).NotTo(HaveOccurred()) 306 307 expectedCmdArgs = skel.CmdArgs{ 308 ContainerID: "some-container-id", 309 Netns: "/some/netns/path", 310 IfName: "some-eth0", 311 Args: "DEBUG=" + debugFilePath, 312 Path: cniBinPath, 313 StdinData: newBytes, 314 } 315 }) 316 317 AfterEach(func() { 318 Expect(os.RemoveAll(debugFilePath)).To(Succeed()) 319 }) 320 321 Describe("AddNetwork", func() { 322 It("executes the plugin with command ADD", func() { 323 r, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 324 Expect(err).NotTo(HaveOccurred()) 325 326 result, err := current.GetResult(r) 327 Expect(err).NotTo(HaveOccurred()) 328 329 Expect(result).To(Equal(¤t.Result{ 330 CNIVersion: current.ImplementedSpecVersion, 331 IPs: []*current.IPConfig{ 332 { 333 Version: "4", 334 Address: net.IPNet{ 335 IP: net.ParseIP("10.1.2.3"), 336 Mask: net.IPv4Mask(255, 255, 255, 0), 337 }, 338 }, 339 }, 340 })) 341 342 debug, err := noop_debug.ReadDebug(debugFilePath) 343 Expect(err).NotTo(HaveOccurred()) 344 Expect(debug.Command).To(Equal("ADD")) 345 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 346 Expect(string(debug.CmdArgs.StdinData)).To(ContainSubstring("\"portMappings\":")) 347 348 // Ensure the cached result matches the returned one 349 cacheFile := resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 350 _, err = os.Stat(cacheFile) 351 Expect(err).NotTo(HaveOccurred()) 352 cachedData, err := ioutil.ReadFile(cacheFile) 353 Expect(err).NotTo(HaveOccurred()) 354 returnedData, err := json.Marshal(result) 355 Expect(err).NotTo(HaveOccurred()) 356 Expect(cachedData).To(MatchJSON(returnedData)) 357 }) 358 359 Context("when finding the plugin fails", func() { 360 BeforeEach(func() { 361 netConfig.Network.Type = "does-not-exist" 362 }) 363 364 It("returns the error", func() { 365 _, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 366 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 367 }) 368 }) 369 370 Context("when the plugin errors", func() { 371 BeforeEach(func() { 372 debug.ReportError = "plugin error: banana" 373 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 374 }) 375 It("unmarshals and returns the error", func() { 376 result, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 377 Expect(result).To(BeNil()) 378 Expect(err).To(MatchError("plugin error: banana")) 379 }) 380 }) 381 382 Context("when the result cache directory cannot be accessed", func() { 383 It("returns an error", func() { 384 // Make the results directory inaccessble by making it a 385 // file instead of a directory 386 tmpPath := filepath.Join(cacheDirPath, "results") 387 err := ioutil.WriteFile(tmpPath, []byte("afdsasdfasdf"), 0600) 388 Expect(err).NotTo(HaveOccurred()) 389 390 result, err := cniConfig.AddNetwork(netConfig, runtimeConfig) 391 Expect(result).To(BeNil()) 392 Expect(err).To(HaveOccurred()) 393 }) 394 }) 395 }) 396 397 Describe("GetNetwork", func() { 398 It("executes the plugin with command GET", func() { 399 cacheFile := resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 400 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 401 Expect(err).NotTo(HaveOccurred()) 402 cachedJson := `{ 403 "cniVersion": "0.4.0", 404 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 405 "dns": {} 406 }` 407 err = ioutil.WriteFile(cacheFile, []byte(cachedJson), 0600) 408 Expect(err).NotTo(HaveOccurred()) 409 410 r, err := cniConfig.GetNetwork(netConfig, runtimeConfig) 411 Expect(err).NotTo(HaveOccurred()) 412 413 result, err := current.GetResult(r) 414 Expect(err).NotTo(HaveOccurred()) 415 416 Expect(result).To(Equal(¤t.Result{ 417 CNIVersion: current.ImplementedSpecVersion, 418 IPs: []*current.IPConfig{ 419 { 420 Version: "4", 421 Address: net.IPNet{ 422 IP: net.ParseIP("10.1.2.3"), 423 Mask: net.IPv4Mask(255, 255, 255, 0), 424 }, 425 }, 426 }, 427 })) 428 429 debug, err := noop_debug.ReadDebug(debugFilePath) 430 Expect(err).NotTo(HaveOccurred()) 431 Expect(debug.Command).To(Equal("GET")) 432 Expect(string(debug.CmdArgs.StdinData)).To(ContainSubstring("\"portMappings\":")) 433 434 // Explicitly match stdin data as json after 435 // inserting the expected prevResult 436 var data, data2 map[string]interface{} 437 err = json.Unmarshal(expectedCmdArgs.StdinData, &data) 438 Expect(err).NotTo(HaveOccurred()) 439 err = json.Unmarshal([]byte(cachedJson), &data2) 440 Expect(err).NotTo(HaveOccurred()) 441 data["prevResult"] = data2 442 expectedStdinJson, err := json.Marshal(data) 443 Expect(err).NotTo(HaveOccurred()) 444 Expect(debug.CmdArgs.StdinData).To(MatchJSON(expectedStdinJson)) 445 446 debug.CmdArgs.StdinData = nil 447 expectedCmdArgs.StdinData = nil 448 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 449 }) 450 451 Context("when finding the plugin fails", func() { 452 BeforeEach(func() { 453 netConfig.Network.Type = "does-not-exist" 454 }) 455 456 It("returns the error", func() { 457 _, err := cniConfig.GetNetwork(netConfig, runtimeConfig) 458 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 459 }) 460 }) 461 462 Context("when the plugin errors", func() { 463 BeforeEach(func() { 464 debug.ReportError = "plugin error: banana" 465 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 466 }) 467 It("unmarshals and returns the error", func() { 468 result, err := cniConfig.GetNetwork(netConfig, runtimeConfig) 469 Expect(result).To(BeNil()) 470 Expect(err).To(MatchError("plugin error: banana")) 471 }) 472 }) 473 474 Context("when GET is called with a configuration version", func() { 475 var cacheFile string 476 477 BeforeEach(func() { 478 cacheFile = resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 479 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 480 Expect(err).NotTo(HaveOccurred()) 481 }) 482 483 Context("less than 0.4.0", func() { 484 It("fails as GET is not supported before 0.4.0", func() { 485 // Generate plugin config with older version 486 pluginConfig = `{ 487 "type": "noop", 488 "name": "apitest", 489 "cniVersion": "0.3.1" 490 }` 491 var err error 492 netConfig, err = libcni.ConfFromBytes([]byte(pluginConfig)) 493 Expect(err).NotTo(HaveOccurred()) 494 _, err = cniConfig.GetNetwork(netConfig, runtimeConfig) 495 Expect(err).To(MatchError("configuration version \"0.3.1\" does not support the GET command")) 496 497 debug, err := noop_debug.ReadDebug(debugFilePath) 498 Expect(err).NotTo(HaveOccurred()) 499 Expect(string(debug.CmdArgs.StdinData)).NotTo(ContainSubstring("\"prevResult\":")) 500 }) 501 }) 502 503 Context("equal to 0.4.0", func() { 504 It("passes a prevResult to the plugin", func() { 505 err := ioutil.WriteFile(cacheFile, []byte(`{ 506 "cniVersion": "0.4.0", 507 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 508 "dns": {} 509 }`), 0600) 510 Expect(err).NotTo(HaveOccurred()) 511 512 _, err = cniConfig.GetNetwork(netConfig, runtimeConfig) 513 Expect(err).NotTo(HaveOccurred()) 514 debug, err := noop_debug.ReadDebug(debugFilePath) 515 Expect(err).NotTo(HaveOccurred()) 516 Expect(string(debug.CmdArgs.StdinData)).To(ContainSubstring("\"prevResult\":")) 517 }) 518 }) 519 }) 520 521 Context("when the cached result", func() { 522 var cacheFile string 523 524 BeforeEach(func() { 525 cacheFile = resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 526 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 527 Expect(err).NotTo(HaveOccurred()) 528 }) 529 530 Context("is invalid JSON", func() { 531 It("returns an error", func() { 532 err := ioutil.WriteFile(cacheFile, []byte("adfadsfasdfasfdsafaf"), 0600) 533 Expect(err).NotTo(HaveOccurred()) 534 535 result, err := cniConfig.GetNetwork(netConfig, runtimeConfig) 536 Expect(result).To(BeNil()) 537 Expect(err).To(MatchError("failed to get network 'apitest' cached result: decoding version from network config: invalid character 'a' looking for beginning of value")) 538 }) 539 }) 540 541 Context("version doesn't match the config version", func() { 542 It("succeeds when the cached result can be converted", func() { 543 err := ioutil.WriteFile(cacheFile, []byte(`{ 544 "cniVersion": "0.3.1", 545 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 546 "dns": {} 547 }`), 0600) 548 Expect(err).NotTo(HaveOccurred()) 549 550 _, err = cniConfig.GetNetwork(netConfig, runtimeConfig) 551 Expect(err).NotTo(HaveOccurred()) 552 }) 553 554 It("returns an error when the cached result cannot be converted", func() { 555 err := ioutil.WriteFile(cacheFile, []byte(`{ 556 "cniVersion": "0.4567.0", 557 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 558 "dns": {} 559 }`), 0600) 560 Expect(err).NotTo(HaveOccurred()) 561 562 result, err := cniConfig.GetNetwork(netConfig, runtimeConfig) 563 Expect(result).To(BeNil()) 564 Expect(err).To(MatchError("failed to get network 'apitest' cached result: unsupported CNI result version \"0.4567.0\"")) 565 }) 566 }) 567 }) 568 }) 569 570 Describe("DelNetwork", func() { 571 It("executes the plugin with command DEL", func() { 572 cacheFile := resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 573 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 574 Expect(err).NotTo(HaveOccurred()) 575 cachedJson := `{ 576 "cniVersion": "0.4.0", 577 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 578 "dns": {} 579 }` 580 err = ioutil.WriteFile(cacheFile, []byte(cachedJson), 0600) 581 Expect(err).NotTo(HaveOccurred()) 582 583 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 584 Expect(err).NotTo(HaveOccurred()) 585 586 debug, err := noop_debug.ReadDebug(debugFilePath) 587 Expect(err).NotTo(HaveOccurred()) 588 Expect(debug.Command).To(Equal("DEL")) 589 Expect(string(debug.CmdArgs.StdinData)).To(ContainSubstring("\"portMappings\":")) 590 591 // Explicitly match stdin data as json after 592 // inserting the expected prevResult 593 var data, data2 map[string]interface{} 594 err = json.Unmarshal(expectedCmdArgs.StdinData, &data) 595 Expect(err).NotTo(HaveOccurred()) 596 err = json.Unmarshal([]byte(cachedJson), &data2) 597 Expect(err).NotTo(HaveOccurred()) 598 data["prevResult"] = data2 599 expectedStdinJson, err := json.Marshal(data) 600 Expect(err).NotTo(HaveOccurred()) 601 Expect(debug.CmdArgs.StdinData).To(MatchJSON(expectedStdinJson)) 602 603 debug.CmdArgs.StdinData = nil 604 expectedCmdArgs.StdinData = nil 605 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 606 }) 607 608 Context("when finding the plugin fails", func() { 609 BeforeEach(func() { 610 netConfig.Network.Type = "does-not-exist" 611 }) 612 613 It("returns the error", func() { 614 err := cniConfig.DelNetwork(netConfig, runtimeConfig) 615 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 616 }) 617 }) 618 619 Context("when the plugin errors", func() { 620 BeforeEach(func() { 621 debug.ReportError = "plugin error: banana" 622 Expect(debug.WriteDebug(debugFilePath)).To(Succeed()) 623 }) 624 It("unmarshals and returns the error", func() { 625 err := cniConfig.DelNetwork(netConfig, runtimeConfig) 626 Expect(err).To(MatchError("plugin error: banana")) 627 }) 628 }) 629 630 Context("when DEL is called twice", func() { 631 var cacheFile string 632 633 BeforeEach(func() { 634 cacheFile = resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 635 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 636 Expect(err).NotTo(HaveOccurred()) 637 }) 638 639 It("deletes the cached result after the first DEL", func() { 640 err := ioutil.WriteFile(cacheFile, []byte(`{ 641 "cniVersion": "0.4.0", 642 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 643 "dns": {} 644 }`), 0600) 645 Expect(err).NotTo(HaveOccurred()) 646 647 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 648 Expect(err).NotTo(HaveOccurred()) 649 _, err = ioutil.ReadFile(cacheFile) 650 Expect(err).To(HaveOccurred()) 651 652 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 653 Expect(err).NotTo(HaveOccurred()) 654 }) 655 }) 656 657 Context("when DEL is called with a configuration version", func() { 658 var cacheFile string 659 660 BeforeEach(func() { 661 cacheFile = resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 662 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 663 Expect(err).NotTo(HaveOccurred()) 664 }) 665 666 Context("less than 0.4.0", func() { 667 It("does not pass a prevResult to the plugin", func() { 668 err := ioutil.WriteFile(cacheFile, []byte(`{ 669 "cniVersion": "0.3.1", 670 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 671 "dns": {} 672 }`), 0600) 673 Expect(err).NotTo(HaveOccurred()) 674 675 // Generate plugin config with older version 676 pluginConfig = `{ 677 "type": "noop", 678 "name": "apitest", 679 "cniVersion": "0.3.1" 680 }` 681 netConfig, err = libcni.ConfFromBytes([]byte(pluginConfig)) 682 Expect(err).NotTo(HaveOccurred()) 683 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 684 Expect(err).NotTo(HaveOccurred()) 685 686 debug, err := noop_debug.ReadDebug(debugFilePath) 687 Expect(err).NotTo(HaveOccurred()) 688 Expect(debug.Command).To(Equal("DEL")) 689 Expect(string(debug.CmdArgs.StdinData)).NotTo(ContainSubstring("\"prevResult\":")) 690 }) 691 }) 692 693 Context("equal to 0.4.0", func() { 694 It("passes a prevResult to the plugin", func() { 695 err := ioutil.WriteFile(cacheFile, []byte(`{ 696 "cniVersion": "0.4.0", 697 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 698 "dns": {} 699 }`), 0600) 700 Expect(err).NotTo(HaveOccurred()) 701 702 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 703 Expect(err).NotTo(HaveOccurred()) 704 705 debug, err := noop_debug.ReadDebug(debugFilePath) 706 Expect(err).NotTo(HaveOccurred()) 707 Expect(debug.Command).To(Equal("DEL")) 708 Expect(string(debug.CmdArgs.StdinData)).To(ContainSubstring("\"prevResult\":")) 709 }) 710 }) 711 }) 712 713 Context("when the cached result", func() { 714 var cacheFile string 715 716 BeforeEach(func() { 717 cacheFile = resultCacheFilePath(cacheDirPath, netConfig.Network.Name, runtimeConfig.ContainerID) 718 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 719 Expect(err).NotTo(HaveOccurred()) 720 }) 721 722 Context("is invalid JSON", func() { 723 It("returns an error", func() { 724 err := ioutil.WriteFile(cacheFile, []byte("adfadsfasdfasfdsafaf"), 0600) 725 Expect(err).NotTo(HaveOccurred()) 726 727 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 728 Expect(err).To(MatchError("failed to get network 'apitest' cached result: decoding version from network config: invalid character 'a' looking for beginning of value")) 729 }) 730 }) 731 732 Context("version doesn't match the config version", func() { 733 It("succeeds when the cached result can be converted", func() { 734 err := ioutil.WriteFile(cacheFile, []byte(`{ 735 "cniVersion": "0.3.1", 736 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 737 "dns": {} 738 }`), 0600) 739 Expect(err).NotTo(HaveOccurred()) 740 741 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 742 Expect(err).NotTo(HaveOccurred()) 743 }) 744 745 It("returns an error when the cached result cannot be converted", func() { 746 err := ioutil.WriteFile(cacheFile, []byte(`{ 747 "cniVersion": "0.4567.0", 748 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 749 "dns": {} 750 }`), 0600) 751 Expect(err).NotTo(HaveOccurred()) 752 753 err = cniConfig.DelNetwork(netConfig, runtimeConfig) 754 Expect(err).To(MatchError("failed to get network 'apitest' cached result: unsupported CNI result version \"0.4567.0\"")) 755 }) 756 }) 757 }) 758 }) 759 760 Describe("GetVersionInfo", func() { 761 It("executes the plugin with the command VERSION", func() { 762 versionInfo, err := cniConfig.GetVersionInfo("noop") 763 Expect(err).NotTo(HaveOccurred()) 764 765 Expect(versionInfo).NotTo(BeNil()) 766 Expect(versionInfo.SupportedVersions()).To(Equal([]string{ 767 "0.-42.0", "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", 768 })) 769 }) 770 771 Context("when finding the plugin fails", func() { 772 It("returns the error", func() { 773 _, err := cniConfig.GetVersionInfo("does-not-exist") 774 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 775 }) 776 }) 777 }) 778 }) 779 780 Describe("Invoking a plugin list", func() { 781 var ( 782 plugins []pluginInfo 783 cniBinPath string 784 cniConfig *libcni.CNIConfig 785 netConfigList *libcni.NetworkConfigList 786 runtimeConfig *libcni.RuntimeConf 787 788 expectedCmdArgs skel.CmdArgs 789 ) 790 791 BeforeEach(func() { 792 var err error 793 794 capabilityArgs := map[string]interface{}{ 795 "portMappings": []portMapping{ 796 {HostPort: 8080, ContainerPort: 80, Protocol: "tcp"}, 797 }, 798 "otherCapability": 33, 799 } 800 801 cniBinPath = filepath.Dir(pluginPaths["noop"]) 802 cniConfig = libcni.NewCNIConfig([]string{cniBinPath}, nil) 803 runtimeConfig = &libcni.RuntimeConf{ 804 ContainerID: "some-container-id", 805 NetNS: "/some/netns/path", 806 IfName: "some-eth0", 807 Args: [][2]string{{"FOO", "BAR"}}, 808 CapabilityArgs: capabilityArgs, 809 CacheDir: cacheDirPath, 810 } 811 812 expectedCmdArgs = skel.CmdArgs{ 813 ContainerID: runtimeConfig.ContainerID, 814 Netns: runtimeConfig.NetNS, 815 IfName: runtimeConfig.IfName, 816 Args: "FOO=BAR", 817 Path: cniBinPath, 818 } 819 820 rc := map[string]interface{}{ 821 "containerId": runtimeConfig.ContainerID, 822 "netNs": runtimeConfig.NetNS, 823 "ifName": runtimeConfig.IfName, 824 "args": map[string]string{ 825 "FOO": "BAR", 826 }, 827 "portMappings": capabilityArgs["portMappings"], 828 "otherCapability": capabilityArgs["otherCapability"], 829 } 830 831 ipResult := fmt.Sprintf(`{"cniVersion": "%s", "dns":{},"ips":[{"version": "4", "address": "10.1.2.3/24"}]}`, current.ImplementedSpecVersion) 832 plugins = make([]pluginInfo, 3, 3) 833 plugins[0] = newPluginInfo("some-value", "", true, ipResult, rc, []string{"portMappings", "otherCapability"}) 834 plugins[1] = newPluginInfo("some-other-value", ipResult, true, "PASSTHROUGH", rc, []string{"otherCapability"}) 835 plugins[2] = newPluginInfo("yet-another-value", ipResult, true, "INJECT-DNS", rc, []string{}) 836 837 configList := []byte(fmt.Sprintf(`{ 838 "name": "some-list", 839 "cniVersion": "%s", 840 "plugins": [ 841 %s, 842 %s, 843 %s 844 ] 845 }`, current.ImplementedSpecVersion, plugins[0].config, plugins[1].config, plugins[2].config)) 846 847 netConfigList, err = libcni.ConfListFromBytes(configList) 848 Expect(err).NotTo(HaveOccurred()) 849 }) 850 851 AfterEach(func() { 852 for _, p := range plugins { 853 Expect(os.RemoveAll(p.debugFilePath)).To(Succeed()) 854 } 855 }) 856 857 Describe("AddNetworkList", func() { 858 It("executes all plugins with command ADD and returns an intermediate result", func() { 859 r, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig) 860 Expect(err).NotTo(HaveOccurred()) 861 862 result, err := current.GetResult(r) 863 Expect(err).NotTo(HaveOccurred()) 864 865 Expect(result).To(Equal(¤t.Result{ 866 CNIVersion: current.ImplementedSpecVersion, 867 // IP4 added by first plugin 868 IPs: []*current.IPConfig{ 869 { 870 Version: "4", 871 Address: net.IPNet{ 872 IP: net.ParseIP("10.1.2.3"), 873 Mask: net.IPv4Mask(255, 255, 255, 0), 874 }, 875 }, 876 }, 877 // DNS injected by last plugin 878 DNS: types.DNS{ 879 Nameservers: []string{"1.2.3.4"}, 880 }, 881 })) 882 883 for i := 0; i < len(plugins); i++ { 884 debug, err := noop_debug.ReadDebug(plugins[i].debugFilePath) 885 Expect(err).NotTo(HaveOccurred()) 886 Expect(debug.Command).To(Equal("ADD")) 887 888 // Must explicitly match JSON due to dict element ordering 889 Expect(debug.CmdArgs.StdinData).To(MatchJSON(plugins[i].stdinData)) 890 debug.CmdArgs.StdinData = nil 891 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 892 } 893 }) 894 895 It("writes the correct cached result", func() { 896 r, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig) 897 Expect(err).NotTo(HaveOccurred()) 898 899 result, err := current.GetResult(r) 900 Expect(err).NotTo(HaveOccurred()) 901 902 // Ensure the cached result matches the returned one 903 cacheFile := resultCacheFilePath(cacheDirPath, netConfigList.Name, runtimeConfig.ContainerID) 904 _, err = os.Stat(cacheFile) 905 Expect(err).NotTo(HaveOccurred()) 906 cachedData, err := ioutil.ReadFile(cacheFile) 907 Expect(err).NotTo(HaveOccurred()) 908 returnedData, err := json.Marshal(result) 909 Expect(err).NotTo(HaveOccurred()) 910 Expect(cachedData).To(MatchJSON(returnedData)) 911 }) 912 913 Context("when finding the plugin fails", func() { 914 BeforeEach(func() { 915 netConfigList.Plugins[1].Network.Type = "does-not-exist" 916 }) 917 918 It("returns the error", func() { 919 _, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig) 920 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 921 }) 922 }) 923 924 Context("when the second plugin errors", func() { 925 BeforeEach(func() { 926 plugins[1].debug.ReportError = "plugin error: banana" 927 Expect(plugins[1].debug.WriteDebug(plugins[1].debugFilePath)).To(Succeed()) 928 }) 929 It("unmarshals and returns the error", func() { 930 result, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig) 931 Expect(result).To(BeNil()) 932 Expect(err).To(MatchError("plugin error: banana")) 933 }) 934 }) 935 936 Context("when the result cache directory cannot be accessed", func() { 937 It("returns an error", func() { 938 // Make the results directory inaccessble by making it a 939 // file instead of a directory 940 tmpPath := filepath.Join(cacheDirPath, "results") 941 err := ioutil.WriteFile(tmpPath, []byte("afdsasdfasdf"), 0600) 942 Expect(err).NotTo(HaveOccurred()) 943 944 result, err := cniConfig.AddNetworkList(netConfigList, runtimeConfig) 945 Expect(result).To(BeNil()) 946 Expect(err).To(HaveOccurred()) 947 }) 948 }) 949 }) 950 951 Describe("GetNetworkList", func() { 952 It("executes all plugins with command GET and returns an intermediate result", func() { 953 r, err := cniConfig.GetNetworkList(netConfigList, runtimeConfig) 954 Expect(err).NotTo(HaveOccurred()) 955 956 result, err := current.GetResult(r) 957 Expect(err).NotTo(HaveOccurred()) 958 959 Expect(result).To(Equal(¤t.Result{ 960 CNIVersion: current.ImplementedSpecVersion, 961 // IP4 added by first plugin 962 IPs: []*current.IPConfig{ 963 { 964 Version: "4", 965 Address: net.IPNet{ 966 IP: net.ParseIP("10.1.2.3"), 967 Mask: net.IPv4Mask(255, 255, 255, 0), 968 }, 969 }, 970 }, 971 // DNS injected by last plugin 972 DNS: types.DNS{ 973 Nameservers: []string{"1.2.3.4"}, 974 }, 975 })) 976 977 for i := 0; i < len(plugins); i++ { 978 debug, err := noop_debug.ReadDebug(plugins[i].debugFilePath) 979 Expect(err).NotTo(HaveOccurred()) 980 Expect(debug.Command).To(Equal("GET")) 981 982 // Must explicitly match JSON due to dict element ordering 983 Expect(debug.CmdArgs.StdinData).To(MatchJSON(plugins[i].stdinData)) 984 debug.CmdArgs.StdinData = nil 985 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 986 } 987 }) 988 989 Context("when the configuration version", func() { 990 var cacheFile string 991 992 BeforeEach(func() { 993 cacheFile = resultCacheFilePath(cacheDirPath, netConfigList.Name, runtimeConfig.ContainerID) 994 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 995 Expect(err).NotTo(HaveOccurred()) 996 }) 997 998 Context("is 0.4.0", func() { 999 It("passes a cached result to the first plugin", func() { 1000 cachedJson := `{ 1001 "cniVersion": "0.4.0", 1002 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 1003 "dns": {} 1004 }` 1005 err := ioutil.WriteFile(cacheFile, []byte(cachedJson), 0600) 1006 Expect(err).NotTo(HaveOccurred()) 1007 1008 _, err = cniConfig.GetNetworkList(netConfigList, runtimeConfig) 1009 Expect(err).NotTo(HaveOccurred()) 1010 1011 // Match the first plugin's stdin config to the cached result JSON 1012 debug, err := noop_debug.ReadDebug(plugins[0].debugFilePath) 1013 Expect(err).NotTo(HaveOccurred()) 1014 1015 var data map[string]interface{} 1016 err = json.Unmarshal(debug.CmdArgs.StdinData, &data) 1017 Expect(err).NotTo(HaveOccurred()) 1018 stdinPrevResult, err := json.Marshal(data["prevResult"]) 1019 Expect(err).NotTo(HaveOccurred()) 1020 Expect(stdinPrevResult).To(MatchJSON(cachedJson)) 1021 }) 1022 }) 1023 1024 Context("is less than 0.4.0", func() { 1025 It("fails as GET is not supported before 0.4.0", func() { 1026 // Set an older CNI version 1027 confList := make(map[string]interface{}) 1028 err := json.Unmarshal(netConfigList.Bytes, &confList) 1029 Expect(err).NotTo(HaveOccurred()) 1030 confList["cniVersion"] = "0.3.1" 1031 newBytes, err := json.Marshal(confList) 1032 Expect(err).NotTo(HaveOccurred()) 1033 1034 netConfigList, err = libcni.ConfListFromBytes(newBytes) 1035 Expect(err).NotTo(HaveOccurred()) 1036 _, err = cniConfig.GetNetworkList(netConfigList, runtimeConfig) 1037 Expect(err).To(MatchError("configuration version \"0.3.1\" does not support the GET command")) 1038 }) 1039 }) 1040 }) 1041 1042 Context("when finding the plugin fails", func() { 1043 BeforeEach(func() { 1044 netConfigList.Plugins[1].Network.Type = "does-not-exist" 1045 }) 1046 1047 It("returns the error", func() { 1048 _, err := cniConfig.GetNetworkList(netConfigList, runtimeConfig) 1049 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 1050 }) 1051 }) 1052 1053 Context("when the second plugin errors", func() { 1054 BeforeEach(func() { 1055 plugins[1].debug.ReportError = "plugin error: banana" 1056 Expect(plugins[1].debug.WriteDebug(plugins[1].debugFilePath)).To(Succeed()) 1057 }) 1058 It("unmarshals and returns the error", func() { 1059 result, err := cniConfig.GetNetworkList(netConfigList, runtimeConfig) 1060 Expect(result).To(BeNil()) 1061 Expect(err).To(MatchError("plugin error: banana")) 1062 }) 1063 }) 1064 1065 Context("when the cached result is invalid", func() { 1066 It("returns an error", func() { 1067 cacheFile := resultCacheFilePath(cacheDirPath, netConfigList.Name, runtimeConfig.ContainerID) 1068 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 1069 Expect(err).NotTo(HaveOccurred()) 1070 err = ioutil.WriteFile(cacheFile, []byte("adfadsfasdfasfdsafaf"), 0600) 1071 Expect(err).NotTo(HaveOccurred()) 1072 1073 result, err := cniConfig.GetNetworkList(netConfigList, runtimeConfig) 1074 Expect(result).To(BeNil()) 1075 Expect(err).To(MatchError("failed to get network 'some-list' cached result: decoding version from network config: invalid character 'a' looking for beginning of value")) 1076 }) 1077 }) 1078 }) 1079 1080 Describe("DelNetworkList", func() { 1081 It("executes all the plugins in reverse order with command DEL", func() { 1082 err := cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1083 Expect(err).NotTo(HaveOccurred()) 1084 1085 for i := 0; i < len(plugins); i++ { 1086 debug, err := noop_debug.ReadDebug(plugins[i].debugFilePath) 1087 Expect(err).NotTo(HaveOccurred()) 1088 Expect(debug.Command).To(Equal("DEL")) 1089 1090 // Must explicitly match JSON due to dict element ordering 1091 Expect(debug.CmdArgs.StdinData).To(MatchJSON(plugins[i].stdinData)) 1092 debug.CmdArgs.StdinData = nil 1093 Expect(debug.CmdArgs).To(Equal(expectedCmdArgs)) 1094 } 1095 }) 1096 1097 Context("when the configuration version", func() { 1098 var cacheFile string 1099 1100 BeforeEach(func() { 1101 cacheFile = resultCacheFilePath(cacheDirPath, netConfigList.Name, runtimeConfig.ContainerID) 1102 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 1103 Expect(err).NotTo(HaveOccurred()) 1104 }) 1105 1106 Context("is 0.4.0", func() { 1107 It("passes a cached result to the first plugin", func() { 1108 cachedJson := `{ 1109 "cniVersion": "0.4.0", 1110 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 1111 "dns": {} 1112 }` 1113 err := ioutil.WriteFile(cacheFile, []byte(cachedJson), 0600) 1114 Expect(err).NotTo(HaveOccurred()) 1115 1116 err = cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1117 Expect(err).NotTo(HaveOccurred()) 1118 1119 // Match the first plugin's stdin config to the cached result JSON 1120 debug, err := noop_debug.ReadDebug(plugins[0].debugFilePath) 1121 Expect(err).NotTo(HaveOccurred()) 1122 1123 var data map[string]interface{} 1124 err = json.Unmarshal(debug.CmdArgs.StdinData, &data) 1125 Expect(err).NotTo(HaveOccurred()) 1126 stdinPrevResult, err := json.Marshal(data["prevResult"]) 1127 Expect(err).NotTo(HaveOccurred()) 1128 Expect(stdinPrevResult).To(MatchJSON(cachedJson)) 1129 }) 1130 }) 1131 1132 Context("is less than 0.4.0", func() { 1133 It("does not pass a cached result to the first plugin", func() { 1134 err := ioutil.WriteFile(cacheFile, []byte(`{ 1135 "cniVersion": "0.3.1", 1136 "ips": [{"version": "4", "address": "10.1.2.3/24"}], 1137 "dns": {} 1138 }`), 0600) 1139 Expect(err).NotTo(HaveOccurred()) 1140 1141 // Set an older CNI version 1142 confList := make(map[string]interface{}) 1143 err = json.Unmarshal(netConfigList.Bytes, &confList) 1144 Expect(err).NotTo(HaveOccurred()) 1145 confList["cniVersion"] = "0.3.1" 1146 newBytes, err := json.Marshal(confList) 1147 Expect(err).NotTo(HaveOccurred()) 1148 1149 netConfigList, err = libcni.ConfListFromBytes(newBytes) 1150 Expect(err).NotTo(HaveOccurred()) 1151 err = cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1152 Expect(err).NotTo(HaveOccurred()) 1153 1154 // Make sure first plugin does not receive a prevResult 1155 debug, err := noop_debug.ReadDebug(plugins[0].debugFilePath) 1156 Expect(err).NotTo(HaveOccurred()) 1157 Expect(string(debug.CmdArgs.StdinData)).NotTo(ContainSubstring("\"prevResult\":")) 1158 }) 1159 }) 1160 }) 1161 1162 Context("when finding the plugin fails", func() { 1163 BeforeEach(func() { 1164 netConfigList.Plugins[1].Network.Type = "does-not-exist" 1165 }) 1166 1167 It("returns the error", func() { 1168 err := cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1169 Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`))) 1170 }) 1171 }) 1172 1173 Context("when the plugin errors", func() { 1174 BeforeEach(func() { 1175 plugins[1].debug.ReportError = "plugin error: banana" 1176 Expect(plugins[1].debug.WriteDebug(plugins[1].debugFilePath)).To(Succeed()) 1177 }) 1178 It("unmarshals and returns the error", func() { 1179 err := cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1180 Expect(err).To(MatchError("plugin error: banana")) 1181 }) 1182 }) 1183 1184 Context("when the cached result is invalid", func() { 1185 It("returns an error", func() { 1186 cacheFile := resultCacheFilePath(cacheDirPath, netConfigList.Name, runtimeConfig.ContainerID) 1187 err := os.MkdirAll(filepath.Dir(cacheFile), 0700) 1188 Expect(err).NotTo(HaveOccurred()) 1189 err = ioutil.WriteFile(cacheFile, []byte("adfadsfasdfasfdsafaf"), 0600) 1190 Expect(err).NotTo(HaveOccurred()) 1191 1192 err = cniConfig.DelNetworkList(netConfigList, runtimeConfig) 1193 Expect(err).To(MatchError("failed to get network 'some-list' cached result: decoding version from network config: invalid character 'a' looking for beginning of value")) 1194 }) 1195 }) 1196 }) 1197 1198 }) 1199 })