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  }