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 })