github.com/brandond/cni@v0.8.1/pkg/skel/skel_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 skel 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "strings" 22 23 "github.com/containernetworking/cni/pkg/types" 24 "github.com/containernetworking/cni/pkg/types/current" 25 "github.com/containernetworking/cni/pkg/version" 26 27 . "github.com/onsi/ginkgo" 28 . "github.com/onsi/ginkgo/extensions/table" 29 . "github.com/onsi/gomega" 30 ) 31 32 type fakeCmd struct { 33 CallCount int 34 Returns struct { 35 Error error 36 } 37 Received struct { 38 CmdArgs *CmdArgs 39 } 40 } 41 42 func (c *fakeCmd) Func(args *CmdArgs) error { 43 c.CallCount++ 44 c.Received.CmdArgs = args 45 return c.Returns.Error 46 } 47 48 var _ = Describe("dispatching to the correct callback", func() { 49 var ( 50 environment map[string]string 51 stdinData string 52 stdout, stderr *bytes.Buffer 53 cmdAdd, cmdCheck, cmdDel *fakeCmd 54 dispatch *dispatcher 55 expectedCmdArgs *CmdArgs 56 versionInfo version.PluginInfo 57 ) 58 59 BeforeEach(func() { 60 environment = map[string]string{ 61 "CNI_COMMAND": "ADD", 62 "CNI_CONTAINERID": "some-container-id", 63 "CNI_NETNS": "/some/netns/path", 64 "CNI_IFNAME": "eth0", 65 "CNI_ARGS": "some;extra;args", 66 "CNI_PATH": "/some/cni/path", 67 } 68 69 stdinData = `{ "name":"skel-test", "some": "config", "cniVersion": "9.8.7" }` 70 stdout = &bytes.Buffer{} 71 stderr = &bytes.Buffer{} 72 versionInfo = version.PluginSupports("9.8.7") 73 dispatch = &dispatcher{ 74 Getenv: func(key string) string { return environment[key] }, 75 Stdin: strings.NewReader(stdinData), 76 Stdout: stdout, 77 Stderr: stderr, 78 } 79 cmdAdd = &fakeCmd{} 80 cmdCheck = &fakeCmd{} 81 cmdDel = &fakeCmd{} 82 expectedCmdArgs = &CmdArgs{ 83 ContainerID: "some-container-id", 84 Netns: "/some/netns/path", 85 IfName: "eth0", 86 Args: "some;extra;args", 87 Path: "/some/cni/path", 88 StdinData: []byte(stdinData), 89 } 90 }) 91 92 var envVarChecker = func(envVar string, isRequired bool) { 93 delete(environment, envVar) 94 95 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 96 if isRequired { 97 Expect(err).To(Equal(&types.Error{ 98 Code: types.ErrInvalidEnvironmentVariables, 99 Msg: "required env variables [" + envVar + "] missing", 100 })) 101 } else { 102 Expect(err).NotTo(HaveOccurred()) 103 } 104 } 105 106 Context("when the CNI_COMMAND is ADD", func() { 107 It("extracts env vars and stdin data and calls cmdAdd", func() { 108 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 109 110 Expect(err).NotTo(HaveOccurred()) 111 Expect(cmdAdd.CallCount).To(Equal(1)) 112 Expect(cmdCheck.CallCount).To(Equal(0)) 113 Expect(cmdDel.CallCount).To(Equal(0)) 114 Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs)) 115 }) 116 117 It("returns an error when containerID has invalid characters", func() { 118 environment["CNI_CONTAINERID"] = "some-%%container-id" 119 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 120 Expect(err).To(HaveOccurred()) 121 Expect(err).To(Equal(&types.Error{ 122 Code: types.ErrInvalidEnvironmentVariables, 123 Msg: "invalid characters in containerID", 124 Details: "some-%%container-id", 125 })) 126 }) 127 128 Context("return errors when interface name is invalid", func() { 129 It("interface name is too long", func() { 130 environment["CNI_IFNAME"] = "1234567890123456" 131 132 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 133 Expect(err).To(HaveOccurred()) 134 Expect(err).To(Equal(&types.Error{ 135 Code: types.ErrInvalidEnvironmentVariables, 136 Msg: "interface name is too long", 137 Details: "interface name should be less than 16 characters", 138 })) 139 }) 140 141 It("interface name is .", func() { 142 environment["CNI_IFNAME"] = "." 143 144 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 145 Expect(err).To(HaveOccurred()) 146 Expect(err).To(Equal(&types.Error{ 147 Code: types.ErrInvalidEnvironmentVariables, 148 Msg: "interface name is . or ..", 149 Details: "", 150 })) 151 }) 152 153 It("interface name is ..", func() { 154 environment["CNI_IFNAME"] = ".." 155 156 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 157 Expect(err).To(HaveOccurred()) 158 Expect(err).To(Equal(&types.Error{ 159 Code: types.ErrInvalidEnvironmentVariables, 160 Msg: "interface name is . or ..", 161 Details: "", 162 })) 163 }) 164 165 It("interface name contains invalid characters /", func() { 166 environment["CNI_IFNAME"] = "test/test" 167 168 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 169 Expect(err).To(HaveOccurred()) 170 Expect(err).To(Equal(&types.Error{ 171 Code: types.ErrInvalidEnvironmentVariables, 172 Msg: "interface name contains / or : or whitespace characters", 173 Details: "", 174 })) 175 }) 176 177 It("interface name contains invalid characters :", func() { 178 environment["CNI_IFNAME"] = "test:test" 179 180 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 181 Expect(err).To(HaveOccurred()) 182 Expect(err).To(Equal(&types.Error{ 183 Code: types.ErrInvalidEnvironmentVariables, 184 Msg: "interface name contains / or : or whitespace characters", 185 Details: "", 186 })) 187 }) 188 189 It("interface name contains invalid characters whitespace", func() { 190 environment["CNI_IFNAME"] = "test test" 191 192 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 193 Expect(err).To(HaveOccurred()) 194 Expect(err).To(Equal(&types.Error{ 195 Code: types.ErrInvalidEnvironmentVariables, 196 Msg: "interface name contains / or : or whitespace characters", 197 Details: "", 198 })) 199 }) 200 }) 201 202 It("does not call cmdCheck or cmdDel", func() { 203 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 204 205 Expect(err).NotTo(HaveOccurred()) 206 Expect(cmdCheck.CallCount).To(Equal(0)) 207 Expect(cmdDel.CallCount).To(Equal(0)) 208 }) 209 210 DescribeTable("required / optional env vars", envVarChecker, 211 Entry("command", "CNI_COMMAND", true), 212 Entry("container id", "CNI_CONTAINERID", true), 213 Entry("net ns", "CNI_NETNS", true), 214 Entry("if name", "CNI_IFNAME", true), 215 Entry("args", "CNI_ARGS", false), 216 Entry("path", "CNI_PATH", true), 217 ) 218 219 Context("when multiple required env vars are missing", func() { 220 BeforeEach(func() { 221 delete(environment, "CNI_NETNS") 222 delete(environment, "CNI_IFNAME") 223 delete(environment, "CNI_PATH") 224 }) 225 226 It("reports that all of them are missing, not just the first", func() { 227 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 228 Expect(err).To(HaveOccurred()) 229 230 Expect(err).To(Equal(&types.Error{ 231 Code: types.ErrInvalidEnvironmentVariables, 232 Msg: "required env variables [CNI_NETNS,CNI_IFNAME,CNI_PATH] missing", 233 })) 234 }) 235 }) 236 237 Context("when the stdin data is missing the required cniVersion config", func() { 238 BeforeEach(func() { 239 dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "some": "config" }`) 240 }) 241 242 Context("when the plugin supports version 0.1.0", func() { 243 BeforeEach(func() { 244 versionInfo = version.PluginSupports("0.1.0") 245 expectedCmdArgs.StdinData = []byte(`{ "name": "skel-test", "some": "config" }`) 246 }) 247 248 It("infers the config is 0.1.0 and calls the cmdAdd callback", func() { 249 250 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 251 Expect(err).NotTo(HaveOccurred()) 252 253 Expect(cmdAdd.CallCount).To(Equal(1)) 254 Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs)) 255 }) 256 }) 257 258 Context("when the plugin does not support 0.1.0", func() { 259 BeforeEach(func() { 260 versionInfo = version.PluginSupports("4.3.2") 261 }) 262 263 It("immediately returns a useful error", func() { 264 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 265 Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes 266 Expect(err.Msg).To(Equal("incompatible CNI versions")) 267 Expect(err.Details).To(Equal(`config is "0.1.0", plugin supports ["4.3.2"]`)) 268 }) 269 270 It("does not call either callback", func() { 271 dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 272 Expect(cmdAdd.CallCount).To(Equal(0)) 273 Expect(cmdCheck.CallCount).To(Equal(0)) 274 Expect(cmdDel.CallCount).To(Equal(0)) 275 }) 276 }) 277 }) 278 }) 279 280 Context("when the CNI_COMMAND is CHECK", func() { 281 BeforeEach(func() { 282 environment["CNI_COMMAND"] = "CHECK" 283 }) 284 285 It("extracts env vars and stdin data and calls cmdCheck", func() { 286 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 287 288 Expect(err).NotTo(HaveOccurred()) 289 Expect(cmdAdd.CallCount).To(Equal(0)) 290 Expect(cmdCheck.CallCount).To(Equal(1)) 291 Expect(cmdDel.CallCount).To(Equal(0)) 292 Expect(cmdCheck.Received.CmdArgs).To(Equal(expectedCmdArgs)) 293 }) 294 295 It("does not call cmdAdd or cmdDel", func() { 296 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 297 298 Expect(err).NotTo(HaveOccurred()) 299 Expect(cmdAdd.CallCount).To(Equal(0)) 300 Expect(cmdDel.CallCount).To(Equal(0)) 301 }) 302 303 DescribeTable("required / optional env vars", envVarChecker, 304 Entry("command", "CNI_COMMAND", true), 305 Entry("container id", "CNI_CONTAINERID", true), 306 Entry("net ns", "CNI_NETNS", true), 307 Entry("if name", "CNI_IFNAME", true), 308 Entry("args", "CNI_ARGS", false), 309 Entry("path", "CNI_PATH", true), 310 ) 311 312 Context("when multiple required env vars are missing", func() { 313 BeforeEach(func() { 314 delete(environment, "CNI_NETNS") 315 delete(environment, "CNI_IFNAME") 316 delete(environment, "CNI_PATH") 317 }) 318 319 It("reports that all of them are missing, not just the first", func() { 320 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 321 Expect(err).To(HaveOccurred()) 322 323 Expect(err).To(Equal(&types.Error{ 324 Code: types.ErrInvalidEnvironmentVariables, 325 Msg: "required env variables [CNI_NETNS,CNI_IFNAME,CNI_PATH] missing", 326 })) 327 }) 328 }) 329 330 Context("when cniVersion is less than 0.4.0", func() { 331 It("immediately returns a useful error", func() { 332 dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "0.3.0", "some": "config" }`) 333 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 334 Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes 335 Expect(err.Msg).To(Equal("config version does not allow CHECK")) 336 Expect(cmdAdd.CallCount).To(Equal(0)) 337 Expect(cmdCheck.CallCount).To(Equal(0)) 338 Expect(cmdDel.CallCount).To(Equal(0)) 339 }) 340 }) 341 342 Context("when plugin does not support 0.4.0", func() { 343 It("immediately returns a useful error", func() { 344 dispatch.Stdin = strings.NewReader(`{ "name": "skel-test", "cniVersion": "0.4.0", "some": "config" }`) 345 versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0") 346 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 347 Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes 348 Expect(err.Msg).To(Equal("plugin version does not allow CHECK")) 349 Expect(cmdAdd.CallCount).To(Equal(0)) 350 Expect(cmdCheck.CallCount).To(Equal(0)) 351 Expect(cmdDel.CallCount).To(Equal(0)) 352 }) 353 }) 354 355 Context("when the config has a bad version", func() { 356 It("immediately returns a useful error", func() { 357 dispatch.Stdin = strings.NewReader(`{ "cniVersion": "adsfsadf", "some": "config", "name": "test" }`) 358 versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0") 359 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 360 Expect(err.Code).To(Equal(uint(types.ErrDecodingFailure))) 361 Expect(cmdAdd.CallCount).To(Equal(0)) 362 Expect(cmdCheck.CallCount).To(Equal(0)) 363 Expect(cmdDel.CallCount).To(Equal(0)) 364 }) 365 }) 366 367 Context("when the config has a bad name", func() { 368 It("immediately returns invalid network config", func() { 369 dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "te%%st" }`) 370 versionInfo = version.PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.4.0") 371 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 372 Expect(err.Code).To(Equal(uint(types.ErrInvalidNetworkConfig))) 373 Expect(cmdAdd.CallCount).To(Equal(0)) 374 Expect(cmdCheck.CallCount).To(Equal(0)) 375 Expect(cmdDel.CallCount).To(Equal(0)) 376 }) 377 }) 378 379 Context("when the plugin has a bad version", func() { 380 It("immediately returns a useful error", func() { 381 dispatch.Stdin = strings.NewReader(`{ "cniVersion": "0.4.0", "some": "config", "name": "test" }`) 382 versionInfo = version.PluginSupports("0.1.0", "0.2.0", "adsfasdf") 383 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 384 Expect(err.Code).To(Equal(uint(types.ErrDecodingFailure))) 385 Expect(cmdAdd.CallCount).To(Equal(0)) 386 Expect(cmdCheck.CallCount).To(Equal(0)) 387 Expect(cmdDel.CallCount).To(Equal(0)) 388 }) 389 }) 390 }) 391 392 Context("when the CNI_COMMAND is DEL", func() { 393 BeforeEach(func() { 394 environment["CNI_COMMAND"] = "DEL" 395 }) 396 397 It("calls cmdDel with the env vars and stdin data", func() { 398 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 399 400 Expect(err).NotTo(HaveOccurred()) 401 Expect(cmdDel.CallCount).To(Equal(1)) 402 Expect(cmdDel.Received.CmdArgs).To(Equal(expectedCmdArgs)) 403 }) 404 405 It("does not call cmdAdd", func() { 406 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 407 408 Expect(err).NotTo(HaveOccurred()) 409 Expect(cmdAdd.CallCount).To(Equal(0)) 410 }) 411 412 DescribeTable("required / optional env vars", envVarChecker, 413 Entry("command", "CNI_COMMAND", true), 414 Entry("container id", "CNI_CONTAINERID", true), 415 Entry("net ns", "CNI_NETNS", false), 416 Entry("if name", "CNI_IFNAME", true), 417 Entry("args", "CNI_ARGS", false), 418 Entry("path", "CNI_PATH", true), 419 ) 420 }) 421 422 Context("when the CNI_COMMAND is VERSION", func() { 423 BeforeEach(func() { 424 environment["CNI_COMMAND"] = "VERSION" 425 }) 426 427 It("prints the version to stdout", func() { 428 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 429 430 Expect(err).NotTo(HaveOccurred()) 431 Expect(stdout).To(MatchJSON(fmt.Sprintf(`{ 432 "cniVersion": "%s", 433 "supportedVersions": ["9.8.7"] 434 }`, current.ImplementedSpecVersion))) 435 }) 436 437 It("does not call cmdAdd or cmdDel", func() { 438 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 439 440 Expect(err).NotTo(HaveOccurred()) 441 Expect(cmdAdd.CallCount).To(Equal(0)) 442 Expect(cmdDel.CallCount).To(Equal(0)) 443 }) 444 445 DescribeTable("VERSION does not need the usual env vars", envVarChecker, 446 Entry("command", "CNI_COMMAND", true), 447 Entry("container id", "CNI_CONTAINERID", false), 448 Entry("net ns", "CNI_NETNS", false), 449 Entry("if name", "CNI_IFNAME", false), 450 Entry("args", "CNI_ARGS", false), 451 Entry("path", "CNI_PATH", false), 452 ) 453 454 It("does not read from Stdin", func() { 455 r := &BadReader{} 456 dispatch.Stdin = r 457 458 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 459 460 Expect(err).NotTo(HaveOccurred()) 461 Expect(r.ReadCount).To(Equal(0)) 462 Expect(stdout).To(MatchJSON(fmt.Sprintf(`{ 463 "cniVersion": "%s", 464 "supportedVersions": ["9.8.7"] 465 }`, current.ImplementedSpecVersion))) 466 }) 467 }) 468 469 Context("when the CNI_COMMAND is unrecognized", func() { 470 BeforeEach(func() { 471 environment["CNI_COMMAND"] = "NOPE" 472 }) 473 474 It("does not call any cmd callback", func() { 475 dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 476 477 Expect(cmdAdd.CallCount).To(Equal(0)) 478 Expect(cmdDel.CallCount).To(Equal(0)) 479 }) 480 481 It("returns an error", func() { 482 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 483 484 Expect(err).To(Equal(&types.Error{ 485 Code: types.ErrInvalidEnvironmentVariables, 486 Msg: "unknown CNI_COMMAND: NOPE", 487 })) 488 }) 489 490 It("prints the about string when the command is blank", func() { 491 environment["CNI_COMMAND"] = "" 492 dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "test framework v42") 493 Expect(stderr.String()).To(ContainSubstring("test framework v42")) 494 }) 495 }) 496 497 Context("when the CNI_COMMAND is missing", func() { 498 It("prints the about string to stderr", func() { 499 environment = map[string]string{} 500 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "AWESOME PLUGIN") 501 Expect(err).NotTo(HaveOccurred()) 502 503 Expect(cmdAdd.CallCount).To(Equal(0)) 504 Expect(cmdDel.CallCount).To(Equal(0)) 505 log := stderr.String() 506 Expect(log).To(Equal("AWESOME PLUGIN\n")) 507 }) 508 509 It("fails if there is no about string", func() { 510 environment = map[string]string{} 511 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 512 Expect(err).To(HaveOccurred()) 513 514 Expect(cmdAdd.CallCount).To(Equal(0)) 515 Expect(cmdDel.CallCount).To(Equal(0)) 516 Expect(err).To(Equal(&types.Error{ 517 Code: types.ErrInvalidEnvironmentVariables, 518 Msg: "required env variables [CNI_COMMAND] missing", 519 })) 520 }) 521 }) 522 523 Context("when stdin cannot be read", func() { 524 BeforeEach(func() { 525 dispatch.Stdin = &BadReader{} 526 }) 527 528 It("does not call any cmd callback", func() { 529 dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 530 531 Expect(cmdAdd.CallCount).To(Equal(0)) 532 Expect(cmdDel.CallCount).To(Equal(0)) 533 }) 534 535 It("wraps and returns the error", func() { 536 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 537 538 Expect(err).To(Equal(&types.Error{ 539 Code: types.ErrIOFailure, 540 Msg: "error reading from stdin: banana", 541 })) 542 }) 543 }) 544 545 Context("when the callback returns an error", func() { 546 Context("when it is a typed Error", func() { 547 BeforeEach(func() { 548 cmdAdd.Returns.Error = &types.Error{ 549 Code: 1234, 550 Msg: "insufficient something", 551 } 552 }) 553 554 It("returns the error as-is", func() { 555 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 556 557 Expect(err).To(Equal(&types.Error{ 558 Code: 1234, 559 Msg: "insufficient something", 560 })) 561 }) 562 }) 563 564 Context("when it is an unknown error", func() { 565 BeforeEach(func() { 566 cmdAdd.Returns.Error = errors.New("potato") 567 }) 568 569 It("wraps and returns the error", func() { 570 err := dispatch.pluginMain(cmdAdd.Func, cmdCheck.Func, cmdDel.Func, versionInfo, "") 571 572 Expect(err).To(Equal(&types.Error{ 573 Code: types.ErrInternal, 574 Msg: "potato", 575 })) 576 }) 577 }) 578 }) 579 }) 580 581 // BadReader is an io.Reader which always errors 582 type BadReader struct { 583 Error error 584 ReadCount int 585 } 586 587 func (r *BadReader) Read(buffer []byte) (int, error) { 588 r.ReadCount++ 589 if r.Error != nil { 590 return 0, r.Error 591 } 592 return 0, errors.New("banana") 593 } 594 595 func (r *BadReader) Close() error { 596 return nil 597 }