github.com/nimakaviani/cli@v6.37.1-0.20180619223813-e734901a73fa+incompatible/integration/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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("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 Context("when there is a command conflict", func() { 286 Context("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 Context("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 Context("when the plugin has a command that is the same as another plugin command", func() { 331 BeforeEach(func() { 332 helpers.InstallConfigurablePlugin("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 Context("when the plugin has a command that is the same as another plugin alias", func() { 358 BeforeEach(func() { 359 helpers.InstallConfigurablePlugin("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 Context("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 Context("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 Context("when the plugin has an alias that is the same as another plugin command", func() { 432 BeforeEach(func() { 433 helpers.InstallConfigurablePlugin("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 Context("when the plugin has an alias that is the same as another plugin alias", func() { 459 BeforeEach(func() { 460 helpers.InstallConfigurablePlugin("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 Context("when the plugin has a command and an alias that are both taken by another plugin", func() { 488 BeforeEach(func() { 489 helpers.InstallConfigurablePlugin("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 Context("when the -f flag is not given", func() { 517 Context("when the user says yes", func() { 518 BeforeEach(func() { 519 buffer = NewBuffer() 520 _, _ = buffer.Write([]byte("y\n")) 521 }) 522 523 It("installs the plugin", func() { 524 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 525 526 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 527 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 528 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]: y", helpers.ConvertPathToRegularExpression(pluginPath))) 529 Eventually(session).Should(Say("Installing plugin some-plugin\\.\\.\\.")) 530 Eventually(session).Should(Say("OK")) 531 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 532 533 Eventually(session).Should(Exit(0)) 534 535 pluginsSession := helpers.CF("plugins", "--checksum") 536 expectedSha := helpers.Sha1Sum( 537 generic.ExecutableFilename(filepath.Join(homeDir, ".cf/plugins/some-plugin"))) 538 Eventually(pluginsSession).Should(Say("some-plugin\\s+1.0.0\\s+%s", expectedSha)) 539 Eventually(pluginsSession).Should(Exit(0)) 540 541 Eventually(helpers.CF("some-command")).Should(Exit(0)) 542 543 helpSession := helpers.CF("help") 544 Eventually(helpSession).Should(Say("some-command")) 545 Eventually(helpSession).Should(Exit(0)) 546 }) 547 548 Context("when the plugin is already installed", func() { 549 BeforeEach(func() { 550 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 551 }) 552 553 It("fails and tells the user how to force a reinstall", func() { 554 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 555 556 Eventually(session).Should(Say("FAILED")) 557 Eventually(session.Err).Should(Say("Plugin some-plugin 1\\.0\\.0 could not be installed\\. A plugin with that name is already installed\\.")) 558 Eventually(session.Err).Should(Say("TIP: Use 'cf install-plugin -f' to force a reinstall\\.")) 559 560 Eventually(session).Should(Exit(1)) 561 }) 562 }) 563 }) 564 565 Context("when the user says no", func() { 566 BeforeEach(func() { 567 buffer = NewBuffer() 568 _, _ = buffer.Write([]byte("n\n")) 569 }) 570 571 It("does not install the plugin", func() { 572 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 573 574 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 575 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 576 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]: n", helpers.ConvertPathToRegularExpression(pluginPath))) 577 Eventually(session).Should(Say("Plugin installation cancelled\\.")) 578 579 Eventually(session).Should(Exit(0)) 580 }) 581 582 Context("when the plugin is already installed", func() { 583 BeforeEach(func() { 584 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 585 }) 586 587 It("does not uninstall the existing plugin", func() { 588 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 589 590 Eventually(session).Should(Say("Plugin installation cancelled\\.")) 591 592 Consistently(session).ShouldNot(Say("Plugin some-plugin 1\\.0\\.0 is already installed\\. Uninstalling existing plugin\\.\\.\\.")) 593 Consistently(session).ShouldNot(Say("CLI-MESSAGE-UNINSTALL")) 594 Consistently(session).ShouldNot(Say("Plugin some-plugin successfully uninstalled\\.")) 595 596 Eventually(session).Should(Exit(0)) 597 }) 598 }) 599 }) 600 601 Context("when the user interrupts with control-c", func() { 602 BeforeEach(func() { 603 buffer = NewBuffer() 604 _, _ = buffer.Write([]byte("y")) // but not enter 605 }) 606 607 It("does not install the plugin and does not create a bad state", func() { 608 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 609 610 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 611 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 612 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]:", helpers.ConvertPathToRegularExpression(pluginPath))) 613 614 session.Interrupt() 615 616 Eventually(session).Should(Say("FAILED")) 617 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 // make sure cf plugins did not break 622 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 623 624 // make sure a retry of the plugin install works 625 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 626 Eventually(retrySession).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 627 Eventually(retrySession).Should(Exit(0)) 628 }) 629 }) 630 }) 631 }) 632 }) 633 634 Describe("installing a plugin from a URL", func() { 635 var ( 636 server *Server 637 ) 638 639 BeforeEach(func() { 640 server = NewTLSServer() 641 // Suppresses ginkgo server logs 642 server.HTTPTestServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 643 }) 644 645 AfterEach(func() { 646 server.Close() 647 }) 648 649 Context("when a URL and the -f flag are provided", func() { 650 Context("when an executable is available for download at the URL", func() { 651 var ( 652 pluginData []byte 653 ) 654 655 BeforeEach(func() { 656 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin", "some-plugin", "1.0.0", 657 []helpers.PluginCommand{ 658 {Name: "some-command", Help: "some-command-help"}, 659 }, 660 ) 661 662 var err error 663 pluginData, err = ioutil.ReadFile(pluginPath) 664 Expect(err).ToNot(HaveOccurred()) 665 server.AppendHandlers( 666 CombineHandlers( 667 VerifyRequest(http.MethodGet, "/"), 668 RespondWith(http.StatusOK, pluginData), 669 ), 670 ) 671 }) 672 673 AfterEach(func() { 674 err := os.Remove(pluginPath) 675 Expect(err).ToNot(HaveOccurred()) 676 }) 677 678 It("installs the plugin", func() { 679 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 680 681 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 682 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 683 684 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 685 Eventually(session).Should(Say("\\d.* .*B / ?")) 686 687 Eventually(session).Should(Say("Installing plugin some-plugin\\.\\.\\.")) 688 Eventually(session).Should(Say("OK")) 689 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 690 691 Eventually(session).Should(Exit(0)) 692 }) 693 694 Context("when the URL redirects", func() { 695 BeforeEach(func() { 696 server.Reset() 697 server.AppendHandlers( 698 CombineHandlers( 699 VerifyRequest(http.MethodGet, "/redirect"), 700 RespondWith(http.StatusMovedPermanently, nil, http.Header{"Location": []string{server.URL()}}), 701 ), 702 CombineHandlers( 703 VerifyRequest(http.MethodGet, "/"), 704 RespondWith(http.StatusOK, pluginData), 705 )) 706 }) 707 708 It("installs the plugin", func() { 709 session := helpers.CF("install-plugin", "-f", fmt.Sprintf("%s/redirect", server.URL()), "-k") 710 711 Eventually(session).Should(Say("Installing plugin some-plugin\\.\\.\\.")) 712 Eventually(session).Should(Say("OK")) 713 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 714 715 Eventually(session).Should(Exit(0)) 716 }) 717 }) 718 719 Context("when the plugin has already been installed", func() { 720 BeforeEach(func() { 721 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 722 }) 723 724 It("uninstalls and reinstalls the plugin", func() { 725 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 726 727 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 728 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 729 730 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 731 Eventually(session).Should(Say("\\d.* .*B / ?")) 732 733 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 is already installed\\. Uninstalling existing plugin\\.\\.\\.")) 734 Eventually(session).Should(Say("CLI-MESSAGE-UNINSTALL")) 735 Eventually(session).Should(Say("Plugin some-plugin successfully uninstalled\\.")) 736 Eventually(session).Should(Say("OK")) 737 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 738 739 Eventually(session).Should(Exit(0)) 740 }) 741 }) 742 }) 743 744 Context("when a 4xx or 5xx HTTP response status is encountered", func() { 745 BeforeEach(func() { 746 server.AppendHandlers( 747 CombineHandlers( 748 VerifyRequest(http.MethodGet, "/"), 749 RespondWith(http.StatusNotFound, nil), 750 ), 751 ) 752 }) 753 754 It("displays an appropriate error", func() { 755 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 756 757 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 758 Eventually(session).Should(Say("FAILED")) 759 Eventually(session.Err).Should(Say("Download attempt failed; server returned 404 Not Found")) 760 Eventually(session.Err).Should(Say("Unable to install; plugin is not available from the given URL\\.")) 761 762 Eventually(session).Should(Exit(1)) 763 }) 764 }) 765 766 Context("when the file is not a plugin", func() { 767 BeforeEach(func() { 768 var err error 769 pluginPath, err = Build("code.cloudfoundry.org/cli/integration/assets/non_plugin") 770 Expect(err).ToNot(HaveOccurred()) 771 772 pluginData, err := ioutil.ReadFile(pluginPath) 773 Expect(err).ToNot(HaveOccurred()) 774 server.AppendHandlers( 775 CombineHandlers( 776 VerifyRequest(http.MethodGet, "/"), 777 RespondWith(http.StatusOK, pluginData), 778 ), 779 ) 780 }) 781 782 AfterEach(func() { 783 err := os.Remove(pluginPath) 784 Expect(err).ToNot(HaveOccurred()) 785 }) 786 787 It("tells the user that the file is not a plugin and fails", func() { 788 session := helpers.CF("install-plugin", "-f", server.URL(), "-k") 789 790 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 791 Eventually(session).Should(Say("FAILED")) 792 Eventually(session.Err).Should(Say("File is not a valid cf CLI plugin binary\\.")) 793 794 Eventually(session).Should(Exit(1)) 795 }) 796 }) 797 }) 798 799 Context("when the -f flag is not provided", func() { 800 var ( 801 pluginData []byte 802 ) 803 804 BeforeEach(func() { 805 pluginPath = helpers.BuildConfigurablePlugin("configurable_plugin", "some-plugin", "1.0.0", 806 []helpers.PluginCommand{ 807 {Name: "some-command", Help: "some-command-help"}, 808 }, 809 ) 810 811 var err error 812 pluginData, err = ioutil.ReadFile(pluginPath) 813 Expect(err).ToNot(HaveOccurred()) 814 server.AppendHandlers( 815 CombineHandlers( 816 VerifyRequest(http.MethodGet, "/"), 817 RespondWith(http.StatusOK, pluginData), 818 ), 819 ) 820 }) 821 822 AfterEach(func() { 823 err := os.Remove(pluginPath) 824 Expect(err).ToNot(HaveOccurred()) 825 }) 826 827 Context("when the user says yes", func() { 828 BeforeEach(func() { 829 buffer = NewBuffer() 830 _, _ = buffer.Write([]byte("y\n")) 831 }) 832 833 It("installs the plugin", func() { 834 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 835 836 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 837 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 838 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]: y", server.URL())) 839 840 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 841 Eventually(session).Should(Say("\\d.* .*B / ?")) 842 843 Eventually(session).Should(Say("Installing plugin some-plugin\\.\\.\\.")) 844 Eventually(session).Should(Say("OK")) 845 Eventually(session).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 846 847 Eventually(session).Should(Exit(0)) 848 }) 849 850 Context("when the plugin is already installed", func() { 851 BeforeEach(func() { 852 Eventually(helpers.CF("install-plugin", pluginPath, "-f")).Should(Exit(0)) 853 }) 854 855 It("fails and tells the user how to force a reinstall", func() { 856 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL(), "-k") 857 858 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 859 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 860 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]: y", server.URL())) 861 862 Eventually(session).Should(Say("Starting download of plugin binary from URL\\.\\.\\.")) 863 Eventually(session).Should(Say("\\d.* .*B / ?")) 864 865 Eventually(session).Should(Say("FAILED")) 866 Eventually(session.Err).Should(Say("Plugin some-plugin 1\\.0\\.0 could not be installed\\. A plugin with that name is already installed\\.")) 867 Eventually(session.Err).Should(Say("TIP: Use 'cf install-plugin -f' to force a reinstall\\.")) 868 Eventually(session).Should(Exit(1)) 869 }) 870 }) 871 }) 872 873 Context("when the user says no", func() { 874 BeforeEach(func() { 875 buffer = NewBuffer() 876 _, _ = buffer.Write([]byte("n\n")) 877 }) 878 879 It("does not install the plugin", func() { 880 session := helpers.CFWithStdin(buffer, "install-plugin", server.URL()) 881 882 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 883 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 884 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]: n", server.URL())) 885 Eventually(session).Should(Say("Plugin installation cancelled\\.")) 886 887 Eventually(session).Should(Exit(0)) 888 889 Expect(server.ReceivedRequests()).To(HaveLen(0)) 890 }) 891 }) 892 893 Context("when the user interrupts with control-c", func() { 894 BeforeEach(func() { 895 buffer = NewBuffer() 896 _, _ = buffer.Write([]byte("y")) // but not enter 897 }) 898 899 It("does not install the plugin and does not create a bad state", func() { 900 session := helpers.CFWithStdin(buffer, "install-plugin", pluginPath) 901 902 Eventually(session).Should(Say("Attention: Plugins are binaries written by potentially untrusted authors\\.")) 903 Eventually(session).Should(Say("Install and use plugins at your own risk\\.")) 904 Eventually(session).Should(Say("Do you want to install the plugin %s\\? \\[yN\\]:", helpers.ConvertPathToRegularExpression(pluginPath))) 905 906 session.Interrupt() 907 908 Eventually(session).Should(Say("FAILED")) 909 910 // There is a timing issue -- the exit code may be either 1 (processed error), 2 (config writing error), or 130 (Ctrl-C) 911 Eventually(session).Should(SatisfyAny(Exit(1), Exit(2), Exit(130))) 912 913 Expect(server.ReceivedRequests()).To(HaveLen(0)) 914 915 // make sure cf plugins did not break 916 Eventually(helpers.CF("plugins", "--checksum")).Should(Exit(0)) 917 918 // make sure a retry of the plugin install works 919 retrySession := helpers.CF("install-plugin", pluginPath, "-f") 920 Eventually(retrySession).Should(Say("Plugin some-plugin 1\\.0\\.0 successfully installed\\.")) 921 Eventually(retrySession).Should(Exit(0)) 922 }) 923 }) 924 }) 925 }) 926 })