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