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