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  })