github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/commands/service/update_service_test.go (about) 1 package service_test 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 8 planbuilderfakes "code.cloudfoundry.org/cli/cf/actors/planbuilder/planbuilderfakes" 9 "code.cloudfoundry.org/cli/cf/api/apifakes" 10 "code.cloudfoundry.org/cli/cf/commandregistry" 11 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 12 "code.cloudfoundry.org/cli/cf/models" 13 "code.cloudfoundry.org/cli/cf/requirements" 14 "code.cloudfoundry.org/cli/cf/requirements/requirementsfakes" 15 testcmd "code.cloudfoundry.org/cli/util/testhelpers/commands" 16 testconfig "code.cloudfoundry.org/cli/util/testhelpers/configuration" 17 testterm "code.cloudfoundry.org/cli/util/testhelpers/terminal" 18 . "github.com/onsi/ginkgo" 19 . "github.com/onsi/gomega" 20 21 "code.cloudfoundry.org/cli/cf/commands/service" 22 "code.cloudfoundry.org/cli/cf/flags" 23 . "code.cloudfoundry.org/cli/util/testhelpers/matchers" 24 ) 25 26 var _ = Describe("update-service command", func() { 27 var ( 28 ui *testterm.FakeUI 29 config coreconfig.Repository 30 requirementsFactory *requirementsfakes.FakeFactory 31 serviceRepo *apifakes.FakeServiceRepository 32 planBuilder *planbuilderfakes.FakePlanBuilder 33 offering1 models.ServiceOffering 34 deps commandregistry.Dependency 35 ) 36 37 updateCommandDependency := func(pluginCall bool) { 38 deps.UI = ui 39 deps.RepoLocator = deps.RepoLocator.SetServiceRepository(serviceRepo) 40 deps.Config = config 41 deps.PlanBuilder = planBuilder 42 commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("update-service").SetDependency(deps, pluginCall)) 43 } 44 45 BeforeEach(func() { 46 ui = &testterm.FakeUI{} 47 48 config = testconfig.NewRepositoryWithDefaults() 49 50 requirementsFactory = new(requirementsfakes.FakeFactory) 51 requirementsFactory.NewLoginRequirementReturns(requirements.Passing{}) 52 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Passing{}) 53 requirementsFactory.NewMinAPIVersionRequirementReturns(requirements.Passing{Type: "minAPIVersionReq"}) 54 55 serviceRepo = new(apifakes.FakeServiceRepository) 56 planBuilder = new(planbuilderfakes.FakePlanBuilder) 57 58 offering1 = models.ServiceOffering{} 59 offering1.Label = "cleardb" 60 offering1.Plans = []models.ServicePlanFields{{ 61 Name: "spark", 62 GUID: "cleardb-spark-guid", 63 }, { 64 Name: "flare", 65 GUID: "cleardb-flare-guid", 66 }, 67 } 68 69 }) 70 71 var callUpdateService = func(args []string) bool { 72 return testcmd.RunCLICommand("update-service", args, requirementsFactory, updateCommandDependency, false, ui) 73 } 74 75 Describe("requirements", func() { 76 It("passes when logged in and a space is targeted", func() { 77 Expect(callUpdateService([]string{"cleardb"})).To(BeTrue()) 78 }) 79 80 It("fails with usage when not provided exactly one arg", func() { 81 Expect(callUpdateService([]string{})).To(BeFalse()) 82 }) 83 84 It("fails when not logged in", func() { 85 requirementsFactory.NewLoginRequirementReturns(requirements.Failing{Message: "not logged in"}) 86 Expect(callUpdateService([]string{"cleardb", "spark", "my-cleardb-service"})).To(BeFalse()) 87 }) 88 89 It("fails when a space is not targeted", func() { 90 requirementsFactory.NewTargetedSpaceRequirementReturns(requirements.Failing{Message: "not targeting space"}) 91 Expect(callUpdateService([]string{"cleardb", "spark", "my-cleardb-service"})).To(BeFalse()) 92 }) 93 94 Context("-p", func() { 95 It("when provided, requires a CC API version > cf.UpdateServicePlanMinimumAPIVersion", func() { 96 cmd := &service.UpdateService{} 97 98 fc := flags.NewFlagContext(cmd.MetaData().Flags) 99 fc.Parse("potato", "-p", "plan-name") 100 101 reqs, err := cmd.Requirements(requirementsFactory, fc) 102 Expect(err).NotTo(HaveOccurred()) 103 Expect(reqs).NotTo(BeEmpty()) 104 105 Expect(reqs).To(ContainElement(requirements.Passing{Type: "minAPIVersionReq"})) 106 }) 107 108 It("does not requirue a CC Api Version if not provided", func() { 109 cmd := &service.UpdateService{} 110 111 fc := flags.NewFlagContext(cmd.MetaData().Flags) 112 fc.Parse("potato") 113 114 reqs, err := cmd.Requirements(requirementsFactory, fc) 115 Expect(err).NotTo(HaveOccurred()) 116 Expect(reqs).NotTo(BeEmpty()) 117 118 Expect(reqs).NotTo(ContainElement(requirements.Passing{Type: "minAPIVersionReq"})) 119 }) 120 }) 121 }) 122 123 Context("when no flags are passed", func() { 124 125 Context("when the instance exists", func() { 126 It("prints a user indicating it is a no-op", func() { 127 callUpdateService([]string{"my-service"}) 128 129 Expect(ui.Outputs()).To(ContainSubstrings( 130 []string{"OK"}, 131 []string{"No changes were made"}, 132 )) 133 }) 134 }) 135 }) 136 137 Context("when passing arbitrary params", func() { 138 BeforeEach(func() { 139 serviceInstance := models.ServiceInstance{ 140 ServiceInstanceFields: models.ServiceInstanceFields{ 141 Name: "my-service-instance", 142 GUID: "my-service-instance-guid", 143 LastOperation: models.LastOperationFields{ 144 Type: "update", 145 State: "in progress", 146 Description: "fake service instance description", 147 }, 148 }, 149 ServiceOffering: models.ServiceOfferingFields{ 150 Label: "murkydb", 151 GUID: "murkydb-guid", 152 }, 153 } 154 155 servicePlans := []models.ServicePlanFields{{ 156 Name: "spark", 157 GUID: "murkydb-spark-guid", 158 }, { 159 Name: "flare", 160 GUID: "murkydb-flare-guid", 161 }, 162 } 163 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 164 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 165 }) 166 167 Context("as a json string", func() { 168 It("successfully updates a service", func() { 169 callUpdateService([]string{"-p", "flare", "-c", `{"foo": "bar"}`, "my-service-instance"}) 170 171 Expect(ui.Outputs()).To(ContainSubstrings( 172 []string{"Updating service", "my-service", "as", "my-user", "..."}, 173 []string{"OK"}, 174 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 175 )) 176 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 177 178 instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 179 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 180 Expect(planGUID).To(Equal("murkydb-flare-guid")) 181 Expect(params).To(Equal(map[string]interface{}{"foo": "bar"})) 182 }) 183 184 Context("that are not valid json", func() { 185 It("returns an error to the UI", func() { 186 callUpdateService([]string{"-p", "flare", "-c", `bad-json`, "my-service-instance"}) 187 188 Expect(ui.Outputs()).To(ContainSubstrings( 189 []string{"FAILED"}, 190 []string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."}, 191 )) 192 }) 193 }) 194 }) 195 196 Context("as a file that contains json", func() { 197 var jsonFile *os.File 198 var params string 199 200 BeforeEach(func() { 201 params = "{\"foo\": \"bar\"}" 202 }) 203 204 AfterEach(func() { 205 if jsonFile != nil { 206 jsonFile.Close() 207 os.Remove(jsonFile.Name()) 208 } 209 }) 210 211 JustBeforeEach(func() { 212 var err error 213 jsonFile, err = ioutil.TempFile("", "") 214 Expect(err).ToNot(HaveOccurred()) 215 216 err = ioutil.WriteFile(jsonFile.Name(), []byte(params), os.ModePerm) 217 Expect(err).NotTo(HaveOccurred()) 218 }) 219 220 It("successfully updates a service and passes the params as a json", func() { 221 callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"}) 222 223 Expect(ui.Outputs()).To(ContainSubstrings( 224 []string{"Updating service", "my-service", "as", "my-user", "..."}, 225 []string{"OK"}, 226 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 227 )) 228 229 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 230 231 instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 232 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 233 Expect(planGUID).To(Equal("murkydb-flare-guid")) 234 Expect(params).To(Equal(map[string]interface{}{"foo": "bar"})) 235 }) 236 237 Context("that are not valid json", func() { 238 BeforeEach(func() { 239 params = "bad-json" 240 }) 241 242 It("returns an error to the UI", func() { 243 callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"}) 244 245 Expect(ui.Outputs()).To(ContainSubstrings( 246 []string{"FAILED"}, 247 []string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."}, 248 )) 249 }) 250 }) 251 }) 252 }) 253 254 Context("when passing in tags", func() { 255 It("successfully updates a service and passes the tags as json", func() { 256 callUpdateService([]string{"-t", "tag1, tag2,tag3, tag4", "my-service-instance"}) 257 258 Expect(ui.Outputs()).To(ContainSubstrings( 259 []string{"Updating service instance", "my-service-instance"}, 260 []string{"OK"}, 261 )) 262 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 263 Expect(tags).To(ConsistOf("tag1", "tag2", "tag3", "tag4")) 264 }) 265 266 It("successfully updates a service and passes the tags as json", func() { 267 callUpdateService([]string{"-t", "tag1", "my-service-instance"}) 268 269 Expect(ui.Outputs()).To(ContainSubstrings( 270 []string{"Updating service instance", "my-service-instance"}, 271 []string{"OK"}, 272 )) 273 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 274 Expect(tags).To(ConsistOf("tag1")) 275 }) 276 277 Context("and the tags string is passed with an empty string", func() { 278 It("successfully updates the service", func() { 279 callUpdateService([]string{"-t", "", "my-service-instance"}) 280 281 Expect(ui.Outputs()).To(ContainSubstrings( 282 []string{"Updating service instance", "my-service-instance"}, 283 []string{"OK"}, 284 )) 285 _, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0) 286 Expect(tags).To(Equal([]string{})) 287 }) 288 }) 289 }) 290 291 Context("when service update is asynchronous", func() { 292 Context("when the plan flag is passed", func() { 293 BeforeEach(func() { 294 serviceInstance := models.ServiceInstance{ 295 ServiceInstanceFields: models.ServiceInstanceFields{ 296 Name: "my-service-instance", 297 GUID: "my-service-instance-guid", 298 LastOperation: models.LastOperationFields{ 299 Type: "update", 300 State: "in progress", 301 Description: "fake service instance description", 302 }, 303 }, 304 ServiceOffering: models.ServiceOfferingFields{ 305 Label: "murkydb", 306 GUID: "murkydb-guid", 307 }, 308 } 309 310 servicePlans := []models.ServicePlanFields{{ 311 Name: "spark", 312 GUID: "murkydb-spark-guid", 313 }, { 314 Name: "flare", 315 GUID: "murkydb-flare-guid", 316 }, 317 } 318 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 319 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 320 }) 321 322 It("successfully updates a service", func() { 323 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 324 325 Expect(ui.Outputs()).To(ContainSubstrings( 326 []string{"Updating service", "my-service", "as", "my-user", "..."}, 327 []string{"OK"}, 328 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 329 )) 330 331 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 332 333 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 334 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 335 Expect(planGUID).To(Equal("murkydb-flare-guid")) 336 }) 337 338 It("successfully updates a service", func() { 339 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 340 341 Expect(ui.Outputs()).To(ContainSubstrings( 342 []string{"Updating service", "my-service", "as", "my-user", "..."}, 343 []string{"OK"}, 344 []string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."}, 345 )) 346 347 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 348 349 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 350 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 351 Expect(planGUID).To(Equal("murkydb-flare-guid")) 352 }) 353 354 Context("when there is an err finding the instance", func() { 355 It("returns an error", func() { 356 serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance")) 357 358 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 359 360 Expect(ui.Outputs()).To(ContainSubstrings( 361 []string{"Error finding instance"}, 362 []string{"FAILED"}, 363 )) 364 }) 365 }) 366 Context("when there is an err finding service plans", func() { 367 It("returns an error", func() { 368 planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans")) 369 370 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 371 372 Expect(ui.Outputs()).To(ContainSubstrings( 373 []string{"Error fetching plans"}, 374 []string{"FAILED"}, 375 )) 376 }) 377 }) 378 Context("when the plan specified does not exist in the service offering", func() { 379 It("returns an error", func() { 380 callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"}) 381 382 Expect(ui.Outputs()).To(ContainSubstrings( 383 []string{"Plan does not exist for the murkydb service"}, 384 []string{"FAILED"}, 385 )) 386 }) 387 }) 388 Context("when there is an error updating the service instance", func() { 389 It("returns an error", func() { 390 serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance")) 391 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 392 393 Expect(ui.Outputs()).To(ContainSubstrings( 394 []string{"Error updating service instance"}, 395 []string{"FAILED"}, 396 )) 397 }) 398 }) 399 }) 400 }) 401 402 Context("when service update is synchronous", func() { 403 Context("when the plan flag is passed", func() { 404 BeforeEach(func() { 405 serviceInstance := models.ServiceInstance{ 406 ServiceInstanceFields: models.ServiceInstanceFields{ 407 Name: "my-service-instance", 408 GUID: "my-service-instance-guid", 409 }, 410 ServiceOffering: models.ServiceOfferingFields{ 411 Label: "murkydb", 412 GUID: "murkydb-guid", 413 }, 414 } 415 416 servicePlans := []models.ServicePlanFields{{ 417 Name: "spark", 418 GUID: "murkydb-spark-guid", 419 }, { 420 Name: "flare", 421 GUID: "murkydb-flare-guid", 422 }, 423 } 424 serviceRepo.FindInstanceByNameReturns(serviceInstance, nil) 425 planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil) 426 427 }) 428 It("successfully updates a service", func() { 429 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 430 431 Expect(ui.Outputs()).To(ContainSubstrings( 432 []string{"Updating service", "my-service", "as", "my-user", "..."}, 433 []string{"OK"}, 434 )) 435 Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance")) 436 serviceGUID, orgName := planBuilder.GetPlansForServiceForOrgArgsForCall(0) 437 Expect(serviceGUID).To(Equal("murkydb-guid")) 438 Expect(orgName).To(Equal("my-org")) 439 440 instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0) 441 Expect(instanceGUID).To(Equal("my-service-instance-guid")) 442 Expect(planGUID).To(Equal("murkydb-flare-guid")) 443 }) 444 445 Context("when there is an err finding the instance", func() { 446 It("returns an error", func() { 447 serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance")) 448 449 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 450 451 Expect(ui.Outputs()).To(ContainSubstrings( 452 []string{"Error finding instance"}, 453 []string{"FAILED"}, 454 )) 455 }) 456 }) 457 Context("when there is an err finding service plans", func() { 458 It("returns an error", func() { 459 planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans")) 460 461 callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"}) 462 463 Expect(ui.Outputs()).To(ContainSubstrings( 464 []string{"Error fetching plans"}, 465 []string{"FAILED"}, 466 )) 467 }) 468 }) 469 Context("when the plan specified does not exist in the service offering", func() { 470 It("returns an error", func() { 471 callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"}) 472 473 Expect(ui.Outputs()).To(ContainSubstrings( 474 []string{"Plan does not exist for the murkydb service"}, 475 []string{"FAILED"}, 476 )) 477 }) 478 }) 479 Context("when there is an error updating the service instance", func() { 480 It("returns an error", func() { 481 serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance")) 482 callUpdateService([]string{"-p", "flare", "my-service-instance"}) 483 484 Expect(ui.Outputs()).To(ContainSubstrings( 485 []string{"Error updating service instance"}, 486 []string{"FAILED"}, 487 )) 488 }) 489 }) 490 }) 491 492 }) 493 })