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