github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/integration/v6/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 619 Eventually(session).Should(Say("FAILED")) 620 621 // There is a timing issue -- the exit code may be either 1 (processed error), 2 (config writing error), or 130 (Ctrl-C) 622 Eventually(session).Should(SatisfyAny(Exit(1), Exit(2), Exit(130))) 623 624 // make sure cf plugins did not break 625 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 626 627 // make sure a retry of the plugin install works 628 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 629 Eventually(retrySession).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 630 Eventually(retrySession).Should(Exit(0)) 631 }) 632 }) 633 }) 634 }) 635 }) 636 637 Describe("installing a plugin from a URL", func() { 638 var ( 639 server *Server 640 ) 641 642 BeforeEach(func() { 643 server = NewTLSServer() 644 // Suppresses ginkgo server logs 645 server.HTTPTestServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 646 }) 647 648 AfterEach(func() { 649 server.Close() 650 }) 651 652 When("a URL and the -f flag are provided", func() { 653 When("an executable is available for download at the URL", func() { 654 var ( 655 pluginData []byte 656 ) 657 658 BeforeEach(func() { 659 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin", "some-plugin", "1.0.0", 660 []helpers.PluginCommand{ 661 {Name: "some-command", Help: "some-command-help"}, 662 }, 663 ) 664 665 var err error 666 pluginData, err = ioutil.ReadFile(pluginPath) 667 Expect(err).ToNot(HaveOccurred()) 668 server.AppendHandlers( 669 CombineHandlers( 670 VerifyRequest(http.MethodGet, "/"), 671 RespondWith(http.StatusOK, pluginData), 672 ), 673 ) 674 }) 675 676 AfterEach(func() { 677 err := os.Remove(pluginPath) 678 Expect(err).ToNot(HaveOccurred()) 679 }) 680 681 It("installs the plugin", func() { 682 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 683 684 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 685 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 686 687 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 688 Eventually(session).Should(Say(`\d.* .*B / ?`)) 689 690 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 691 Eventually(session).Should(Say("OK")) 692 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 693 694 Eventually(session).Should(Exit(0)) 695 }) 696 697 When("the URL redirects", func() { 698 BeforeEach(func() { 699 server.Reset() 700 server.AppendHandlers( 701 CombineHandlers( 702 VerifyRequest(http.MethodGet, "/redirect"), 703 RespondWith(http.StatusMovedPermanently, nil, http.Header{"Location": []string{server.URL()}}), 704 ), 705 CombineHandlers( 706 VerifyRequest(http.MethodGet, "/"), 707 RespondWith(http.StatusOK, pluginData), 708 )) 709 }) 710 711 It("installs the plugin", func() { 712 session := helpers.CF("install-plugin", "-f", fmt.Sprintf("%s/redirect", server.URL()), "-k") 713 714 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 715 Eventually(session).Should(Say("OK")) 716 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 717 718 Eventually(session).Should(Exit(0)) 719 }) 720 }) 721 722 When("the plugin has already been installed", func() { 723 BeforeEach(func() { 724 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 725 }) 726 727 It("uninstalls and reinstalls the plugin", func() { 728 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 729 730 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 731 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 732 733 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 734 Eventually(session).Should(Say(`\d.* .*B / ?`)) 735 736 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 is already installed\. Uninstalling existing plugin\.\.\.`)) 737 Eventually(session).Should(Say("CLI-MESSAGE-UNINSTALL")) 738 Eventually(session).Should(Say(`Plugin some-plugin successfully uninstalled\.`)) 739 Eventually(session).Should(Say("OK")) 740 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 741 742 Eventually(session).Should(Exit(0)) 743 }) 744 }) 745 }) 746 747 When("a 4xx or 5xx HTTP response status is encountered", func() { 748 BeforeEach(func() { 749 server.AppendHandlers( 750 CombineHandlers( 751 VerifyRequest(http.MethodGet, "/"), 752 RespondWith(http.StatusNotFound, nil), 753 ), 754 ) 755 }) 756 757 It("displays an appropriate error", func() { 758 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 759 760 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 761 Eventually(session).Should(Say("FAILED")) 762 Eventually(session.Err).Should(Say("Download attempt failed; server returned 404 Not Found")) 763 Eventually(session.Err).Should(Say(`Unable to install; plugin is not available from the given URL\.`)) 764 765 Eventually(session).Should(Exit(1)) 766 }) 767 }) 768 769 When("the file is not a plugin", func() { 770 BeforeEach(func() { 771 var err error 772 pluginPath, err = Build("code.cloudfoundry.org/cli/integration/assets/non_plugin") 773 Expect(err).ToNot(HaveOccurred()) 774 775 pluginData, err := ioutil.ReadFile(pluginPath) 776 Expect(err).ToNot(HaveOccurred()) 777 server.AppendHandlers( 778 CombineHandlers( 779 VerifyRequest(http.MethodGet, "/"), 780 RespondWith(http.StatusOK, pluginData), 781 ), 782 ) 783 }) 784 785 AfterEach(func() { 786 err := os.Remove(pluginPath) 787 Expect(err).ToNot(HaveOccurred()) 788 }) 789 790 It("tells the user that the file is not a plugin and fails", func() { 791 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 792 793 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 794 Eventually(session).Should(Say("FAILED")) 795 Eventually(session.Err).Should(Say(`File is not a valid cf CLI plugin binary\.`)) 796 797 Eventually(session).Should(Exit(1)) 798 }) 799 }) 800 }) 801 802 When("the -f flag is not provided", func() { 803 var ( 804 pluginData []byte 805 ) 806 807 BeforeEach(func() { 808 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin", "some-plugin", "1.0.0", 809 []helpers.PluginCommand{ 810 {Name: "some-command", Help: "some-command-help"}, 811 }, 812 ) 813 814 var err error 815 pluginData, err = ioutil.ReadFile(pluginPath) 816 Expect(err).ToNot(HaveOccurred()) 817 server.AppendHandlers( 818 CombineHandlers( 819 VerifyRequest(http.MethodGet, "/"), 820 RespondWith(http.StatusOK, pluginData), 821 ), 822 ) 823 }) 824 825 AfterEach(func() { 826 err := os.Remove(pluginPath) 827 Expect(err).ToNot(HaveOccurred()) 828 }) 829 830 When("the user says yes", func() { 831 BeforeEach(func() { 832 buffer = NewBuffer() 833 _, err := buffer.Write([]byte("y\n")) 834 Expect(err).ToNot(HaveOccurred()) 835 }) 836 837 It("installs the plugin", func() { 838 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 839 840 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 841 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 842 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: y`, server.URL())) 843 844 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 845 Eventually(session).Should(Say(`\d.* .*B / ?`)) 846 847 Eventually(session).Should(Say(`Installing plugin some-plugin\.\.\.`)) 848 Eventually(session).Should(Say("OK")) 849 Eventually(session).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 850 851 Eventually(session).Should(Exit(0)) 852 }) 853 854 When("the plugin is already installed", func() { 855 BeforeEach(func() { 856 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 857 }) 858 859 It("fails and tells the user how to force a reinstall", func() { 860 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 861 862 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 863 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 864 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: y`, server.URL())) 865 866 Eventually(session).Should(Say(`Starting download of plugin binary from URL\.\.\.`)) 867 Eventually(session).Should(Say(`\d.* .*B / ?`)) 868 869 Eventually(session).Should(Say("FAILED")) 870 Eventually(session.Err).Should(Say(`Plugin some-plugin 1\.0\.0 could not be installed\. A plugin with that name is already installed\.`)) 871 Eventually(session.Err).Should(Say(`TIP: Use 'cf install-plugin -f' to force a reinstall\.`)) 872 Eventually(session).Should(Exit(1)) 873 }) 874 }) 875 }) 876 877 When("the user says no", func() { 878 BeforeEach(func() { 879 buffer = NewBuffer() 880 _, err := buffer.Write([]byte("n\n")) 881 Expect(err).ToNot(HaveOccurred()) 882 }) 883 884 It("does not install the plugin", func() { 885 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL()) 886 887 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 888 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 889 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]: n`, server.URL())) 890 Eventually(session).Should(Say(`Plugin installation cancelled\.`)) 891 892 Eventually(session).Should(Exit(0)) 893 894 Expect(server.ReceivedRequests()).To(HaveLen(0)) 895 }) 896 }) 897 898 When("the user interrupts with control-c", func() { 899 BeforeEach(func() { 900 buffer = NewBuffer() 901 _, err := buffer.Write([]byte("y")) // but not enter 902 Expect(err).ToNot(HaveOccurred()) 903 }) 904 905 It("does not install the plugin and does not create a bad state", func() { 906 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 907 908 Eventually(session).Should(Say(`Attention: Plugins are binaries written by potentially untrusted authors\.`)) 909 Eventually(session).Should(Say(`Install and use plugins at your own risk\.`)) 910 Eventually(session).Should(Say(`Do you want to install the plugin %s\? \[yN\]:`, helpers.ConvertPathToRegularExpression(pluginPath))) 911 912 session.Interrupt() 913 914 Eventually(session).Should(Say("FAILED")) 915 916 // There is a timing issue -- the exit code may be either 1 (processed error), 2 (config writing error), or 130 (Ctrl-C) 917 Eventually(session).Should(SatisfyAny(Exit(1), Exit(2), Exit(130))) 918 919 Expect(server.ReceivedRequests()).To(HaveLen(0)) 920 921 // make sure cf plugins did not break 922 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 923 924 // make sure a retry of the plugin install works 925 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 926 Eventually(retrySession).Should(Say(`Plugin some-plugin 1\.0\.0 successfully installed\.`)) 927 Eventually(retrySession).Should(Exit(0)) 928 }) 929 }) 930 }) 931 }) 932 })