github.com/swisscom/cloudfoundry-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 })