github.com/arunkumar7540/cli@v6.45.0+incompatible/integration/v7/isolated/ssh_command_test.go (about)

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