github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+incompatible/cf/commands/application/ssh_test.go (about) 1 package application_test 2 3 import ( 4 "errors" 5 "net/http" 6 "net/http/httptest" 7 "time" 8 9 "code.cloudfoundry.org/cli/cf/api/apifakes" 10 "code.cloudfoundry.org/cli/cf/commandregistry" 11 "code.cloudfoundry.org/cli/cf/commands/commandsfakes" 12 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 13 "code.cloudfoundry.org/cli/cf/models" 14 "code.cloudfoundry.org/cli/cf/net" 15 "code.cloudfoundry.org/cli/cf/requirements" 16 "code.cloudfoundry.org/cli/cf/requirements/requirementsfakes" 17 "code.cloudfoundry.org/cli/cf/ssh/sshfakes" 18 testcmd "code.cloudfoundry.org/cli/testhelpers/commands" 19 testconfig "code.cloudfoundry.org/cli/testhelpers/configuration" 20 testnet "code.cloudfoundry.org/cli/testhelpers/net" 21 testterm "code.cloudfoundry.org/cli/testhelpers/terminal" 22 23 "code.cloudfoundry.org/cli/cf/trace/tracefakes" 24 . "code.cloudfoundry.org/cli/testhelpers/matchers" 25 . "github.com/onsi/ginkgo" 26 . "github.com/onsi/gomega" 27 ) 28 29 var _ = Describe("SSH command", func() { 30 var ( 31 ui *testterm.FakeUI 32 33 sshCodeGetter *commandsfakes.FakeSSHCodeGetter 34 originalSSHCodeGetter commandregistry.Command 35 36 requirementsFactory *requirementsfakes.FakeFactory 37 configRepo coreconfig.Repository 38 deps commandregistry.Dependency 39 ccGateway net.Gateway 40 41 fakeSecureShell *sshfakes.FakeSecureShell 42 ) 43 44 BeforeEach(func() { 45 ui = &testterm.FakeUI{} 46 configRepo = testconfig.NewRepositoryWithDefaults() 47 requirementsFactory = new(requirementsfakes.FakeFactory) 48 deps.Gateways = make(map[string]net.Gateway) 49 50 //save original command and restore later 51 originalSSHCodeGetter = commandregistry.Commands.FindCommand("ssh-code") 52 53 sshCodeGetter = new(commandsfakes.FakeSSHCodeGetter) 54 55 //setup fakes to correctly interact with commandregistry 56 sshCodeGetter.SetDependencyStub = func(_ commandregistry.Dependency, _ bool) commandregistry.Command { 57 return sshCodeGetter 58 } 59 sshCodeGetter.MetaDataReturns(commandregistry.CommandMetadata{Name: "ssh-code"}) 60 }) 61 62 AfterEach(func() { 63 //restore original command 64 commandregistry.Register(originalSSHCodeGetter) 65 }) 66 67 updateCommandDependency := func(pluginCall bool) { 68 deps.UI = ui 69 deps.Config = configRepo 70 71 //inject fake 'sshCodeGetter' into registry 72 commandregistry.Register(sshCodeGetter) 73 74 commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("ssh").SetDependency(deps, pluginCall)) 75 } 76 77 runCommand := func(args ...string) bool { 78 return testcmd.RunCLICommand("ssh", args, requirementsFactory, updateCommandDependency, false, ui) 79 } 80 81 Describe("Requirements", func() { 82 It("fails with usage when not provided exactly one arg", func() { 83 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 84 85 runCommand() 86 Expect(ui.Outputs()).To(ContainSubstrings( 87 []string{"Incorrect Usage", "Requires", "argument"}, 88 )) 89 90 }) 91 92 It("fails requirements when not logged in", func() { 93 requirementsFactory.NewLoginRequirementReturns(requirements.Failing{Message: "not logged in"}) 94 Expect(runCommand("my-app")).To(BeFalse()) 95 }) 96 97 It("fails if a space is not targeted", func() { 98 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 99 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Failing{Message: "not targeting space"}) 100 Expect(runCommand("my-app")).To(BeFalse()) 101 }) 102 103 It("fails if a application is not found", func() { 104 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 105 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Passing{}) 106 applicationReq := new(requirementsfakes.FakeApplicationRequirement) 107 applicationReq.ExecuteReturns(errors.New("no app")) 108 requirementsFactory.NewApplicationRequirementReturns(applicationReq) 109 110 Expect(runCommand("my-app")).To(BeFalse()) 111 }) 112 113 Describe("Flag options", func() { 114 var args []string 115 116 BeforeEach(func() { 117 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 118 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Passing{}) 119 }) 120 121 Context("when an -i flag is provided", func() { 122 BeforeEach(func() { 123 args = append(args, "app-name") 124 }) 125 126 Context("with a negative integer argument", func() { 127 BeforeEach(func() { 128 args = append(args, "-i", "-3") 129 }) 130 131 It("returns an error", func() { 132 Expect(runCommand(args...)).To(BeFalse()) 133 Expect(ui.Outputs()).To(ContainSubstrings( 134 []string{"Incorrect Usage", "cannot be negative"}, 135 )) 136 137 }) 138 }) 139 }) 140 }) 141 142 Describe("SSHOptions", func() { 143 Context("when an error is returned during initialization", func() { 144 It("shows error and prints command usage", func() { 145 Expect(runCommand("app_name", "-L", "[9999:localhost...")).To(BeFalse()) 146 Expect(ui.Outputs()).To(ContainSubstrings( 147 []string{"Incorrect Usage"}, 148 []string{"USAGE:"}, 149 )) 150 151 }) 152 }) 153 }) 154 155 }) 156 157 Describe("ssh", func() { 158 var ( 159 currentApp models.Application 160 ) 161 162 BeforeEach(func() { 163 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 164 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Passing{}) 165 currentApp = models.Application{} 166 currentApp.Name = "my-app" 167 currentApp.State = "started" 168 currentApp.GUID = "my-app-guid" 169 currentApp.EnableSSH = true 170 currentApp.Diego = true 171 172 applicationReq := new(requirementsfakes.FakeApplicationRequirement) 173 applicationReq.GetApplicationReturns(currentApp) 174 requirementsFactory.NewApplicationRequirementReturns(applicationReq) 175 }) 176 177 Describe("Error getting required info to run ssh", func() { 178 var ( 179 testServer *httptest.Server 180 handler *testnet.TestHandler 181 ) 182 183 AfterEach(func() { 184 testServer.Close() 185 }) 186 187 Context("error when getting SSH info from /v2/info", func() { 188 BeforeEach(func() { 189 getRequest := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 190 Method: "GET", 191 Path: "/v2/info", 192 Response: testnet.TestResponse{ 193 Status: http.StatusNotFound, 194 Body: `{}`, 195 }, 196 }) 197 198 testServer, handler = testnet.NewServer([]testnet.TestRequest{getRequest}) 199 configRepo.SetAPIEndpoint(testServer.URL) 200 ccGateway = net.NewCloudControllerGateway(configRepo, time.Now, &testterm.FakeUI{}, new(tracefakes.FakePrinter), "") 201 deps.Gateways["cloud-controller"] = ccGateway 202 }) 203 204 It("notifies users", func() { 205 runCommand("my-app") 206 207 Expect(handler).To(HaveAllRequestsCalled()) 208 Expect(ui.Outputs()).To(ContainSubstrings( 209 []string{"Error getting SSH info", "404"}, 210 )) 211 212 }) 213 }) 214 215 Context("error when getting oauth token", func() { 216 BeforeEach(func() { 217 sshCodeGetter.GetReturns("", errors.New("auth api error")) 218 219 getRequest := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 220 Method: "GET", 221 Path: "/v2/info", 222 Response: testnet.TestResponse{ 223 Status: http.StatusOK, 224 Body: `{}`, 225 }, 226 }) 227 228 testServer, handler = testnet.NewServer([]testnet.TestRequest{getRequest}) 229 configRepo.SetAPIEndpoint(testServer.URL) 230 ccGateway = net.NewCloudControllerGateway(configRepo, time.Now, &testterm.FakeUI{}, new(tracefakes.FakePrinter), "") 231 deps.Gateways["cloud-controller"] = ccGateway 232 }) 233 234 It("notifies users", func() { 235 runCommand("my-app") 236 237 Expect(handler).To(HaveAllRequestsCalled()) 238 Expect(ui.Outputs()).To(ContainSubstrings( 239 []string{"Error getting one time auth code", "auth api error"}, 240 )) 241 242 }) 243 }) 244 }) 245 246 Describe("Connecting to ssh server", func() { 247 var testServer *httptest.Server 248 249 AfterEach(func() { 250 testServer.Close() 251 }) 252 253 BeforeEach(func() { 254 fakeSecureShell = new(sshfakes.FakeSecureShell) 255 256 deps.WildcardDependency = fakeSecureShell 257 258 getRequest := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 259 Method: "GET", 260 Path: "/v2/info", 261 Response: testnet.TestResponse{ 262 Status: http.StatusOK, 263 Body: getInfoResponseBody, 264 }, 265 }) 266 267 testServer, _ = testnet.NewServer([]testnet.TestRequest{getRequest}) 268 configRepo.SetAPIEndpoint(testServer.URL) 269 ccGateway = net.NewCloudControllerGateway(configRepo, time.Now, &testterm.FakeUI{}, new(tracefakes.FakePrinter), "") 270 deps.Gateways["cloud-controller"] = ccGateway 271 }) 272 273 Context("Error when connecting", func() { 274 It("notifies users", func() { 275 fakeSecureShell.ConnectReturns(errors.New("dial errorrr")) 276 277 runCommand("my-app") 278 279 Expect(ui.Outputs()).To(ContainSubstrings( 280 []string{"Error opening SSH connection", "dial error"}, 281 )) 282 283 }) 284 }) 285 286 Context("Error port forwarding when -L is provided", func() { 287 It("notifies users", func() { 288 fakeSecureShell.LocalPortForwardReturns(errors.New("listen error")) 289 290 runCommand("my-app", "-L", "8000:localhost:8000") 291 292 Expect(ui.Outputs()).To(ContainSubstrings( 293 []string{"Error forwarding port", "listen error"}, 294 )) 295 296 }) 297 }) 298 299 Context("when -N is provided", func() { 300 It("calls secureShell.Wait()", func() { 301 fakeSecureShell.ConnectReturns(nil) 302 fakeSecureShell.LocalPortForwardReturns(nil) 303 304 runCommand("my-app", "-N") 305 306 Expect(fakeSecureShell.WaitCallCount()).To(Equal(1)) 307 }) 308 }) 309 310 Context("when -N is provided", func() { 311 It("calls secureShell.InteractiveSession()", func() { 312 fakeSecureShell.ConnectReturns(nil) 313 fakeSecureShell.LocalPortForwardReturns(nil) 314 315 runCommand("my-app", "-k") 316 317 Expect(fakeSecureShell.InteractiveSessionCallCount()).To(Equal(1)) 318 }) 319 }) 320 321 Context("when Wait() or InteractiveSession() returns error", func() { 322 323 It("notifities users", func() { 324 fakeSecureShell.ConnectReturns(nil) 325 fakeSecureShell.LocalPortForwardReturns(nil) 326 327 fakeSecureShell.InteractiveSessionReturns(errors.New("ssh exit error")) 328 runCommand("my-app", "-k") 329 330 Expect(ui.Outputs()).To(ContainSubstrings( 331 []string{"ssh exit error"}, 332 )) 333 334 }) 335 }) 336 }) 337 }) 338 }) 339 340 const getInfoResponseBody string = ` 341 { 342 "name": "vcap", 343 "build": "2222", 344 "support": "http://support.cloudfoundry.com", 345 "version": 2, 346 "description": "Cloud Foundry sponsored by ABC", 347 "authorization_endpoint": "https://login.run.abc.com", 348 "token_endpoint": "https://uaa.run.abc.com", 349 "min_cli_version": null, 350 "min_recommended_cli_version": null, 351 "api_version": "2.35.0", 352 "app_ssh_endpoint": "ssh.run.pivotal.io:2222", 353 "app_ssh_host_key_fingerprint": "11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11", 354 "logging_endpoint": "wss://loggregator.run.abc.com:443", 355 "doppler_logging_endpoint": "wss://doppler.run.abc.com:443", 356 "user": "6e477566-ac8d-4653-98c6-d319595ec7b0" 357 }`