github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/api/applications/applications_test.go (about) 1 package applications_test 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "net/http/httptest" 7 "time" 8 9 "code.cloudfoundry.org/cli/cf/api/apifakes" 10 "code.cloudfoundry.org/cli/cf/errors" 11 "code.cloudfoundry.org/cli/cf/models" 12 "code.cloudfoundry.org/cli/cf/net" 13 "code.cloudfoundry.org/cli/cf/terminal/terminalfakes" 14 testconfig "code.cloudfoundry.org/cli/util/testhelpers/configuration" 15 testnet "code.cloudfoundry.org/cli/util/testhelpers/net" 16 17 . "code.cloudfoundry.org/cli/cf/api/applications" 18 "code.cloudfoundry.org/cli/cf/trace/tracefakes" 19 . "code.cloudfoundry.org/cli/util/testhelpers/matchers" 20 . "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 "github.com/onsi/gomega/ghttp" 23 ) 24 25 var _ = Describe("ApplicationsRepository", func() { 26 Describe("finding apps by name", func() { 27 It("returns the app when it is found", func() { 28 ts, handler, repo := createAppRepo([]testnet.TestRequest{findAppRequest}) 29 defer ts.Close() 30 31 app, apiErr := repo.Read("My App") 32 Expect(handler).To(HaveAllRequestsCalled()) 33 Expect(apiErr).NotTo(HaveOccurred()) 34 Expect(app.Name).To(Equal("My App")) 35 Expect(app.GUID).To(Equal("app1-guid")) 36 Expect(app.Memory).To(Equal(int64(128))) 37 Expect(app.DiskQuota).To(Equal(int64(512))) 38 Expect(app.InstanceCount).To(Equal(1)) 39 Expect(app.EnvironmentVars).To(Equal(map[string]interface{}{"foo": "bar", "baz": "boom"})) 40 Expect(app.Routes[0].Host).To(Equal("app1")) 41 Expect(app.Routes[0].Domain.Name).To(Equal("cfapps.io")) 42 Expect(app.Stack.Name).To(Equal("awesome-stacks-ahoy")) 43 }) 44 45 It("returns a failure response when the app is not found", func() { 46 request := apifakes.NewCloudControllerTestRequest(findAppRequest) 47 request.Response = testnet.TestResponse{Status: http.StatusOK, Body: `{"resources": []}`} 48 49 ts, handler, repo := createAppRepo([]testnet.TestRequest{request}) 50 defer ts.Close() 51 52 _, apiErr := repo.Read("My App") 53 Expect(handler).To(HaveAllRequestsCalled()) 54 Expect(apiErr.(*errors.ModelNotFoundError)).NotTo(BeNil()) 55 }) 56 }) 57 58 Describe(".GetApp", func() { 59 It("returns an application using the given app guid", func() { 60 request := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 61 Method: "GET", 62 Path: "/v2/apps/app-guid", 63 Response: appModelResponse, 64 }) 65 ts, handler, repo := createAppRepo([]testnet.TestRequest{request}) 66 defer ts.Close() 67 app, err := repo.GetApp("app-guid") 68 69 Expect(err).ToNot(HaveOccurred()) 70 Expect(handler).To(HaveAllRequestsCalled()) 71 Expect(app.Name).To(Equal("My App")) 72 }) 73 }) 74 75 Describe(".ReadFromSpace", func() { 76 It("returns an application using the given space guid", func() { 77 request := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 78 Method: "GET", 79 Path: "/v2/spaces/another-space-guid/apps?q=name%3AMy+App&inline-relations-depth=1", 80 Response: singleAppResponse, 81 }) 82 ts, handler, repo := createAppRepo([]testnet.TestRequest{request}) 83 defer ts.Close() 84 app, err := repo.ReadFromSpace("My App", "another-space-guid") 85 86 Expect(err).ToNot(HaveOccurred()) 87 Expect(handler).To(HaveAllRequestsCalled()) 88 Expect(app.Name).To(Equal("My App")) 89 }) 90 }) 91 92 Describe("Create", func() { 93 var ( 94 ccServer *ghttp.Server 95 repo CloudControllerRepository 96 appParams models.AppParams 97 ) 98 99 BeforeEach(func() { 100 ccServer = ghttp.NewServer() 101 configRepo := testconfig.NewRepositoryWithDefaults() 102 configRepo.SetAPIEndpoint(ccServer.URL()) 103 gateway := net.NewCloudControllerGateway(configRepo, time.Now, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 104 repo = NewCloudControllerRepository(configRepo, gateway) 105 106 name := "my-cool-app" 107 buildpackURL := "buildpack-url" 108 spaceGUID := "some-space-guid" 109 stackGUID := "some-stack-guid" 110 command := "some-command" 111 memory := int64(2048) 112 diskQuota := int64(512) 113 instanceCount := 3 114 115 appParams = models.AppParams{ 116 Name: &name, 117 BuildpackURL: &buildpackURL, 118 SpaceGUID: &spaceGUID, 119 StackGUID: &stackGUID, 120 Command: &command, 121 Memory: &memory, 122 DiskQuota: &diskQuota, 123 InstanceCount: &instanceCount, 124 } 125 126 ccServer.AppendHandlers( 127 ghttp.CombineHandlers( 128 ghttp.VerifyRequest("POST", "/v2/apps"), 129 ghttp.VerifyJSON(`{ 130 "name":"my-cool-app", 131 "instances":3, 132 "buildpack":"buildpack-url", 133 "memory":2048, 134 "disk_quota": 512, 135 "space_guid":"some-space-guid", 136 "stack_guid":"some-stack-guid", 137 "command":"some-command" 138 }`), 139 ), 140 ) 141 }) 142 143 AfterEach(func() { 144 ccServer.Close() 145 }) 146 147 It("tries to create the app", func() { 148 repo.Create(appParams) 149 Expect(ccServer.ReceivedRequests()).To(HaveLen(1)) 150 }) 151 152 Context("when the create succeeds", func() { 153 BeforeEach(func() { 154 h := ccServer.GetHandler(0) 155 ccServer.SetHandler(0, 156 ghttp.CombineHandlers( 157 h, 158 ghttp.RespondWith(http.StatusCreated, `{ 159 "metadata": { 160 "guid": "my-cool-app-guid" 161 }, 162 "entity": { 163 "name": "my-cool-app" 164 } 165 }`), 166 ), 167 ) 168 }) 169 170 It("returns the application", func() { 171 createdApp, err := repo.Create(appParams) 172 Expect(err).NotTo(HaveOccurred()) 173 174 app := models.Application{} 175 app.Name = "my-cool-app" 176 app.GUID = "my-cool-app-guid" 177 Expect(createdApp).To(Equal(app)) 178 }) 179 }) 180 181 Context("when the create fails", func() { 182 BeforeEach(func() { 183 h := ccServer.GetHandler(0) 184 ccServer.SetHandler(0, 185 ghttp.CombineHandlers( 186 h, 187 ghttp.RespondWith(http.StatusInternalServerError, ""), 188 ), 189 ) 190 }) 191 192 It("returns an error", func() { 193 _, err := repo.Create(appParams) 194 Expect(err).To(HaveOccurred()) 195 }) 196 }) 197 }) 198 199 Describe("reading environment for an app", func() { 200 Context("when the response can be parsed as json", func() { 201 var ( 202 testServer *httptest.Server 203 userEnv *models.Environment 204 err error 205 handler *testnet.TestHandler 206 repo Repository 207 ) 208 209 AfterEach(func() { 210 testServer.Close() 211 }) 212 213 Context("when there are system provided env vars", func() { 214 BeforeEach(func() { 215 216 var appEnvRequest = apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 217 Method: "GET", 218 Path: "/v2/apps/some-cool-app-guid/env", 219 Response: testnet.TestResponse{ 220 Status: http.StatusOK, 221 Body: ` 222 { 223 "staging_env_json": { 224 "STAGING_ENV": "staging_value", 225 "staging": true, 226 "number": 42 227 }, 228 "running_env_json": { 229 "RUNNING_ENV": "running_value", 230 "running": false, 231 "number": 37 232 }, 233 "environment_json": { 234 "key": "value", 235 "number": 123, 236 "bool": true 237 }, 238 "system_env_json": { 239 "VCAP_SERVICES": { 240 "system_hash": { 241 "system_key": "system_value" 242 } 243 } 244 } 245 } 246 `, 247 }}) 248 249 testServer, handler, repo = createAppRepo([]testnet.TestRequest{appEnvRequest}) 250 userEnv, err = repo.ReadEnv("some-cool-app-guid") 251 Expect(err).ToNot(HaveOccurred()) 252 Expect(handler).To(HaveAllRequestsCalled()) 253 }) 254 255 It("returns the user environment, vcap services, running/staging env variables", func() { 256 Expect(userEnv.Environment["key"]).To(Equal("value")) 257 Expect(userEnv.Environment["number"]).To(Equal(float64(123))) 258 Expect(userEnv.Environment["bool"]).To(BeTrue()) 259 Expect(userEnv.Running["RUNNING_ENV"]).To(Equal("running_value")) 260 Expect(userEnv.Running["running"]).To(BeFalse()) 261 Expect(userEnv.Running["number"]).To(Equal(float64(37))) 262 Expect(userEnv.Staging["STAGING_ENV"]).To(Equal("staging_value")) 263 Expect(userEnv.Staging["staging"]).To(BeTrue()) 264 Expect(userEnv.Staging["number"]).To(Equal(float64(42))) 265 266 vcapServices := userEnv.System["VCAP_SERVICES"] 267 data, err := json.Marshal(vcapServices) 268 269 Expect(err).ToNot(HaveOccurred()) 270 Expect(string(data)).To(ContainSubstring("\"system_key\":\"system_value\"")) 271 }) 272 273 }) 274 275 Context("when there are no environment variables", func() { 276 BeforeEach(func() { 277 var emptyEnvRequest = apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 278 Method: "GET", 279 Path: "/v2/apps/some-cool-app-guid/env", 280 Response: testnet.TestResponse{ 281 Status: http.StatusOK, 282 Body: `{"system_env_json": {"VCAP_SERVICES": {} }}`, 283 }}) 284 285 testServer, handler, repo = createAppRepo([]testnet.TestRequest{emptyEnvRequest}) 286 userEnv, err = repo.ReadEnv("some-cool-app-guid") 287 Expect(err).ToNot(HaveOccurred()) 288 Expect(handler).To(HaveAllRequestsCalled()) 289 }) 290 291 It("returns an empty string", func() { 292 Expect(len(userEnv.Environment)).To(Equal(0)) 293 Expect(len(userEnv.System["VCAP_SERVICES"].(map[string]interface{}))).To(Equal(0)) 294 }) 295 }) 296 }) 297 }) 298 299 Describe("restaging applications", func() { 300 It("POSTs to the right URL", func() { 301 appRestageRequest := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 302 Method: "POST", 303 Path: "/v2/apps/some-cool-app-guid/restage", 304 Response: testnet.TestResponse{ 305 Status: http.StatusOK, 306 Body: "", 307 }, 308 }) 309 310 ts, handler, repo := createAppRepo([]testnet.TestRequest{appRestageRequest}) 311 defer ts.Close() 312 313 repo.CreateRestageRequest("some-cool-app-guid") 314 Expect(handler).To(HaveAllRequestsCalled()) 315 }) 316 }) 317 318 Describe("updating applications", func() { 319 It("makes the right request", func() { 320 ts, handler, repo := createAppRepo([]testnet.TestRequest{updateApplicationRequest}) 321 defer ts.Close() 322 323 app := models.Application{} 324 app.GUID = "my-app-guid" 325 app.Name = "my-cool-app" 326 app.BuildpackURL = "buildpack-url" 327 app.Command = "some-command" 328 app.HealthCheckType = "none" 329 app.Memory = 2048 330 app.InstanceCount = 3 331 app.Stack = &models.Stack{GUID: "some-stack-guid"} 332 app.SpaceGUID = "some-space-guid" 333 app.State = "started" 334 app.DiskQuota = 512 335 Expect(app.EnvironmentVars).To(BeNil()) 336 337 updatedApp, apiErr := repo.Update(app.GUID, app.ToParams()) 338 339 Expect(handler).To(HaveAllRequestsCalled()) 340 Expect(apiErr).NotTo(HaveOccurred()) 341 Expect(updatedApp.Command).To(Equal("some-command")) 342 Expect(updatedApp.DetectedStartCommand).To(Equal("detected command")) 343 Expect(updatedApp.Name).To(Equal("my-cool-app")) 344 Expect(updatedApp.GUID).To(Equal("my-cool-app-guid")) 345 }) 346 347 It("sets environment variables", func() { 348 request := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 349 Method: "PUT", 350 Path: "/v2/apps/app1-guid", 351 Matcher: testnet.RequestBodyMatcher(`{"environment_json":{"DATABASE_URL":"mysql://example.com/my-db"}}`), 352 Response: testnet.TestResponse{Status: http.StatusCreated}, 353 }) 354 355 ts, handler, repo := createAppRepo([]testnet.TestRequest{request}) 356 defer ts.Close() 357 358 envParams := map[string]interface{}{"DATABASE_URL": "mysql://example.com/my-db"} 359 params := models.AppParams{EnvironmentVars: &envParams} 360 361 _, apiErr := repo.Update("app1-guid", params) 362 363 Expect(handler).To(HaveAllRequestsCalled()) 364 Expect(apiErr).NotTo(HaveOccurred()) 365 }) 366 367 It("can remove environment variables", func() { 368 request := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 369 Method: "PUT", 370 Path: "/v2/apps/app1-guid", 371 Matcher: testnet.RequestBodyMatcher(`{"environment_json":{}}`), 372 Response: testnet.TestResponse{Status: http.StatusCreated}, 373 }) 374 375 ts, handler, repo := createAppRepo([]testnet.TestRequest{request}) 376 defer ts.Close() 377 378 envParams := map[string]interface{}{} 379 params := models.AppParams{EnvironmentVars: &envParams} 380 381 _, apiErr := repo.Update("app1-guid", params) 382 383 Expect(handler).To(HaveAllRequestsCalled()) 384 Expect(apiErr).NotTo(HaveOccurred()) 385 }) 386 }) 387 388 It("deletes applications", func() { 389 deleteApplicationRequest := apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 390 Method: "DELETE", 391 Path: "/v2/apps/my-cool-app-guid?recursive=true", 392 Response: testnet.TestResponse{Status: http.StatusOK, Body: ""}, 393 }) 394 395 ts, handler, repo := createAppRepo([]testnet.TestRequest{deleteApplicationRequest}) 396 defer ts.Close() 397 398 apiErr := repo.Delete("my-cool-app-guid") 399 Expect(handler).To(HaveAllRequestsCalled()) 400 Expect(apiErr).NotTo(HaveOccurred()) 401 }) 402 }) 403 404 var appModelResponse = testnet.TestResponse{ 405 Status: http.StatusOK, 406 Body: ` 407 { 408 "metadata": { 409 "guid": "app1-guid" 410 }, 411 "entity": { 412 "name": "My App", 413 "environment_json": { 414 "foo": "bar", 415 "baz": "boom" 416 }, 417 "memory": 128, 418 "instances": 1, 419 "disk_quota": 512, 420 "state": "STOPPED", 421 "stack": { 422 "metadata": { 423 "guid": "app1-route-guid" 424 }, 425 "entity": { 426 "name": "awesome-stacks-ahoy" 427 } 428 }, 429 "routes": [ 430 { 431 "metadata": { 432 "guid": "app1-route-guid" 433 }, 434 "entity": { 435 "host": "app1", 436 "domain": { 437 "metadata": { 438 "guid": "domain1-guid" 439 }, 440 "entity": { 441 "name": "cfapps.io" 442 } 443 } 444 } 445 } 446 ] 447 } 448 } 449 `} 450 451 var singleAppResponse = testnet.TestResponse{ 452 Status: http.StatusOK, 453 Body: ` 454 { 455 "resources": [ 456 { 457 "metadata": { 458 "guid": "app1-guid" 459 }, 460 "entity": { 461 "name": "My App", 462 "environment_json": { 463 "foo": "bar", 464 "baz": "boom" 465 }, 466 "memory": 128, 467 "instances": 1, 468 "disk_quota": 512, 469 "state": "STOPPED", 470 "stack": { 471 "metadata": { 472 "guid": "app1-route-guid" 473 }, 474 "entity": { 475 "name": "awesome-stacks-ahoy" 476 } 477 }, 478 "routes": [ 479 { 480 "metadata": { 481 "guid": "app1-route-guid" 482 }, 483 "entity": { 484 "host": "app1", 485 "domain": { 486 "metadata": { 487 "guid": "domain1-guid" 488 }, 489 "entity": { 490 "name": "cfapps.io" 491 } 492 } 493 } 494 } 495 ] 496 } 497 } 498 ] 499 }`} 500 501 var findAppRequest = apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 502 Method: "GET", 503 Path: "/v2/spaces/my-space-guid/apps?q=name%3AMy+App&inline-relations-depth=1", 504 Response: singleAppResponse, 505 }) 506 507 var createApplicationResponse = ` 508 { 509 "metadata": { 510 "guid": "my-cool-app-guid" 511 }, 512 "entity": { 513 "name": "my-cool-app" 514 } 515 }` 516 517 var updateApplicationResponse = ` 518 { 519 "metadata": { 520 "guid": "my-cool-app-guid" 521 }, 522 "entity": { 523 "name": "my-cool-app", 524 "command": "some-command", 525 "detected_start_command": "detected command" 526 } 527 }` 528 529 var updateApplicationRequest = apifakes.NewCloudControllerTestRequest(testnet.TestRequest{ 530 Method: "PUT", 531 Path: "/v2/apps/my-app-guid?inline-relations-depth=1", 532 Matcher: testnet.RequestBodyMatcher(`{ 533 "name":"my-cool-app", 534 "instances":3, 535 "buildpack":"buildpack-url", 536 "docker_image":"", 537 "memory":2048, 538 "health_check_type":"none", 539 "health_check_http_endpoint":"", 540 "disk_quota":512, 541 "space_guid":"some-space-guid", 542 "state":"STARTED", 543 "stack_guid":"some-stack-guid", 544 "command":"some-command" 545 }`), 546 Response: testnet.TestResponse{ 547 Status: http.StatusOK, 548 Body: updateApplicationResponse}, 549 }) 550 551 func createAppRepo(requests []testnet.TestRequest) (ts *httptest.Server, handler *testnet.TestHandler, repo Repository) { 552 ts, handler = testnet.NewServer(requests) 553 configRepo := testconfig.NewRepositoryWithDefaults() 554 configRepo.SetAPIEndpoint(ts.URL) 555 gateway := net.NewCloudControllerGateway(configRepo, time.Now, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 556 repo = NewCloudControllerRepository(configRepo, gateway) 557 return 558 }