github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/integration/v7/plugin/install_plugin_command_test.go (about) 1 package plugin 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "net/http" 9 "os" 10 "path/filepath" 11 12 "code.cloudfoundry.org/cli/integration/helpers" 13 "code.cloudfoundry.org/cli/util/generic" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 . "github.com/onsi/gomega/gbytes" 17 . "github.com/onsi/gomega/gexec" 18 . "github.com/onsi/gomega/ghttp" 19 ) 20 21 var _ = Describe("install-plugin command", func() { 22 var ( 23 buffer *Buffer 24 pluginPath string 25 v6PluginPath string 26 ) 27 28 AfterEach(func() { 29 pluginsHomeDirContents, err := ioutil.ReadDir(filepath.Join(homeDir, ".cf", "plugins")) 30 if os.IsNotExist(err) { 31 return 32 } 33 34 Expect(err).ToNot(HaveOccurred()) 35 36 for _, entry := range pluginsHomeDirContents { 37 Expect(entry.Name()).NotTo(ContainSubstring("temp")) 38 } 39 }) 40 41 Describe("help", func() { 42 When("the --help flag is given", func() { 43 It("displays command usage to stdout", func() { 44 session := helpers.CF("install-plugin", "--help") 45 46 Eventually(session).Should(Say("NAME:")) 47 Eventually(session).Should(Say("install-plugin - Install CLI plugin")) 48 Eventually(session).Should(Say("USAGE:")) 49 Eventually(session).Should(Say(`cf install-plugin PLUGIN_NAME \[-r REPO_NAME\] \[-f\]`)) 50 Eventually(session).Should(Say(`cf install-plugin LOCAL-PATH/TO/PLUGIN | URL \[-f\]`)) 51 Eventually(session).Should(Say("")) 52 Eventually(session).Should(Say("WARNING:")) 53 Eventually(session).Should(Say("Plugins are binaries written by potentially untrusted authors.")) 54 Eventually(session).Should(Say("Install and use plugins at your own risk.")) 55 Eventually(session).Should(Say("")) 56 Eventually(session).Should(Say("EXAMPLES:")) 57 Eventually(session).Should(Say("cf install-plugin ~/Downloads/plugin-foobar")) 58 Eventually(session).Should(Say("cf install-plugin https://example.com/plugin-foobar_linux_amd64")) 59 Eventually(session).Should(Say("cf install-plugin -r My-Repo plugin-echo")) 60 Eventually(session).Should(Say("OPTIONS:")) 61 Eventually(session).Should(Say(`-f\s+Force install of plugin without confirmation`)) 62 Eventually(session).Should(Say(`-r\s+Restrict search for plugin to this registered repository`)) 63 Eventually(session).Should(Say("SEE ALSO:")) 64 Eventually(session).Should(Say("add-plugin-repo, list-plugin-repos, plugins")) 65 66 Eventually(session).Should(Exit(0)) 67 }) 68 }) 69 }) 70 71 When("the user does not provide a plugin name or location", func() { 72 It("errors and displays usage", func() { 73 session := helpers.CF("install-plugin") 74 Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `PLUGIN_NAME_OR_LOCATION` was not provided")) 75 Eventually(session).Should(Say("USAGE:")) 76 77 Eventually(session).Should(Exit(1)) 78 }) 79 }) 80 81 When("the plugin dir does not exist", func() { 82 var ( 83 newPluginHome string 84 85 cfPluginHome string 86 ) 87 88 BeforeEach(func() { 89 cfPluginHome = os.Getenv("CF_PLUGIN_HOME") 90 91 var err error 92 newPluginHome, err = ioutil.TempDir("", "plugin-temp-dir") 93 Expect(err).ToNot(HaveOccurred()) 94 Expect(os.RemoveAll(newPluginHome)) 95 96 Expect(os.Setenv("CF_PLUGIN_HOME", newPluginHome)).ToNot(HaveOccurred()) 97 98 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin_v7", "some-plugin", "1.0.0", 99 []helpers.PluginCommand{ 100 {Name: "some-command", Help: "some-command-help"}, 101 }, 102 ) 103 }) 104 105 AfterEach(func() { 106 Expect(os.RemoveAll(newPluginHome)).ToNot(HaveOccurred()) 107 Expect(os.Setenv("CF_PLUGIN_HOME", cfPluginHome)).ToNot(HaveOccurred()) 108 }) 109 110 It("creates the new directory, and continues as normal", func() { 111 session := helpers.CF("install-plugin", pluginPath, "-f") 112 Eventually(session).Should(Exit(0)) 113 114 log.Println(newPluginHome) 115 _, err := os.Stat(newPluginHome) 116 Expect(os.IsNotExist(err)).To(Equal(false)) 117 }) 118 }) 119 120 Describe("installing a plugin from a local file", func() { 121 When("the file is compiled for a different os and architecture", func() { 122 BeforeEach(func() { 123 goos := os.Getenv("GOOS") 124 goarch := os.Getenv("GOARCH") 125 126 err := os.Setenv("GOOS", "openbsd") 127 Expect(err).ToNot(HaveOccurred()) 128 err = os.Setenv("GOARCH", "amd64") 129 Expect(err).ToNot(HaveOccurred()) 130 131 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin_v7", "some-plugin", "1.0.0", 132 []helpers.PluginCommand{ 133 {Name: "some-command", Help: "some-command-help"}, 134 }, 135 ) 136 137 err = os.Setenv("GOOS", goos) 138 Expect(err).ToNot(HaveOccurred()) 139 err = os.Setenv("GOARCH", goarch) 140 Expect(err).ToNot(HaveOccurred()) 141 }) 142 143 It("fails and reports the file is not a valid CLI plugin", func() { 144 session := helpers.CF("install-plugin", pluginPath, "-f") 145 146 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 147 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 148 Eventually(session).Should(Say("FAILED")) 149 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 150 151 Eventually(session).Should(Exit(1)) 152 }) 153 }) 154 155 When("the file is compiled for the correct os and architecture", func() { 156 BeforeEach(func() { 157 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin_v7", "some-plugin", "1.0.0", 158 []helpers.PluginCommand{ 159 {Name: "some-command", Help: "some-command-help"}, 160 }, 161 ) 162 }) 163 164 When("the plugin is a v6 plugin", func() { 165 BeforeEach(func() { 166 v6PluginPath = helpers.BuildConfigurablePlugin("configurable_plugin", "some-v6-plugin", "1.0.0", 167 []helpers.PluginCommand{ 168 {Name: "some-v6-command", Help: "some-command-help"}, 169 }, 170 ) 171 }) 172 173 It("rejects the non v7 plugin", func() { 174 session := helpers.CF("install-plugin", v6PluginPath, "-f") 175 Eventually(session).Should(Exit(1), "Expected install-plugin to fail, but it succeeded.") 176 Expect(session.Err).To(Say("This plugin is not compatible with this version of the CLI")) 177 }) 178 }) 179 180 When("the -f flag is given", func() { 181 It("installs the plugin and cleans up all temp files", func() { 182 session := helpers.CF("install-plugin", pluginPath, "-f") 183 184 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 185 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 186 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 187 Eventually(session).Should(Say("OK")) 188 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 189 190 Eventually(session).Should(Exit(0)) 191 192 installedPath := generic.ExecutableFilename(filepath.Join(homeDir, ".cf", "plugins", "some-plugin")) 193 194 pluginsSession := helpers.CF("plugins", "--checksum") 195 expectedSha := helpers.Sha1Sum(installedPath) 196 197 Eventually(pluginsSession).Should(Say(`some-plugin\s+1\.0\.0\s+%s`, expectedSha)) 198 Eventually(pluginsSession).Should(Exit(0)) 199 200 Eventually(helpers.CF("some-command")).Should(Exit(0)) 201 202 helpSession := helpers.CF("help") 203 Eventually(helpSession).Should(Say("some-command")) 204 Eventually(helpSession).Should(Exit(0)) 205 }) 206 207 When("the file does not have executable permissions", func() { 208 BeforeEach(func() { 209 Expect(os.Chmod(pluginPath, 0666)).ToNot(HaveOccurred()) 210 }) 211 212 It("installs the plugin", func() { 213 session := helpers.CF("install-plugin", pluginPath, "-f") 214 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 215 Eventually(session).Should(Exit(0)) 216 }) 217 }) 218 219 When("the plugin is already installed", func() { 220 BeforeEach(func() { 221 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 222 }) 223 224 It("uninstalls the existing plugin and installs the plugin", func() { 225 session := helpers.CF("install-plugin", pluginPath, "-f") 226 227 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 is already installed\. Uninstalling existing plugin\.\.\.`)) 228 Eventually(session).Should(Say("CLI-MESSAGE-UNINSTALL")) 229 Eventually(session).Should(Say(`Plugin some-plugin successfully uninstalled\.`)) 230 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 231 232 Eventually(session).Should(Exit(0)) 233 }) 234 }) 235 236 When("the file does not exist", func() { 237 It("tells the user that the file was not found and fails", func() { 238 session := helpers.CF("install-plugin", "some/path/that/does/not/exist", "-f") 239 Eventually(session.Err).Should(Say(`Plugin some/path/that/does/not/exist not found on disk or in any registered repo\.`)) 240 Eventually(session.Err).Should(Say(`Use 'cf repo-plugins' to list plugins available in the repos\.`)) 241 242 Consistently(session).ShouldNot(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 243 Consistently(session).ShouldNot(Say(`Install and use plugins at your own risk\.`)) 244 245 Eventually(session).Should(Exit(1)) 246 }) 247 }) 248 249 When("the file is not an executable", func() { 250 BeforeEach(func() { 251 badPlugin, err := ioutil.TempFile("", "") 252 Expect(err).ToNot(HaveOccurred()) 253 pluginPath = badPlugin.Name() 254 err = badPlugin.Close() 255 Expect(err).ToNot(HaveOccurred()) 256 }) 257 258 AfterEach(func() { 259 err := os.Remove(pluginPath) 260 Expect(err).ToNot(HaveOccurred()) 261 }) 262 263 It("tells the user that the file is not a plugin and fails", func() { 264 session := helpers.CF("install-plugin", pluginPath, "-f") 265 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 266 267 Eventually(session).Should(Exit(1)) 268 }) 269 }) 270 271 When("the file is not a plugin", func() { 272 BeforeEach(func() { 273 var err error 274 pluginPath, err = Build("code.cloudfoundry.org/cli/integration/assets/non_plugin") 275 Expect(err).ToNot(HaveOccurred()) 276 }) 277 278 It("tells the user that the file is not a plugin and fails", func() { 279 session := helpers.CF("install-plugin", pluginPath, "-f") 280 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 281 282 Eventually(session).Should(Exit(1)) 283 }) 284 }) 285 286 When("getting metadata from the plugin errors", func() { 287 BeforeEach(func() { 288 var err error 289 pluginPath, err = Build("code.cloudfoundry.org/cli/integration/assets/test_plugin_fails_metadata") 290 Expect(err).ToNot(HaveOccurred()) 291 }) 292 293 It("displays the error to stderr", func() { 294 session := helpers.CF("install-plugin", pluginPath, "-f") 295 Eventually(session.Err).Should(Say("exit status 51")) 296 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 297 298 Eventually(session).Should(Exit(1)) 299 }) 300 }) 301 302 When("there is a command conflict", func() { 303 When("the plugin has a command that is the same as a built-in command", func() { 304 BeforeEach(func() { 305 pluginPath = helpers.BuildConfigurablePlugin( 306 "configurable_plugin_v7", "some-plugin", "1.1.1", 307 []helpers.PluginCommand{ 308 {Name: "version"}, 309 }) 310 }) 311 312 It("tells the user about the conflict and fails", func() { 313 session := helpers.CF("install-plugin", "-f", pluginPath) 314 315 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 316 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 317 318 Eventually(session).Should(Say("FAILED")) 319 Eventually(session.Err).Should(Say(`Plugin some-plugin v1\.1\.1 could not be installed as it contains commands with names that are already used: version`)) 320 321 Eventually(session).Should(Exit(1)) 322 }) 323 }) 324 325 When("the plugin has a command that is the same as a built-in alias", func() { 326 BeforeEach(func() { 327 pluginPath = helpers.BuildConfigurablePlugin( 328 "configurable_plugin_v7", "some-plugin", "1.1.1", 329 []helpers.PluginCommand{ 330 {Name: "cups"}, 331 }) 332 }) 333 334 It("tells the user about the conflict and fails", func() { 335 session := helpers.CF("install-plugin", "-f", pluginPath) 336 337 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 338 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 339 340 Eventually(session).Should(Say("FAILED")) 341 Eventually(session.Err).Should(Say(`Plugin some-plugin v1\.1\.1 could not be installed as it contains commands with names that are already used: cups`)) 342 343 Eventually(session).Should(Exit(1)) 344 }) 345 }) 346 347 When("the plugin has a command that is the same as another plugin command", func() { 348 BeforeEach(func() { 349 helpers.InstallConfigurablePlugin("configurable_plugin_v7", "existing-plugin", "1.1.1", 350 []helpers.PluginCommand{ 351 {Name: "existing-command"}, 352 }) 353 354 pluginPath = helpers.BuildConfigurablePlugin( 355 "configurable_plugin_v7", "new-plugin", "1.1.1", 356 []helpers.PluginCommand{ 357 {Name: "existing-command"}, 358 }) 359 }) 360 361 It("tells the user about the conflict and fails", func() { 362 session := helpers.CF("install-plugin", "-f", pluginPath) 363 364 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 365 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 366 367 Eventually(session).Should(Say("FAILED")) 368 Eventually(session.Err).Should(Say(`Plugin new-plugin v1\.1\.1 could not be installed as it contains commands with names that are already used: existing-command\.`)) 369 370 Eventually(session).Should(Exit(1)) 371 }) 372 }) 373 374 When("the plugin has a command that is the same as another plugin alias", func() { 375 BeforeEach(func() { 376 helpers.InstallConfigurablePlugin("configurable_plugin_v7", "existing-plugin", "1.1.1", 377 []helpers.PluginCommand{ 378 {Name: "existing-command"}, 379 }) 380 381 pluginPath = helpers.BuildConfigurablePlugin( 382 "configurable_plugin_v7", "new-plugin", "1.1.1", 383 []helpers.PluginCommand{ 384 {Name: "new-command", Alias: "existing-command"}, 385 }) 386 }) 387 388 It("tells the user about the conflict and fails", func() { 389 session := helpers.CF("install-plugin", "-f", pluginPath) 390 391 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 392 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 393 394 Eventually(session).Should(Say("FAILED")) 395 Eventually(session.Err).Should(Say(`Plugin new-plugin v1\.1\.1 could not be installed as it contains commands with aliases that are already used: existing-command\.`)) 396 397 Eventually(session).Should(Exit(1)) 398 }) 399 }) 400 }) 401 402 Context("alias conflict", func() { 403 When("the plugin has an alias that is the same as a built-in command", func() { 404 405 BeforeEach(func() { 406 pluginPath = helpers.BuildConfigurablePlugin( 407 "configurable_plugin_v7", "some-plugin", "1.1.1", 408 []helpers.PluginCommand{ 409 {Name: "some-command", Alias: "version"}, 410 }) 411 }) 412 413 It("tells the user about the conflict and fails", func() { 414 session := helpers.CF("install-plugin", "-f", pluginPath) 415 416 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 417 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 418 419 Eventually(session).Should(Say("FAILED")) 420 Eventually(session.Err).Should(Say(`Plugin some-plugin v1\.1\.1 could not be installed as it contains commands with aliases that are already used: version`)) 421 422 Eventually(session).Should(Exit(1)) 423 }) 424 }) 425 426 When("the plugin has an alias that is the same as a built-in alias", func() { 427 BeforeEach(func() { 428 pluginPath = helpers.BuildConfigurablePlugin( 429 "configurable_plugin_v7", "some-plugin", "1.1.1", 430 []helpers.PluginCommand{ 431 {Name: "some-command", Alias: "cups"}, 432 }) 433 }) 434 435 It("tells the user about the conflict and fails", func() { 436 session := helpers.CF("install-plugin", "-f", pluginPath) 437 438 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 439 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 440 441 Eventually(session).Should(Say("FAILED")) 442 Eventually(session.Err).Should(Say(`Plugin some-plugin v1\.1\.1 could not be installed as it contains commands with aliases that are already used: cups`)) 443 444 Eventually(session).Should(Exit(1)) 445 }) 446 }) 447 448 When("the plugin has an alias that is the same as another plugin command", func() { 449 BeforeEach(func() { 450 helpers.InstallConfigurablePlugin("configurable_plugin_v7", "existing-plugin", "1.1.1", 451 []helpers.PluginCommand{ 452 {Name: "existing-command"}, 453 }) 454 455 pluginPath = helpers.BuildConfigurablePlugin( 456 "configurable_plugin_v7", "new-plugin", "1.1.1", 457 []helpers.PluginCommand{ 458 {Name: "new-command", Alias: "existing-command"}, 459 }) 460 }) 461 462 It("tells the user about the conflict and fails", func() { 463 session := helpers.CF("install-plugin", "-f", pluginPath) 464 465 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 466 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 467 468 Eventually(session).Should(Say("FAILED")) 469 Eventually(session.Err).Should(Say(`Plugin new-plugin v1\.1\.1 could not be installed as it contains commands with aliases that are already used: existing-command\.`)) 470 471 Eventually(session).Should(Exit(1)) 472 }) 473 }) 474 475 When("the plugin has an alias that is the same as another plugin alias", func() { 476 BeforeEach(func() { 477 helpers.InstallConfigurablePlugin("configurable_plugin_v7", "existing-plugin", "1.1.1", 478 []helpers.PluginCommand{ 479 {Name: "existing-command", Alias: "existing-alias"}, 480 }) 481 482 pluginPath = helpers.BuildConfigurablePlugin( 483 "configurable_plugin_v7", "new-plugin", "1.1.1", 484 []helpers.PluginCommand{ 485 {Name: "new-command", Alias: "existing-alias"}, 486 }) 487 }) 488 489 It("tells the user about the conflict and fails", func() { 490 session := helpers.CF("install-plugin", "-f", pluginPath) 491 492 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 493 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 494 495 Eventually(session).Should(Say("FAILED")) 496 Eventually(session.Err).Should(Say(`Plugin new-plugin v1\.1\.1 could not be installed as it contains commands with aliases that are already used: existing-alias\.`)) 497 498 Eventually(session).Should(Exit(1)) 499 }) 500 }) 501 }) 502 503 Context("alias and command conflicts", func() { 504 When("the plugin has a command and an alias that are both taken by another plugin", func() { 505 BeforeEach(func() { 506 helpers.InstallConfigurablePlugin("configurable_plugin_v7", "existing-plugin", "1.1.1", 507 []helpers.PluginCommand{ 508 {Name: "existing-command", Alias: "existing-alias"}, 509 }) 510 511 pluginPath = helpers.BuildConfigurablePlugin( 512 "configurable_plugin_v7", "new-plugin", "1.1.1", 513 []helpers.PluginCommand{ 514 {Name: "existing-command", Alias: "existing-alias"}, 515 }) 516 }) 517 518 It("tells the user about the conflict and fails", func() { 519 session := helpers.CF("install-plugin", "-f", pluginPath) 520 521 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 522 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 523 524 Eventually(session).Should(Say("FAILED")) 525 Eventually(session.Err).Should(Say(`Plugin new-plugin v1\.1\.1 could not be installed as it contains commands with names and aliases that are already used: existing-command, existing-alias\.`)) 526 527 Eventually(session).Should(Exit(1)) 528 }) 529 }) 530 }) 531 }) 532 533 When("the -f flag is not given", func() { 534 When("the user says yes", func() { 535 BeforeEach(func() { 536 buffer = NewBuffer() 537 _, err := buffer.Write([]byte("y\n")) 538 Expect(err).ToNot(HaveOccurred()) 539 }) 540 541 It("installs the plugin", func() { 542 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 543 544 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 545 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 546 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: y`, helpers.ConvertPathToRegularExpression(pluginPath))) 547 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 548 Eventually(session).Should(Say("OK")) 549 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 550 551 Eventually(session).Should(Exit(0)) 552 553 pluginsSession := helpers.CF("plugins", "--checksum") 554 expectedSha := helpers.Sha1Sum( 555 generic.ExecutableFilename(filepath.Join(homeDir, ".cf/plugins/some-plugin"))) 556 Eventually(pluginsSession).Should(Say(`some-plugin\s+1.0.0\s+%s`, expectedSha)) 557 Eventually(pluginsSession).Should(Exit(0)) 558 559 Eventually(helpers.CF("some-command")).Should(Exit(0)) 560 561 helpSession := helpers.CF("help") 562 Eventually(helpSession).Should(Say("some-command")) 563 Eventually(helpSession).Should(Exit(0)) 564 }) 565 566 When("the plugin is already installed", func() { 567 BeforeEach(func() { 568 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 569 }) 570 571 It("fails and tells the user how to force a reinstall", func() { 572 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 573 574 Eventually(session).Should(Say("FAILED")) 575 Eventually(session.Err).Should(Say(`Plugin some-plugin 1\.0\.0 could not be installed\. A plugin with that name is already installed\.`)) 576 Eventually(session.Err).Should(Say(`TIP: Use 'cf install-plugin -f' to force a reinstall\.`)) 577 578 Eventually(session).Should(Exit(1)) 579 }) 580 }) 581 }) 582 583 When("the user says no", func() { 584 BeforeEach(func() { 585 buffer = NewBuffer() 586 _, err := buffer.Write([]byte("n\n")) 587 Expect(err).ToNot(HaveOccurred()) 588 }) 589 590 It("does not install the plugin", func() { 591 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 592 593 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 594 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 595 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: n`, helpers.ConvertPathToRegularExpression(pluginPath))) 596 Eventually(session).Should(Say(`Plugin installation cancelled\.`)) 597 598 Eventually(session).Should(Exit(0)) 599 }) 600 601 When("the plugin is already installed", func() { 602 BeforeEach(func() { 603 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 604 }) 605 606 It("does not uninstall the existing plugin", func() { 607 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 608 609 Eventually(session).Should(Say(`Plugin installation cancelled\.`)) 610 611 Consistently(session).ShouldNot(Say(`Plugin some-plugin 1\.0\.0 is already installed\. Uninstalling existing plugin\.\.\.`)) 612 Consistently(session).ShouldNot(Say("CLI-MESSAGE-UNINSTALL")) 613 Consistently(session).ShouldNot(Say(`Plugin some-plugin successfully uninstalled\.`)) 614 615 Eventually(session).Should(Exit(0)) 616 }) 617 }) 618 }) 619 620 When("the user interrupts with control-c", func() { 621 BeforeEach(func() { 622 buffer = NewBuffer() 623 _, err := buffer.Write([]byte("y")) // but not enter 624 Expect(err).ToNot(HaveOccurred()) 625 }) 626 627 It("does not install the plugin and does not create a bad state", func() { 628 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 629 630 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 631 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 632 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]:`, helpers.ConvertPathToRegularExpression(pluginPath))) 633 634 session.Interrupt() 635 636 Eventually(session).Should(Say("FAILED")) 637 638 // There is a timing issue -- the exit code may be either 1 (processed error), 2 (config writing error), or 130 (Ctrl-C) 639 Eventually(session).Should(SatisfyAny(Exit(1), Exit(2), Exit(130))) 640 641 // make sure cf plugins did not break 642 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 643 644 // make sure a retry of the plugin install works 645 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 646 Eventually(retrySession).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 647 Eventually(retrySession).Should(Exit(0)) 648 }) 649 }) 650 }) 651 }) 652 }) 653 654 Describe("installing a plugin from a URL", func() { 655 var ( 656 server *Server 657 ) 658 659 BeforeEach(func() { 660 server = NewTLSServer() 661 // Suppresses ginkgo server logs 662 server.HTTPTestServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 663 }) 664 665 AfterEach(func() { 666 server.Close() 667 }) 668 669 When("a URL and the -f flag are provided", func() { 670 When("an executable is available for download at the URL", func() { 671 var ( 672 pluginData []byte 673 ) 674 675 BeforeEach(func() { 676 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin_v7", "some-plugin", "1.0.0", 677 []helpers.PluginCommand{ 678 {Name: "some-command", Help: "some-command-help"}, 679 }, 680 ) 681 682 var err error 683 pluginData, err = ioutil.ReadFile(pluginPath) 684 Expect(err).ToNot(HaveOccurred()) 685 server.AppendHandlers( 686 CombineHandlers( 687 VerifyRequest(http.MethodGet, "/"), 688 RespondWith(http.StatusOK, pluginData), 689 ), 690 ) 691 }) 692 693 AfterEach(func() { 694 err := os.Remove(pluginPath) 695 Expect(err).ToNot(HaveOccurred()) 696 }) 697 698 It("installs the plugin", func() { 699 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 700 701 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 702 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 703 704 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 705 Eventually(session).Should(Say(`\d.* .*B / ?`)) 706 707 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 708 Eventually(session).Should(Say("OK")) 709 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 710 711 Eventually(session).Should(Exit(0)) 712 }) 713 714 When("the URL redirects", func() { 715 BeforeEach(func() { 716 server.Reset() 717 server.AppendHandlers( 718 CombineHandlers( 719 VerifyRequest(http.MethodGet, "/redirect"), 720 RespondWith(http.StatusMovedPermanently, nil, http.Header{"Location": []string{server.URL()}}), 721 ), 722 CombineHandlers( 723 VerifyRequest(http.MethodGet, "/"), 724 RespondWith(http.StatusOK, pluginData), 725 )) 726 }) 727 728 It("installs the plugin", func() { 729 session := helpers.CF("install-plugin", "-f", fmt.Sprintf("%s/redirect", server.URL()), "-k") 730 731 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 732 Eventually(session).Should(Say("OK")) 733 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 734 735 Eventually(session).Should(Exit(0)) 736 }) 737 }) 738 739 When("the plugin has already been installed", func() { 740 BeforeEach(func() { 741 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 742 }) 743 744 It("uninstalls and reinstalls the plugin", func() { 745 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 746 747 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 748 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 749 750 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 751 Eventually(session).Should(Say(`\d.* .*B / ?`)) 752 753 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 is already installed\. Uninstalling existing plugin\.\.\.`)) 754 Eventually(session).Should(Say("CLI-MESSAGE-UNINSTALL")) 755 Eventually(session).Should(Say(`Plugin some-plugin successfully uninstalled\.`)) 756 Eventually(session).Should(Say("OK")) 757 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 758 759 Eventually(session).Should(Exit(0)) 760 }) 761 }) 762 }) 763 764 When("a 4xx or 5xx HTTP response status is encountered", func() { 765 BeforeEach(func() { 766 server.AppendHandlers( 767 CombineHandlers( 768 VerifyRequest(http.MethodGet, "/"), 769 RespondWith(http.StatusNotFound, nil), 770 ), 771 ) 772 }) 773 774 It("displays an appropriate error", func() { 775 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 776 777 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 778 Eventually(session).Should(Say("FAILED")) 779 Eventually(session.Err).Should(Say("Download attempt failed; server returned 404 Not Found")) 780 Eventually(session.Err).Should(Say(`Unable to install; plugin is not available from the given URL\.`)) 781 782 Eventually(session).Should(Exit(1)) 783 }) 784 }) 785 786 When("the file is not a plugin", func() { 787 BeforeEach(func() { 788 var err error 789 pluginPath, err = Build("code.cloudfoundry.org/cli/integration/assets/non_plugin") 790 Expect(err).ToNot(HaveOccurred()) 791 792 pluginData, err := ioutil.ReadFile(pluginPath) 793 Expect(err).ToNot(HaveOccurred()) 794 server.AppendHandlers( 795 CombineHandlers( 796 VerifyRequest(http.MethodGet, "/"), 797 RespondWith(http.StatusOK, pluginData), 798 ), 799 ) 800 }) 801 802 AfterEach(func() { 803 err := os.Remove(pluginPath) 804 Expect(err).ToNot(HaveOccurred()) 805 }) 806 807 It("tells the user that the file is not a plugin and fails", func() { 808 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 809 810 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 811 Eventually(session).Should(Say("FAILED")) 812 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 813 814 Eventually(session).Should(Exit(1)) 815 }) 816 }) 817 }) 818 819 When("the -f flag is not provided", func() { 820 var ( 821 pluginData []byte 822 ) 823 824 BeforeEach(func() { 825 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin_v7", "some-plugin", "1.0.0", 826 []helpers.PluginCommand{ 827 {Name: "some-command", Help: "some-command-help"}, 828 }, 829 ) 830 831 var err error 832 pluginData, err = ioutil.ReadFile(pluginPath) 833 Expect(err).ToNot(HaveOccurred()) 834 server.AppendHandlers( 835 CombineHandlers( 836 VerifyRequest(http.MethodGet, "/"), 837 RespondWith(http.StatusOK, pluginData), 838 ), 839 ) 840 }) 841 842 AfterEach(func() { 843 err := os.Remove(pluginPath) 844 Expect(err).ToNot(HaveOccurred()) 845 }) 846 847 When("the user says yes", func() { 848 BeforeEach(func() { 849 buffer = NewBuffer() 850 _, err := buffer.Write([]byte("y\n")) 851 Expect(err).ToNot(HaveOccurred()) 852 }) 853 854 It("installs the plugin", func() { 855 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 856 857 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 858 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 859 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: y`, server.URL())) 860 861 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 862 Eventually(session).Should(Say(`\d.* .*B / ?`)) 863 864 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 865 Eventually(session).Should(Say("OK")) 866 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 867 868 Eventually(session).Should(Exit(0)) 869 }) 870 871 When("the plugin is already installed", func() { 872 BeforeEach(func() { 873 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 874 }) 875 876 It("fails and tells the user how to force a reinstall", func() { 877 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 878 879 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 880 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 881 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: y`, server.URL())) 882 883 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 884 Eventually(session).Should(Say(`\d.* .*B / ?`)) 885 886 Eventually(session).Should(Say("FAILED")) 887 Eventually(session.Err).Should(Say(`Plugin some-plugin 1\.0\.0 could not be installed\. A plugin with that name is already installed\.`)) 888 Eventually(session.Err).Should(Say(`TIP: Use 'cf install-plugin -f' to force a reinstall\.`)) 889 Eventually(session).Should(Exit(1)) 890 }) 891 }) 892 }) 893 894 When("the user says no", func() { 895 BeforeEach(func() { 896 buffer = NewBuffer() 897 _, err := buffer.Write([]byte("n\n")) 898 Expect(err).ToNot(HaveOccurred()) 899 }) 900 901 It("does not install the plugin", func() { 902 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL()) 903 904 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 905 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 906 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: n`, server.URL())) 907 Eventually(session).Should(Say(`Plugin installation cancelled\.`)) 908 909 Eventually(session).Should(Exit(0)) 910 911 Expect(server.ReceivedRequests()).To(HaveLen(0)) 912 }) 913 }) 914 915 When("the user interrupts with control-c", func() { 916 BeforeEach(func() { 917 buffer = NewBuffer() 918 _, err := buffer.Write([]byte("y")) // but not enter 919 Expect(err).ToNot(HaveOccurred()) 920 }) 921 922 It("does not install the plugin and does not create a bad state", func() { 923 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 924 925 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 926 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 927 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]:`, helpers.ConvertPathToRegularExpression(pluginPath))) 928 929 session.Interrupt() 930 931 Eventually(session).Should(Say("FAILED")) 932 933 // There is a timing issue -- the exit code may be either 1 (processed error), 2 (config writing error), or 130 (Ctrl-C) 934 Eventually(session).Should(SatisfyAny(Exit(1), Exit(2), Exit(130))) 935 936 Expect(server.ReceivedRequests()).To(HaveLen(0)) 937 938 // make sure cf plugins did not break 939 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 940 941 // make sure a retry of the plugin install works 942 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 943 Eventually(retrySession).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 944 Eventually(retrySession).Should(Exit(0)) 945 }) 946 }) 947 }) 948 }) 949 })