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