github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+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  
    95  		Context("when the instance exists", func() {
    96  			It("prints a user indicating it is a no-op", func() {
    97  				callUpdateService([]string{"my-service"})
    98  
    99  				Expect(ui.Outputs()).To(ContainSubstrings(
   100  					[]string{"OK"},
   101  					[]string{"No changes were made"},
   102  				))
   103  			})
   104  		})
   105  	})
   106  
   107  	Context("when passing arbitrary params", 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  			}
   124  
   125  			servicePlans := []models.ServicePlanFields{{
   126  				Name: "spark",
   127  				GUID: "murkydb-spark-guid",
   128  			}, {
   129  				Name: "flare",
   130  				GUID: "murkydb-flare-guid",
   131  			},
   132  			}
   133  			serviceRepo.FindInstanceByNameReturns(serviceInstance, nil)
   134  			planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil)
   135  		})
   136  
   137  		Context("as a json string", func() {
   138  			It("successfully updates a service", func() {
   139  				callUpdateService([]string{"-p", "flare", "-c", `{"foo": "bar"}`, "my-service-instance"})
   140  
   141  				Expect(ui.Outputs()).To(ContainSubstrings(
   142  					[]string{"Updating service", "my-service", "as", "my-user", "..."},
   143  					[]string{"OK"},
   144  					[]string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."},
   145  				))
   146  				Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance"))
   147  
   148  				instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   149  				Expect(instanceGUID).To(Equal("my-service-instance-guid"))
   150  				Expect(planGUID).To(Equal("murkydb-flare-guid"))
   151  				Expect(params).To(Equal(map[string]interface{}{"foo": "bar"}))
   152  			})
   153  
   154  			Context("that are not valid json", func() {
   155  				It("returns an error to the UI", func() {
   156  					callUpdateService([]string{"-p", "flare", "-c", `bad-json`, "my-service-instance"})
   157  
   158  					Expect(ui.Outputs()).To(ContainSubstrings(
   159  						[]string{"FAILED"},
   160  						[]string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."},
   161  					))
   162  				})
   163  			})
   164  		})
   165  
   166  		Context("as a file that contains json", func() {
   167  			var jsonFile *os.File
   168  			var params string
   169  
   170  			BeforeEach(func() {
   171  				params = "{\"foo\": \"bar\"}"
   172  			})
   173  
   174  			AfterEach(func() {
   175  				if jsonFile != nil {
   176  					jsonFile.Close()
   177  					os.Remove(jsonFile.Name())
   178  				}
   179  			})
   180  
   181  			JustBeforeEach(func() {
   182  				var err error
   183  				jsonFile, err = ioutil.TempFile("", "")
   184  				Expect(err).ToNot(HaveOccurred())
   185  
   186  				err = ioutil.WriteFile(jsonFile.Name(), []byte(params), os.ModePerm)
   187  				Expect(err).NotTo(HaveOccurred())
   188  			})
   189  
   190  			It("successfully updates a service and passes the params as a json", func() {
   191  				callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"})
   192  
   193  				Expect(ui.Outputs()).To(ContainSubstrings(
   194  					[]string{"Updating service", "my-service", "as", "my-user", "..."},
   195  					[]string{"OK"},
   196  					[]string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."},
   197  				))
   198  
   199  				Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance"))
   200  
   201  				instanceGUID, planGUID, params, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   202  				Expect(instanceGUID).To(Equal("my-service-instance-guid"))
   203  				Expect(planGUID).To(Equal("murkydb-flare-guid"))
   204  				Expect(params).To(Equal(map[string]interface{}{"foo": "bar"}))
   205  			})
   206  
   207  			Context("that are not valid json", func() {
   208  				BeforeEach(func() {
   209  					params = "bad-json"
   210  				})
   211  
   212  				It("returns an error to the UI", func() {
   213  					callUpdateService([]string{"-p", "flare", "-c", jsonFile.Name(), "my-service-instance"})
   214  
   215  					Expect(ui.Outputs()).To(ContainSubstrings(
   216  						[]string{"FAILED"},
   217  						[]string{"Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."},
   218  					))
   219  				})
   220  			})
   221  		})
   222  	})
   223  
   224  	Context("when passing in tags", func() {
   225  		It("successfully updates a service and passes the tags as json", func() {
   226  			callUpdateService([]string{"-t", "tag1, tag2,tag3,  tag4", "my-service-instance"})
   227  
   228  			Expect(ui.Outputs()).To(ContainSubstrings(
   229  				[]string{"Updating service instance", "my-service-instance"},
   230  				[]string{"OK"},
   231  			))
   232  			_, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   233  			Expect(tags).To(ConsistOf("tag1", "tag2", "tag3", "tag4"))
   234  		})
   235  
   236  		It("successfully updates a service and passes the tags as json", func() {
   237  			callUpdateService([]string{"-t", "tag1", "my-service-instance"})
   238  
   239  			Expect(ui.Outputs()).To(ContainSubstrings(
   240  				[]string{"Updating service instance", "my-service-instance"},
   241  				[]string{"OK"},
   242  			))
   243  			_, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   244  			Expect(tags).To(ConsistOf("tag1"))
   245  		})
   246  
   247  		Context("and the tags string is passed with an empty string", func() {
   248  			It("successfully updates the service", func() {
   249  				callUpdateService([]string{"-t", "", "my-service-instance"})
   250  
   251  				Expect(ui.Outputs()).To(ContainSubstrings(
   252  					[]string{"Updating service instance", "my-service-instance"},
   253  					[]string{"OK"},
   254  				))
   255  				_, _, _, tags := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   256  				Expect(tags).To(Equal([]string{}))
   257  			})
   258  		})
   259  	})
   260  
   261  	Context("when service update is asynchronous", func() {
   262  		Context("when the plan flag is passed", func() {
   263  			BeforeEach(func() {
   264  				serviceInstance := models.ServiceInstance{
   265  					ServiceInstanceFields: models.ServiceInstanceFields{
   266  						Name: "my-service-instance",
   267  						GUID: "my-service-instance-guid",
   268  						LastOperation: models.LastOperationFields{
   269  							Type:        "update",
   270  							State:       "in progress",
   271  							Description: "fake service instance description",
   272  						},
   273  					},
   274  					ServiceOffering: models.ServiceOfferingFields{
   275  						Label: "murkydb",
   276  						GUID:  "murkydb-guid",
   277  					},
   278  				}
   279  
   280  				servicePlans := []models.ServicePlanFields{{
   281  					Name: "spark",
   282  					GUID: "murkydb-spark-guid",
   283  				}, {
   284  					Name: "flare",
   285  					GUID: "murkydb-flare-guid",
   286  				},
   287  				}
   288  				serviceRepo.FindInstanceByNameReturns(serviceInstance, nil)
   289  				planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil)
   290  			})
   291  
   292  			It("successfully updates a service", func() {
   293  				callUpdateService([]string{"-p", "flare", "my-service-instance"})
   294  
   295  				Expect(ui.Outputs()).To(ContainSubstrings(
   296  					[]string{"Updating service", "my-service", "as", "my-user", "..."},
   297  					[]string{"OK"},
   298  					[]string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."},
   299  				))
   300  
   301  				Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance"))
   302  
   303  				instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   304  				Expect(instanceGUID).To(Equal("my-service-instance-guid"))
   305  				Expect(planGUID).To(Equal("murkydb-flare-guid"))
   306  			})
   307  
   308  			It("successfully updates a service", func() {
   309  				callUpdateService([]string{"-p", "flare", "my-service-instance"})
   310  
   311  				Expect(ui.Outputs()).To(ContainSubstrings(
   312  					[]string{"Updating service", "my-service", "as", "my-user", "..."},
   313  					[]string{"OK"},
   314  					[]string{"Update in progress. Use 'cf services' or 'cf service my-service-instance' to check operation status."},
   315  				))
   316  
   317  				Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance"))
   318  
   319  				instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   320  				Expect(instanceGUID).To(Equal("my-service-instance-guid"))
   321  				Expect(planGUID).To(Equal("murkydb-flare-guid"))
   322  			})
   323  
   324  			Context("when there is an err finding the instance", func() {
   325  				It("returns an error", func() {
   326  					serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance"))
   327  
   328  					callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"})
   329  
   330  					Expect(ui.Outputs()).To(ContainSubstrings(
   331  						[]string{"Error finding instance"},
   332  						[]string{"FAILED"},
   333  					))
   334  				})
   335  			})
   336  			Context("when there is an err finding service plans", func() {
   337  				It("returns an error", func() {
   338  					planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans"))
   339  
   340  					callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"})
   341  
   342  					Expect(ui.Outputs()).To(ContainSubstrings(
   343  						[]string{"Error fetching plans"},
   344  						[]string{"FAILED"},
   345  					))
   346  				})
   347  			})
   348  			Context("when the plan specified does not exist in the service offering", func() {
   349  				It("returns an error", func() {
   350  					callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"})
   351  
   352  					Expect(ui.Outputs()).To(ContainSubstrings(
   353  						[]string{"Plan does not exist for the murkydb service"},
   354  						[]string{"FAILED"},
   355  					))
   356  				})
   357  			})
   358  			Context("when there is an error updating the service instance", func() {
   359  				It("returns an error", func() {
   360  					serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance"))
   361  					callUpdateService([]string{"-p", "flare", "my-service-instance"})
   362  
   363  					Expect(ui.Outputs()).To(ContainSubstrings(
   364  						[]string{"Error updating service instance"},
   365  						[]string{"FAILED"},
   366  					))
   367  				})
   368  			})
   369  		})
   370  	})
   371  
   372  	Context("when service update is synchronous", func() {
   373  		Context("when the plan flag is passed", func() {
   374  			BeforeEach(func() {
   375  				serviceInstance := models.ServiceInstance{
   376  					ServiceInstanceFields: models.ServiceInstanceFields{
   377  						Name: "my-service-instance",
   378  						GUID: "my-service-instance-guid",
   379  					},
   380  					ServiceOffering: models.ServiceOfferingFields{
   381  						Label: "murkydb",
   382  						GUID:  "murkydb-guid",
   383  					},
   384  				}
   385  
   386  				servicePlans := []models.ServicePlanFields{{
   387  					Name: "spark",
   388  					GUID: "murkydb-spark-guid",
   389  				}, {
   390  					Name: "flare",
   391  					GUID: "murkydb-flare-guid",
   392  				},
   393  				}
   394  				serviceRepo.FindInstanceByNameReturns(serviceInstance, nil)
   395  				planBuilder.GetPlansForServiceForOrgReturns(servicePlans, nil)
   396  
   397  			})
   398  			It("successfully updates a service", func() {
   399  				callUpdateService([]string{"-p", "flare", "my-service-instance"})
   400  
   401  				Expect(ui.Outputs()).To(ContainSubstrings(
   402  					[]string{"Updating service", "my-service", "as", "my-user", "..."},
   403  					[]string{"OK"},
   404  				))
   405  				Expect(serviceRepo.FindInstanceByNameArgsForCall(0)).To(Equal("my-service-instance"))
   406  				serviceGUID, orgName := planBuilder.GetPlansForServiceForOrgArgsForCall(0)
   407  				Expect(serviceGUID).To(Equal("murkydb-guid"))
   408  				Expect(orgName).To(Equal("my-org"))
   409  
   410  				instanceGUID, planGUID, _, _ := serviceRepo.UpdateServiceInstanceArgsForCall(0)
   411  				Expect(instanceGUID).To(Equal("my-service-instance-guid"))
   412  				Expect(planGUID).To(Equal("murkydb-flare-guid"))
   413  			})
   414  
   415  			Context("when there is an err finding the instance", func() {
   416  				It("returns an error", func() {
   417  					serviceRepo.FindInstanceByNameReturns(models.ServiceInstance{}, errors.New("Error finding instance"))
   418  
   419  					callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"})
   420  
   421  					Expect(ui.Outputs()).To(ContainSubstrings(
   422  						[]string{"Error finding instance"},
   423  						[]string{"FAILED"},
   424  					))
   425  				})
   426  			})
   427  			Context("when there is an err finding service plans", func() {
   428  				It("returns an error", func() {
   429  					planBuilder.GetPlansForServiceForOrgReturns(nil, errors.New("Error fetching plans"))
   430  
   431  					callUpdateService([]string{"-p", "flare", "some-stupid-not-real-instance"})
   432  
   433  					Expect(ui.Outputs()).To(ContainSubstrings(
   434  						[]string{"Error fetching plans"},
   435  						[]string{"FAILED"},
   436  					))
   437  				})
   438  			})
   439  			Context("when the plan specified does not exist in the service offering", func() {
   440  				It("returns an error", func() {
   441  					callUpdateService([]string{"-p", "not-a-real-plan", "instance-without-service-offering"})
   442  
   443  					Expect(ui.Outputs()).To(ContainSubstrings(
   444  						[]string{"Plan does not exist for the murkydb service"},
   445  						[]string{"FAILED"},
   446  					))
   447  				})
   448  			})
   449  			Context("when there is an error updating the service instance", func() {
   450  				It("returns an error", func() {
   451  					serviceRepo.UpdateServiceInstanceReturns(errors.New("Error updating service instance"))
   452  					callUpdateService([]string{"-p", "flare", "my-service-instance"})
   453  
   454  					Expect(ui.Outputs()).To(ContainSubstrings(
   455  						[]string{"Error updating service instance"},
   456  						[]string{"FAILED"},
   457  					))
   458  				})
   459  			})
   460  		})
   461  
   462  	})
   463  })