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