github.com/wanddynosios/cli@v7.1.0+incompatible/integration/v6/experimental/v3_ssh_command_test.go (about)

     1  package experimental
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/cli/integration/helpers"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  	. "github.com/onsi/gomega/gbytes"
    12  	. "github.com/onsi/gomega/gexec"
    13  )
    14  
    15  var _ = Describe("v3-ssh command", func() {
    16  	var (
    17  		appName   string
    18  		orgName   string
    19  		spaceName string
    20  	)
    21  
    22  	BeforeEach(func() {
    23  		helpers.SkipIfClientCredentialsTestMode() // client credentials cannot presently ssh
    24  		appName = helpers.PrefixedRandomName("app")
    25  		orgName = helpers.NewOrgName()
    26  		spaceName = helpers.NewSpaceName()
    27  	})
    28  
    29  	When("--help flag is set", func() {
    30  		It("Displays command usage to output", func() {
    31  			session := helpers.CF("v3-ssh", "--help")
    32  
    33  			Eventually(session).Should(Say(`NAME:`))
    34  			Eventually(session).Should(Say(`ssh - SSH to an application container instance`))
    35  			Eventually(session).Should(Say(`USAGE:`))
    36  			Eventually(session).Should(Say(`cf v3-ssh APP_NAME \[--process PROCESS\] \[-i INDEX\] \[-c COMMAND\]\n`))
    37  			Eventually(session).Should(Say(`\[-L \[BIND_ADDRESS:\]LOCAL_PORT:REMOTE_HOST:REMOTE_PORT\]\.\.\. \[--skip-remote-execution\]`))
    38  			Eventually(session).Should(Say(`\[--disable-pseudo-tty \| --force-pseudo-tty \| --request-pseudo-tty\] \[--skip-host-validation\]`))
    39  			Eventually(session).Should(Say(`OPTIONS:`))
    40  			Eventually(session).Should(Say(`--app-instance-index, -i\s+App process instance index \(Default: 0\)`))
    41  			Eventually(session).Should(Say(`--command, -c\s+Command to run`))
    42  			Eventually(session).Should(Say(`--disable-pseudo-tty, -T\s+Disable pseudo-tty allocation`))
    43  			Eventually(session).Should(Say(`--force-pseudo-tty\s+Force pseudo-tty allocation`))
    44  			Eventually(session).Should(Say(`-L\s+Local port forward specification`))
    45  			Eventually(session).Should(Say(`--process\s+App process name \(Default: web\)`))
    46  			Eventually(session).Should(Say(`--request-pseudo-tty, -t\s+Request pseudo-tty allocation`))
    47  			Eventually(session).Should(Say(`--skip-host-validation, -k\s+Skip host key validation\. Not recommended!`))
    48  			Eventually(session).Should(Say(`--skip-remote-execution, -N\s+Do not execute a remote command`))
    49  			Eventually(session).Should(Say(`ENVIRONMENT:`))
    50  			Eventually(session).Should(Say(`all_proxy=\s+Specify a proxy server to enable proxying for all requests`))
    51  			Eventually(session).Should(Say(`SEE ALSO:`))
    52  			Eventually(session).Should(Say(`allow-space-ssh, enable-ssh, space-ssh-allowed, ssh-code, ssh-enabled`))
    53  			Eventually(session).Should(Exit(0))
    54  		})
    55  	})
    56  
    57  	When("the app name is not provided", func() {
    58  		It("tells the user that the app name is required, prints help text, and exits 1", func() {
    59  			session := helpers.CF("v3-ssh")
    60  
    61  			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
    62  			Eventually(session).Should(Say("NAME:"))
    63  			Eventually(session).Should(Exit(1))
    64  		})
    65  	})
    66  
    67  	It("displays the experimental warning", func() {
    68  		session := helpers.CF("v3-ssh", appName)
    69  		Eventually(session.Err).Should(Say("This command is in EXPERIMENTAL stage and may change without notice"))
    70  		Eventually(session).Should(Exit())
    71  	})
    72  
    73  	When("the environment is not setup correctly", func() {
    74  		When("no API endpoint is set", func() {
    75  			BeforeEach(func() {
    76  				helpers.UnsetAPI()
    77  			})
    78  
    79  			It("fails with no API endpoint set message", func() {
    80  				session := helpers.CF("v3-ssh", appName)
    81  				Eventually(session).Should(Say("FAILED"))
    82  				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
    83  				Eventually(session).Should(Exit(1))
    84  			})
    85  		})
    86  
    87  		When("not logged in", func() {
    88  			BeforeEach(func() {
    89  				helpers.LogoutCF()
    90  			})
    91  
    92  			It("fails with not logged in message", func() {
    93  				session := helpers.CF("v3-ssh", appName)
    94  				Eventually(session).Should(Say("FAILED"))
    95  				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' or 'cf login --sso' to log in."))
    96  				Eventually(session).Should(Exit(1))
    97  			})
    98  		})
    99  
   100  		When("there is no org set", func() {
   101  			BeforeEach(func() {
   102  				helpers.LogoutCF()
   103  				helpers.LoginCF()
   104  			})
   105  
   106  			It("fails with no targeted org error message", func() {
   107  				session := helpers.CF("v3-ssh", appName)
   108  				Eventually(session).Should(Say("FAILED"))
   109  				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
   110  				Eventually(session).Should(Exit(1))
   111  			})
   112  		})
   113  
   114  		When("there is no space set", func() {
   115  			BeforeEach(func() {
   116  				helpers.LogoutCF()
   117  				helpers.LoginCF()
   118  				helpers.TargetOrg(ReadOnlyOrg)
   119  			})
   120  
   121  			It("fails with no targeted space error message", func() {
   122  				session := helpers.CF("v3-ssh", appName)
   123  				Eventually(session).Should(Say("FAILED"))
   124  				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space."))
   125  				Eventually(session).Should(Exit(1))
   126  			})
   127  		})
   128  	})
   129  
   130  	When("the environment is setup correctly", func() {
   131  		BeforeEach(func() {
   132  			helpers.SetupCF(orgName, spaceName)
   133  		})
   134  
   135  		AfterEach(func() {
   136  			helpers.QuickDeleteOrg(orgName)
   137  		})
   138  
   139  		When("the app does not exist", func() {
   140  			It("it displays the app does not exist", func() {
   141  				session := helpers.CF("v3-ssh", appName)
   142  				Eventually(session).Should(Say("FAILED"))
   143  				Eventually(session.Err).Should(Say("App '%s' not found", appName))
   144  				Eventually(session).Should(Exit(1))
   145  			})
   146  		})
   147  
   148  		When("the app exists", func() {
   149  			BeforeEach(func() {
   150  				helpers.WithProcfileApp(func(appDir string) {
   151  					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
   152  				})
   153  			})
   154  
   155  			Context("TTY Options", func() {
   156  				// * The columns specify the various TTY flags passed to cf ssh
   157  				//   (--disable-pseudo-tty, --force-pseudo-tty, --request-pseudo-tty).
   158  				// * The rows specify what kind of shell you’re running "cf ssh" from. To
   159  				//   simulate an interactive shell, simply use your terminal as always.
   160  				//   To simulate a non-interactive shell, append "<< EOF <new-line>
   161  				//   <command-to-execute-on-remote-host> <new-line> EOF" to your command
   162  				// * The values (yes/no) determine whether a TTY session should be
   163  				//   allocated on the remote host. Verify by running "TTY" on remote host.
   164  				//
   165  				//               TTY Option -> | Default(auto) | Disable | Force | Request
   166  				// Shell_Type__________________|_______________|_________|_______|_____________
   167  				// interactive                 | Yes           | No      | Yes   | Yes
   168  				// non-interactive             | No            | No      | No    | No
   169  				// interactive w/ commands     | No            | No      | Yes   | Yes
   170  				// non-interactive w/ commands | No            | No      | Yes   | No
   171  
   172  				When("the running session is interactive", func() {
   173  					// This should be tested manually (launching an interactive shell in code is hard)
   174  				})
   175  
   176  				When("the running session is non-interactive", func() {
   177  					When("providing commands to run on the remote host", func() {
   178  						When("using default tty option (auto)", func() {
   179  							It("the remote shell is not TTY", func() {
   180  								// we echo hello because a successful ssh call returns the status
   181  								session := helpers.CF("v3-ssh", appName, "-c tty;", "-c echo hello")
   182  								Eventually(session).Should(Say("not a tty"))
   183  								Eventually(session).Should(Exit(0))
   184  							})
   185  						})
   186  
   187  						When("disable-pseudo-tty is specified", func() {
   188  							It("the remote shell is not TTY", func() {
   189  								session := helpers.CF("v3-ssh", appName, "--disable-pseudo-tty", "-c tty;", "-c echo hello")
   190  								Eventually(session).Should(Say("not a tty"))
   191  								Eventually(session).Should(Exit(0))
   192  							})
   193  						})
   194  
   195  						When("force-pseudo-tty is specified", func() {
   196  							It("the remote shell is TTY", func() {
   197  								session := helpers.CF("v3-ssh", appName, "--force-pseudo-tty", "-c tty;", "-c echo hello")
   198  								Eventually(session).ShouldNot(Say("not a tty"))
   199  								Eventually(session).Should(Say("/dev/*"))
   200  								Eventually(session).Should(Exit(0))
   201  							})
   202  						})
   203  
   204  						When("request-pseudo-tty is specified", func() {
   205  							It("the remote shell is not TTY", func() {
   206  								session := helpers.CF("v3-ssh", appName, "--request-pseudo-tty", "-c tty;", "-c echo hello")
   207  								Eventually(session).Should(Say("not a tty"))
   208  								Eventually(session).Should(Exit(0))
   209  							})
   210  						})
   211  					})
   212  
   213  					When("not providing commands as args", func() {
   214  						var buffer *Buffer
   215  
   216  						BeforeEach(func() {
   217  							buffer = NewBuffer()
   218  						})
   219  
   220  						When("using default tty option (auto)", func() {
   221  							It("the remote shell is not TTY", func() {
   222  								_, err := buffer.Write([]byte("tty\n"))
   223  								Expect(err).NotTo(HaveOccurred())
   224  
   225  								_, err = buffer.Write([]byte("echo hello\n"))
   226  								Expect(err).NotTo(HaveOccurred())
   227  
   228  								_, err = buffer.Write([]byte("exit\n"))
   229  								Expect(err).NotTo(HaveOccurred())
   230  
   231  								session := helpers.CFWithStdin(buffer, "v3-ssh", appName)
   232  								Eventually(session).Should(Say("not a tty"))
   233  								Eventually(session).Should(Exit(0))
   234  							})
   235  						})
   236  
   237  						When("disable-pseudo-tty is specified", func() {
   238  							It("the remote shell is not TTY", func() {
   239  								_, err := buffer.Write([]byte("tty\n"))
   240  								Expect(err).NotTo(HaveOccurred())
   241  
   242  								_, err = buffer.Write([]byte("echo hello\n"))
   243  								Expect(err).NotTo(HaveOccurred())
   244  
   245  								_, err = buffer.Write([]byte("exit\n"))
   246  								Expect(err).NotTo(HaveOccurred())
   247  
   248  								session := helpers.CFWithStdin(buffer, "v3-ssh", appName, "--disable-pseudo-tty")
   249  								Eventually(session).Should(Say("not a tty"))
   250  								Eventually(session).Should(Exit(0))
   251  							})
   252  						})
   253  
   254  						When("force-pseudo-tty is specified", func() {
   255  							It("the remote shell is TTY", func() {
   256  								_, err := buffer.Write([]byte("tty\n"))
   257  								Expect(err).NotTo(HaveOccurred())
   258  
   259  								_, err = buffer.Write([]byte("echo hello\n"))
   260  								Expect(err).NotTo(HaveOccurred())
   261  
   262  								_, err = buffer.Write([]byte("exit\n"))
   263  								Expect(err).NotTo(HaveOccurred())
   264  
   265  								session := helpers.CFWithStdin(buffer, "v3-ssh", appName, "--force-pseudo-tty")
   266  								Eventually(session).ShouldNot(Say("not a tty"))
   267  								Eventually(session).Should(Say("/dev/*"))
   268  								Eventually(session).Should(Exit(0))
   269  							})
   270  						})
   271  
   272  						When("request-pseudo-tty is specified", func() {
   273  							It("the remote shell is TTY", func() {
   274  								_, err := buffer.Write([]byte("tty\n"))
   275  								Expect(err).NotTo(HaveOccurred())
   276  
   277  								_, err = buffer.Write([]byte("echo hello\n"))
   278  								Expect(err).NotTo(HaveOccurred())
   279  
   280  								_, err = buffer.Write([]byte("exit\n"))
   281  								Expect(err).NotTo(HaveOccurred())
   282  
   283  								session := helpers.CFWithStdin(buffer, "v3-ssh", appName, "--request-pseudo-tty")
   284  								Eventually(session).Should(Say("not a tty"))
   285  								Eventually(session).Should(Exit(0))
   286  							})
   287  						})
   288  					})
   289  				})
   290  			})
   291  
   292  			It("ssh's to the process 'web', index '0'", func() {
   293  				session := helpers.CF("v3-ssh", appName, "-c", "ps aux;", "-c", "env")
   294  				// To verify we ssh'd into the web process we examine processes
   295  				// that were launched tha are unique to that process
   296  				Eventually(session).Should(Say("vcap.*ruby"))
   297  				Eventually(session).Should(Say("INSTANCE_INDEX=0"))
   298  				Eventually(session).Should(Exit(0))
   299  			})
   300  
   301  			When("commands to run are specified", func() {
   302  				It("ssh's to the default container and runs the commands", func() {
   303  					session := helpers.CF("v3-ssh", appName, "-c", "ls;", "-c", "echo $USER")
   304  					Eventually(session).Should(Say("app"))
   305  					Eventually(session).Should(Say("deps"))
   306  					Eventually(session).Should(Say("logs"))
   307  					Eventually(session).Should(Say("vcap"))
   308  					Eventually(session).Should(Exit(0))
   309  				})
   310  			})
   311  
   312  			When("the application hasn't started", func() {
   313  				BeforeEach(func() {
   314  					session := helpers.CF("v3-stop", appName)
   315  					Eventually(session).Should(Exit(0))
   316  				})
   317  
   318  				It("prints an error message", func() {
   319  					session := helpers.CF("v3-ssh", appName)
   320  					Eventually(session).Should(Say("FAILED"))
   321  					Eventually(session.Err).Should(Say(fmt.Sprintf("Application '%s' is not in the STARTED state", appName)))
   322  					Eventually(session).Should(Exit(1))
   323  				})
   324  			})
   325  
   326  			When("the remote command exits with a different status code", func() {
   327  				It("exits with that status code", func() {
   328  					session := helpers.CF("v3-ssh", appName, "-c", "asdf")
   329  					Eventually(session).Should(Exit(127))
   330  				})
   331  			})
   332  
   333  			When("port forwarding is used", func() {
   334  				var port int
   335  
   336  				BeforeEach(func() {
   337  					port = 55500 + GinkgoParallelNode()
   338  				})
   339  
   340  				It("configures local port to connect to the app port", func() {
   341  					session := helpers.CF("v3-ssh", appName, "-N", "-L", fmt.Sprintf("%d:localhost:8080", port))
   342  
   343  					time.Sleep(35 * time.Second) // Need to wait a few seconds for pipes to connect.
   344  					response, err := http.Get(fmt.Sprintf("http://localhost:%d/", port))
   345  					Expect(err).ToNot(HaveOccurred())
   346  					defer response.Body.Close()
   347  
   348  					Eventually(BufferReader(response.Body)).Should(Say("WEBrick"))
   349  
   350  					session.Kill()
   351  					Eventually(session).Should(Exit())
   352  				})
   353  			})
   354  
   355  			When("a process is specified", func() {
   356  				When("the process does not exist", func() {
   357  					It("displays the process does not exist", func() {
   358  						session := helpers.CF("v3-ssh", appName, "--process", "fake-process")
   359  						Eventually(session).Should(Say("FAILED"))
   360  						Eventually(session.Err).Should(Say("Process fake-process not found"))
   361  						Eventually(session).Should(Exit(1))
   362  					})
   363  				})
   364  
   365  				When("the process exists", func() {
   366  					BeforeEach(func() {
   367  						Eventually(helpers.CF("v3-scale", appName, "--process", "console", "-i", "1")).Should(Exit(0))
   368  					})
   369  
   370  					It("ssh's to the process's default index", func() {
   371  						session := helpers.CF("v3-ssh", appName, "--process", "console", "-c", "ps aux;", "-c", "env")
   372  						Eventually(session).Should(Say("vcap.*irb"))
   373  						Eventually(session).Should(Say("INSTANCE_INDEX=0"))
   374  						Eventually(session).Should(Exit(0))
   375  					})
   376  
   377  					When("the index is specified", func() {
   378  						When("the index does not exist", func() {
   379  							It("returns an instance not found error", func() {
   380  								session := helpers.CF("v3-ssh", appName, "--process", "console", "-i", "1", "-c", "ps aux;", "-c", "env")
   381  								Eventually(session).Should(Say("FAILED"))
   382  								Eventually(session.Err).Should(Say("Instance %d of process console not found", 1))
   383  								Eventually(session).Should(Exit(1))
   384  							})
   385  						})
   386  
   387  						When("the index exists", func() {
   388  							It("ssh's to the provided index", func() {
   389  								session := helpers.CF("v3-ssh", appName, "--process", "console", "-i", "0", "-c", "ps aux;", "-c", "env")
   390  								Eventually(session).Should(Say("vcap.*irb"))
   391  								Eventually(session).Should(Say("INSTANCE_INDEX=0"))
   392  								Eventually(session).Should(Exit(0))
   393  							})
   394  						})
   395  					})
   396  				})
   397  			})
   398  
   399  			When("a user isn't authorized", func() {
   400  				var (
   401  					newUser string
   402  					newPass string
   403  				)
   404  
   405  				BeforeEach(func() {
   406  					newUser = helpers.NewUsername()
   407  					newPass = helpers.NewPassword()
   408  
   409  					Eventually(helpers.CF("create-user", newUser, newPass)).Should(Exit(0))
   410  					Eventually(helpers.CF("set-space-role", newUser, orgName, spaceName, "SpaceAuditor")).Should(Exit(0))
   411  					env := map[string]string{
   412  						"CF_USERNAME": newUser,
   413  						"CF_PASSWORD": newPass,
   414  					}
   415  					Eventually(helpers.CFWithEnv(env, "auth")).Should(Exit(0))
   416  					helpers.TargetOrgAndSpace(orgName, spaceName)
   417  				})
   418  
   419  				AfterEach(func() {
   420  					helpers.LoginCF()
   421  				})
   422  
   423  				It("returns an error", func() {
   424  					session := helpers.CF("v3-ssh", appName)
   425  
   426  					Eventually(session.Err).Should(Say("Error opening SSH connection: You are not authorized to perform the requested action."))
   427  					Eventually(session).Should(Exit(1))
   428  				})
   429  			})
   430  		})
   431  	})
   432  })