github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/integration/v7/isolated/update_service_command_test.go (about)

     1  package isolated
     2  
     3  import (
     4  	"os"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/integration/helpers"
     8  	"code.cloudfoundry.org/cli/integration/helpers/servicebrokerstub"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/ginkgo/extensions/table"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/gbytes"
    13  	. "github.com/onsi/gomega/gexec"
    14  )
    15  
    16  var _ = Describe("update-service command", func() {
    17  	const command = "update-service"
    18  
    19  	Describe("help", func() {
    20  		matchHelpMessage := SatisfyAll(
    21  			Say(`NAME:\n`),
    22  			Say(`\s+%s - Update a service instance\n`, command),
    23  			Say(`\n`),
    24  			Say(`USAGE:\n`),
    25  			Say(`\s+cf update-service SERVICE_INSTANCE \[-p NEW_PLAN\] \[-c PARAMETERS_AS_JSON\] \[-t TAGS\]\n`),
    26  			Say(`\n`),
    27  			Say(`\s+Optionally provide service-specific configuration parameters in a valid JSON object in-line:\n`),
    28  			Say(`\s+cf update-service SERVICE_INSTANCE -c '{\"name\":\"value\",\"name\":\"value\"}'\n`),
    29  			Say(`\s+Optionally provide a file containing service-specific configuration parameters in a valid JSON object\.\n`),
    30  			Say(`\s+The path to the parameters file can be an absolute or relative path to a file:\n`),
    31  			Say(`\s+cf update-service SERVICE_INSTANCE -c PATH_TO_FILE\n`),
    32  			Say(`\s+Example of valid JSON object:\n`),
    33  			Say(`\s+{\n`),
    34  			Say(`\s+\"cluster_nodes\": {\n`),
    35  			Say(`\s+\"count\": 5,\n`),
    36  			Say(`\s+\"memory_mb\": 1024\n`),
    37  			Say(`\s+}\n`),
    38  			Say(`\s+}\n`),
    39  			Say(`\s+ Optionally provide a list of comma-delimited tags that will be written to the VCAP_SERVICES environment variable for any bound applications.\n`),
    40  			Say(`EXAMPLES:\n`),
    41  			Say(`\s+cf update-service mydb -p gold\n`),
    42  			Say(`\s+cf update-service mydb -c '{\"ram_gb\":4}'\n`),
    43  			Say(`\s+cf update-service mydb -c ~/workspace/tmp/instance_config.json\n`),
    44  			Say(`\s+cf update-service mydb -t "list, of, tags"\n`),
    45  			Say(`OPTIONS:\n`),
    46  			Say(`\s+-c\s+Valid JSON object containing service-specific configuration parameters, provided either in-line or in a file\. For a list of supported configuration parameters, see documentation for the particular service offering\.\n`),
    47  			Say(`\s+-p\s+Change service plan for a service instance\n`),
    48  			Say(`\s+-t\s+User provided tags\n`),
    49  			Say(`\s+--wait, -w\s+Wait for the operation to complete\n`),
    50  			Say(`SEE ALSO:\n`),
    51  			Say(`\s+rename-service, services, update-user-provided-service\n`),
    52  		)
    53  
    54  		When("the --help flag is specified", func() {
    55  			It("exits successfully and prints the help message", func() {
    56  				session := helpers.CF(command, "--help")
    57  
    58  				Eventually(session).Should(Exit(0))
    59  				Expect(session.Out).To(matchHelpMessage)
    60  				Expect(session.Err.Contents()).To(BeEmpty())
    61  			})
    62  		})
    63  
    64  		When("the service instance name is omitted", func() {
    65  			It("fails and prints the help message", func() {
    66  				session := helpers.CF(command)
    67  
    68  				Eventually(session).Should(Exit(1))
    69  				Expect(session.Out).To(matchHelpMessage)
    70  				Expect(session.Err).To(Say("Incorrect Usage: the required argument `SERVICE_INSTANCE` was not provided\n"))
    71  			})
    72  		})
    73  
    74  		When("an extra parameter is provided", func() {
    75  			It("fails and prints the help message", func() {
    76  				session := helpers.CF(command, "service-instance-name", "invalid-extra-parameter")
    77  
    78  				Eventually(session).Should(Exit(1))
    79  				Expect(session.Out).To(matchHelpMessage)
    80  				Expect(session.Err).To(Say(`Incorrect Usage: unexpected argument "invalid-extra-parameter"`))
    81  			})
    82  		})
    83  
    84  		When("an extra flag is provided", func() {
    85  			It("fails and prints the help message", func() {
    86  				session := helpers.CF(command, "service-instance-name", "--invalid")
    87  
    88  				Eventually(session).Should(Exit(1))
    89  				Expect(session.Out).To(matchHelpMessage)
    90  				Expect(session.Err).To(Say("Incorrect Usage: unknown flag `invalid'"))
    91  			})
    92  		})
    93  
    94  		When("a flag is provided without a value", func() {
    95  			DescribeTable(
    96  				"it fails and prints a help message",
    97  				func(flag string) {
    98  					session := helpers.CF(command, "service-instance-name", flag)
    99  
   100  					Eventually(session).Should(Exit(1))
   101  					Expect(session.Out).To(matchHelpMessage)
   102  					Expect(session.Err).To(Say("Incorrect Usage: expected argument for flag `%s'", flag))
   103  				},
   104  				Entry("configuration", "-c"),
   105  				Entry("plan", "-p"),
   106  				Entry("tags", "-t"),
   107  			)
   108  		})
   109  	})
   110  
   111  	When("the environment is not setup correctly", func() {
   112  		It("fails with the appropriate errors", func() {
   113  			helpers.CheckEnvironmentTargetedCorrectly(true, true, ReadOnlyOrg, command, "service-instance-name")
   114  		})
   115  	})
   116  
   117  	When("logged in and targeting a space", func() {
   118  		const serviceCommand = "service"
   119  
   120  		var (
   121  			orgName             string
   122  			spaceName           string
   123  			username            string
   124  			serviceInstanceName string
   125  		)
   126  
   127  		BeforeEach(func() {
   128  			orgName = helpers.NewOrgName()
   129  			spaceName = helpers.NewSpaceName()
   130  			helpers.SetupCF(orgName, spaceName)
   131  
   132  			username, _ = helpers.GetCredentials()
   133  
   134  			serviceInstanceName = helpers.NewServiceInstanceName()
   135  		})
   136  
   137  		AfterEach(func() {
   138  			helpers.QuickDeleteOrg(orgName)
   139  		})
   140  
   141  		When("the service instance doesn't exist", func() {
   142  			It("fails with an appropriate error", func() {
   143  				session := helpers.CF(command, serviceInstanceName, "-t", "important")
   144  				Eventually(session).Should(Exit(1))
   145  				Expect(session.Out).To(SatisfyAll(
   146  					Say("Updating service instance %s in org %s / space %s as %s...", serviceInstanceName, orgName, spaceName, username),
   147  					Say("FAILED"),
   148  				))
   149  				Expect(session.Err).To(Say("Service instance '%s' not found\n", serviceInstanceName))
   150  			})
   151  		})
   152  
   153  		When("the service instance exists", func() {
   154  			const (
   155  				originalTags = "foo, bar"
   156  				newTags      = "bax, quz"
   157  			)
   158  			var broker *servicebrokerstub.ServiceBrokerStub
   159  
   160  			BeforeEach(func() {
   161  				broker = servicebrokerstub.New().WithPlans(2).EnableServiceAccess()
   162  				helpers.CreateManagedServiceInstance(
   163  					broker.FirstServiceOfferingName(),
   164  					broker.FirstServicePlanName(),
   165  					serviceInstanceName,
   166  					"-t", originalTags,
   167  				)
   168  				session := helpers.CF("m")
   169  				Eventually(session).Should(Exit(0))
   170  
   171  			})
   172  
   173  			AfterEach(func() {
   174  				broker.Forget()
   175  			})
   176  
   177  			It("can update tags, parameters and plan", func() {
   178  				By("performing the update")
   179  				newPlanName := broker.Services[0].Plans[1].Name
   180  				session := helpers.CF(command, serviceInstanceName, "-t", newTags, "-p", newPlanName, "-c", `{"foo":"bar"}`)
   181  				Eventually(session).Should(Exit(0))
   182  
   183  				Expect(session.Out).To(SatisfyAll(
   184  					Say(`Updating service instance %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, orgName, spaceName, username),
   185  					Say(`\n`),
   186  					Say(`Update of service instance %s complete\.\n`, serviceInstanceName),
   187  					Say(`OK\n`),
   188  				))
   189  
   190  				Expect(string(session.Err.Contents())).To(BeEmpty())
   191  
   192  				By("checking that the update changed things")
   193  				session = helpers.CF(serviceCommand, serviceInstanceName)
   194  				Eventually(session).Should(Exit(0))
   195  
   196  				Expect(session.Out).To(SatisfyAll(
   197  					Say(`plan:\s+%s`, newPlanName),
   198  					Say(`tags:\s+%s`, newTags),
   199  				))
   200  			})
   201  
   202  			Describe("updating tags", func() {
   203  				It("can update tags alone", func() {
   204  					session := helpers.CF(command, serviceInstanceName, "-t", newTags)
   205  					Eventually(session).Should(Exit(0))
   206  
   207  					Expect(session.Out).To(SatisfyAll(
   208  						Say(`Updating service instance %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, orgName, spaceName, username),
   209  						Say(`\n`),
   210  						Say(`Update of service instance %s complete\.\n`, serviceInstanceName),
   211  						Say(`OK\n`),
   212  					))
   213  
   214  					Expect(string(session.Err.Contents())).To(BeEmpty())
   215  
   216  					session = helpers.CF(serviceCommand, serviceInstanceName)
   217  					Eventually(session).Should(Exit(0))
   218  					Expect(session.Out).To(Say(`tags:\s+%s`, newTags))
   219  				})
   220  			})
   221  
   222  			Describe("updating parameters", func() {
   223  				const (
   224  					validParams   = `{"funky":"chicken"}`
   225  					invalidParams = `{"funky":chicken"}`
   226  				)
   227  
   228  				checkParams := func() {
   229  					session := helpers.CF(serviceCommand, serviceInstanceName, "--params")
   230  					Eventually(session).Should(Exit(0))
   231  					Expect(session.Out).To(SatisfyAll(
   232  						Say(`\{\n`),
   233  						Say(`  "funky": "chicken"\n`),
   234  						Say(`\}\n`),
   235  					))
   236  				}
   237  
   238  				It("accepts JSON on the command line", func() {
   239  					session := helpers.CF(command, serviceInstanceName, "-c", validParams)
   240  					Eventually(session).Should(Exit(0))
   241  
   242  					checkParams()
   243  				})
   244  
   245  				It("rejects invalid JSON on the command line", func() {
   246  					session := helpers.CF(command, serviceInstanceName, "-c", invalidParams)
   247  					Eventually(session).Should(Exit(1))
   248  
   249  					Expect(session.Err).To(Say("Incorrect Usage: Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.\n"))
   250  				})
   251  
   252  				It("accepts JSON from a file", func() {
   253  					path := helpers.TempFileWithContent(validParams)
   254  					defer os.Remove(path)
   255  
   256  					session := helpers.CF(command, serviceInstanceName, "-c", path, "-v")
   257  					Eventually(session).Should(Exit(0))
   258  
   259  					checkParams()
   260  				})
   261  
   262  				It("rejects invalid JSON from a file", func() {
   263  					path := helpers.TempFileWithContent(invalidParams)
   264  					defer os.Remove(path)
   265  
   266  					session := helpers.CF(command, serviceInstanceName, "-c", path)
   267  					Eventually(session).Should(Exit(1))
   268  
   269  					Expect(session.Err).To(Say("Incorrect Usage: Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.\n"))
   270  				})
   271  			})
   272  
   273  			Describe("updating plan", func() {
   274  				It("updates the plan", func() {
   275  					newPlanName := broker.Services[0].Plans[1].Name
   276  					session := helpers.CF(command, serviceInstanceName, "-p", newPlanName)
   277  					Eventually(session).Should(Exit(0))
   278  
   279  					Eventually(helpers.CF("service", serviceInstanceName)).Should(
   280  						SatisfyAll(
   281  							Say(`plan:\s+%s`, newPlanName),
   282  							Say(`status:\s+update succeeded`),
   283  						),
   284  					)
   285  				})
   286  
   287  				When("plan does not exist", func() {
   288  					const invalidPlan = "invalid-plan"
   289  
   290  					It("displays an error and exits 1", func() {
   291  						session := helpers.CF(command, serviceInstanceName, "-p", invalidPlan)
   292  						Eventually(session).Should(Exit(1))
   293  						Expect(session.Err).To(Say("The plan '%s' could not be found for service offering '%s' and broker '%s'.", invalidPlan, broker.Services[0].Name, broker.Name))
   294  					})
   295  				})
   296  
   297  				When("plan is the same as current", func() {
   298  					It("displays a message and exits 0", func() {
   299  						session := helpers.CF(command, serviceInstanceName, "-p", broker.Services[0].Plans[0].Name)
   300  						Eventually(session).Should(Exit(0))
   301  						Expect(session.Out).To(SatisfyAll(
   302  							Say(`No changes were made\.\n`),
   303  							Say(`OK\n`),
   304  						))
   305  					})
   306  				})
   307  			})
   308  
   309  			When("the broker responds asynchronously", func() {
   310  				BeforeEach(func() {
   311  					broker.WithAsyncDelay(time.Second).Configure()
   312  				})
   313  
   314  				It("says that the operation is in progress", func() {
   315  					newPlanName := broker.Services[0].Plans[1].Name
   316  					session := helpers.CF(command, serviceInstanceName, "-p", newPlanName)
   317  					Eventually(session).Should(Exit(0))
   318  
   319  					Expect(session.Out).To(SatisfyAll(
   320  						Say(`Updating service instance %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, orgName, spaceName, username),
   321  						Say(`\n`),
   322  						Say(`Update in progress. Use 'cf services' or 'cf service %s' to check operation status\.\n`, serviceInstanceName),
   323  						Say(`OK\n`),
   324  					))
   325  				})
   326  
   327  				It("accepts the --wait flag to wait for completion", func() {
   328  					session := helpers.CF(command, serviceInstanceName, "-c", `{"funky":"chicken"}`, "--wait")
   329  					Eventually(session).Should(Exit(0))
   330  
   331  					Expect(session.Out).To(SatisfyAll(
   332  						Say(`Updating service instance %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, orgName, spaceName, username),
   333  						Say(`\n`),
   334  						Say(`Waiting for the operation to complete\.+\n`),
   335  						Say(`\n`),
   336  						Say(`Update of service instance %s complete\.\n`, serviceInstanceName),
   337  						Say(`OK\n`),
   338  					))
   339  
   340  					Expect(string(session.Err.Contents())).To(BeEmpty())
   341  					session = helpers.CF(serviceCommand, serviceInstanceName)
   342  					Eventually(session).Should(Exit(0))
   343  					Expect(session.Out).To(Say(`status:\s+update succeeded`))
   344  				})
   345  			})
   346  		})
   347  	})
   348  })