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

     1  package isolated
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"time"
     8  
     9  	"code.cloudfoundry.org/cli/integration/helpers"
    10  	"code.cloudfoundry.org/cli/integration/helpers/servicebrokerstub"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/gbytes"
    14  	. "github.com/onsi/gomega/gexec"
    15  )
    16  
    17  const serviceCommand = "service"
    18  
    19  var _ = Describe("create-service command", func() {
    20  	Describe("help", func() {
    21  
    22  		matchHelpMessage := SatisfyAll(
    23  			Say(`NAME:\n`),
    24  			Say(`\s+create-service - Create a service instance\n`),
    25  			Say(`\n`),
    26  			Say(`USAGE:\n`),
    27  			Say(`\s+cf create-service SERVICE_OFFERING PLAN SERVICE_INSTANCE \[-b SERVICE_BROKER\] \[-c PARAMETERS_AS_JSON\] \[-t TAGS\]\n`),
    28  			Say(`\s+Optionally provide service-specific configuration parameters in a valid JSON object in-line:\n`),
    29  			Say(`\s+cf create-service SERVICE_OFFERING PLAN SERVICE_INSTANCE -c '{\"name\":\"value\",\"name\":\"value\"}'\n`),
    30  			Say(`\s+Optionally provide a file containing service-specific configuration parameters in a valid JSON object\.\n`),
    31  			Say(`\s+The path to the parameters file can be an absolute or relative path to a file:\n`),
    32  			Say(`\s+cf create-service SERVICE_OFFERING PLAN SERVICE_INSTANCE -c PATH_TO_FILE\n`),
    33  			Say(`\s+Example of valid JSON object:`),
    34  			Say(`\s+{`),
    35  			Say(`\s+\"cluster_nodes\": {`),
    36  			Say(`\s+\"count\": 5,`),
    37  			Say(`\s+\"memory_mb\": 1024`),
    38  			Say(`\s+}`),
    39  			Say(`\s+}`),
    40  			Say(`TIP:`),
    41  			Say(`\s+Use 'cf create-user-provided-service' to make user-provided service instances available to CF apps`),
    42  			Say(`EXAMPLES:`),
    43  			Say(`\s+Linux/Mac:\n`),
    44  			Say(`\s+cf create-service db-service silver mydb -c '{\"ram_gb\":4}`),
    45  			Say(`\s+Windows Command Line:`),
    46  			Say(`\s+cf create-service db-service silver mydb -c \"{\\\"ram_gb\\\":4}\"`),
    47  			Say(`\s+Windows PowerShell:`),
    48  			Say(`\s+cf create-service db-service silver mydb -c '{\\\"ram_gb\\\":4}'`),
    49  			Say(`\s+cf create-service db-service silver mydb -c ~/workspace/tmp/instance_config.json`),
    50  			Say(`\s+cf create-service db-service silver mydb -t \"list, of, tags\"`),
    51  			Say(`ALIAS:`),
    52  			Say(`\s+cs`),
    53  			Say(`OPTIONS:`),
    54  			Say(`\s+-b\s+Create a service instance from a particular broker\. Required when service offering name is ambiguous`),
    55  			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\.`),
    56  			Say(`\s+-t\s+User provided tags`),
    57  			Say(`\s+--wait, -w\s+Wait for the operation to complete`),
    58  			Say(`SEE ALSO:`),
    59  			Say(`\s+bind-service, create-user-provided-service, marketplace, services`),
    60  		)
    61  
    62  		When("the -h flag is specified", func() {
    63  			It("succeeds and prints help", func() {
    64  				session := helpers.CF("create-service", "-h")
    65  				Eventually(session).Should(Exit(0))
    66  				Expect(session.Out).To(matchHelpMessage)
    67  			})
    68  		})
    69  
    70  		When("the --help flag is specified", func() {
    71  			It("succeeds and prints help", func() {
    72  				session := helpers.CF("create-service", "--help")
    73  				Eventually(session).Should(Exit(0))
    74  				Expect(session.Out).To(matchHelpMessage)
    75  			})
    76  		})
    77  
    78  		When("no arguments are provided", func() {
    79  			It("displays a warning, the help text, and exits 1", func() {
    80  				session := helpers.CF("create-service")
    81  				Eventually(session).Should(Exit(1))
    82  				Expect(session.Err).To(Say("Incorrect Usage: the required arguments `SERVICE_OFFERING`, `SERVICE_PLAN` and `SERVICE_INSTANCE` were not provided"))
    83  				Expect(session.Out).To(matchHelpMessage)
    84  			})
    85  		})
    86  
    87  		When("unknown flag is passed", func() {
    88  			It("displays a warning, the help text, and exits 1", func() {
    89  				session := helpers.CF("create-service", "-u")
    90  				Eventually(session).Should(Exit(1))
    91  				Expect(session.Err).To(Say("Incorrect Usage: unknown flag `u"))
    92  				Expect(session.Out).To(matchHelpMessage)
    93  			})
    94  		})
    95  
    96  		When("a flag is passed with no argument", func() {
    97  			It("displays a warning, the help text, and exits 1", func() {
    98  				session := helpers.CF("create-service", "-c")
    99  				Eventually(session).Should(Exit(1))
   100  				Expect(session.Err).To(Say("Incorrect Usage: expected argument for flag `-c'"))
   101  				Expect(session.Out).To(matchHelpMessage)
   102  			})
   103  		})
   104  
   105  	})
   106  
   107  	When("the environment is not setup correctly", func() {
   108  		It("fails with the appropriate errors", func() {
   109  			helpers.CheckEnvironmentTargetedCorrectly(true, true, ReadOnlyOrg, "create-service", "foo", "foo", "foo")
   110  		})
   111  	})
   112  
   113  	Context("targeting a space", func() {
   114  		var (
   115  			userName  string
   116  			orgName   string
   117  			spaceName string
   118  		)
   119  		const serviceInstanceName = "my-service"
   120  
   121  		assertCreateMessage := func(session *Session) {
   122  			Eventually(session).Should(Say("Creating service instance %s in org %s / space %s as %s...",
   123  				serviceInstanceName, orgName, spaceName, userName))
   124  		}
   125  
   126  		BeforeEach(func() {
   127  			orgName = helpers.NewOrgName()
   128  			spaceName = helpers.NewSpaceName()
   129  			helpers.SetupCF(orgName, spaceName)
   130  			userName, _ = helpers.GetCredentials()
   131  		})
   132  
   133  		When("succeeds", func() {
   134  			var (
   135  				serviceOffering string
   136  				servicePlan     string
   137  				broker          *servicebrokerstub.ServiceBrokerStub
   138  			)
   139  
   140  			BeforeEach(func() {
   141  				broker = servicebrokerstub.EnableServiceAccess()
   142  				serviceOffering = broker.FirstServiceOfferingName()
   143  				servicePlan = broker.FirstServicePlanName()
   144  			})
   145  
   146  			AfterEach(func() {
   147  				helpers.QuickDeleteOrg(orgName)
   148  				broker.Forget()
   149  			})
   150  
   151  			It("displays a message, OK and creates the instance", func() {
   152  				session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName, "-t", "a-tag,another-tag")
   153  				assertCreateMessage(session)
   154  				Eventually(session).Should(Say(`Service instance %s created\.\n`, serviceInstanceName))
   155  				Eventually(session).Should(Exit(0))
   156  
   157  				session = helpers.CF(serviceCommand, serviceInstanceName)
   158  				Eventually(session).Should(Exit(0))
   159  				Expect(session).To(SatisfyAll(
   160  					Say(`name:\s+%s`, serviceInstanceName),
   161  					Say(`type:\s+%s`, "managed"),
   162  					Say(`tags:\s+a-tag,\s*another-tag`),
   163  				))
   164  			})
   165  
   166  			When("creating with valid params json", func() {
   167  				const parametersJSON = `{"valid":"json"}`
   168  
   169  				It("displays an informative success message, and creates the instance with parameters", func() {
   170  					session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName, "-c", parametersJSON)
   171  					Eventually(session).Should(Say("Creating service instance %s in org %s / space %s as %s\\.\\.\\.",
   172  						serviceInstanceName, orgName, spaceName, userName))
   173  					Eventually(session).Should(Say("OK"))
   174  					Eventually(session).Should(Exit(0))
   175  
   176  					session = helpers.CF(serviceCommand, serviceInstanceName, "--params")
   177  					Eventually(session).Should(Exit(0))
   178  					Eventually(session).Should(SatisfyAll(
   179  						Say(`\{\n`),
   180  						Say(`  "valid": "json"\n`),
   181  						Say(`\}\n`),
   182  					))
   183  				})
   184  			})
   185  
   186  			When("creating with valid params json in a file", func() {
   187  				const parametersJSON = `{"valid":"json"}`
   188  				var tempFilePath string
   189  
   190  				BeforeEach(func() {
   191  					tempFilePath = helpers.TempFileWithContent(parametersJSON)
   192  				})
   193  
   194  				AfterEach(func() {
   195  					Expect(os.Remove(tempFilePath)).To(Succeed())
   196  				})
   197  
   198  				It("displays an informative success message, exits 0", func() {
   199  					session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName, "-c", tempFilePath)
   200  					Eventually(session).Should(Say("Creating service instance %s in org %s / space %s as %s...",
   201  						serviceInstanceName, orgName, spaceName, userName))
   202  					Eventually(session).Should(Say("OK"))
   203  					Eventually(session).Should(Exit(0))
   204  
   205  					session = helpers.CF(serviceCommand, serviceInstanceName, "--params")
   206  					Eventually(session).Should(Exit(0))
   207  					Eventually(session).Should(SatisfyAll(
   208  						Say(`\{\n`),
   209  						Say(`  "valid": "json"\n`),
   210  						Say(`\}\n`),
   211  					))
   212  				})
   213  			})
   214  
   215  			When("the service broker responds asynchronously", func() {
   216  				BeforeEach(func() {
   217  					broker.WithAsyncDelay(time.Second).Configure()
   218  				})
   219  
   220  				It("displays a message, OK and creates the instance", func() {
   221  					session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName, "-t", "a-tag,another-tag")
   222  					assertCreateMessage(session)
   223  					Eventually(session).Should(Say("Create in progress. Use 'cf services' or 'cf service my-service' to check operation status."))
   224  					Eventually(session).Should(Exit(0))
   225  
   226  					session = helpers.CF(serviceCommand, serviceInstanceName)
   227  					Eventually(session).Should(Exit(0))
   228  					Expect(session).To(SatisfyAll(
   229  						Say(`name:\s+%s`, serviceInstanceName),
   230  						Say(`type:\s+%s`, "managed"),
   231  						Say(`tags:\s+a-tag,\s*another-tag`),
   232  					))
   233  
   234  				})
   235  			})
   236  
   237  			When("creating with --wait flag", func() {
   238  				BeforeEach(func() {
   239  					broker.WithAsyncDelay(5 * time.Second).Configure()
   240  				})
   241  
   242  				It("displays a message, OK and creates the instance", func() {
   243  					session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName, "--wait")
   244  					Eventually(session).Should(Exit(0))
   245  					assertCreateMessage(session)
   246  					Expect(session.Out).To(SatisfyAll(
   247  						Say(`Service instance %s created\.\n`, serviceInstanceName),
   248  						Say(`OK\n`),
   249  					))
   250  
   251  					session = helpers.CF(serviceCommand, serviceInstanceName)
   252  					Eventually(session).Should(Exit(0))
   253  					Expect(session).To(SatisfyAll(
   254  						Say(`name:\s+%s`, serviceInstanceName),
   255  						Say(`status:\s+create succeeded`),
   256  					))
   257  				})
   258  			})
   259  		})
   260  
   261  		When("there are two offerings with the same name from different brokers", func() {
   262  			var (
   263  				serviceOffering string
   264  				servicePlan     string
   265  				broker1         *servicebrokerstub.ServiceBrokerStub
   266  				broker2         *servicebrokerstub.ServiceBrokerStub
   267  			)
   268  
   269  			BeforeEach(func() {
   270  				broker1 = servicebrokerstub.EnableServiceAccess()
   271  				serviceOffering = broker1.FirstServiceOfferingName()
   272  				servicePlan = broker1.FirstServicePlanName()
   273  				broker2 = servicebrokerstub.New()
   274  				broker2.Services[0].Name = serviceOffering
   275  				broker2.Services[0].Plans[0].Name = servicePlan
   276  				broker2.EnableServiceAccess()
   277  			})
   278  
   279  			AfterEach(func() {
   280  				helpers.QuickDeleteOrg(orgName)
   281  				broker1.Forget()
   282  				broker2.Forget()
   283  			})
   284  
   285  			It("displays an error message prompting to disambiguate", func() {
   286  				session := helpers.CF("create-service", serviceOffering, servicePlan, serviceInstanceName)
   287  				assertCreateMessage(session)
   288  				Eventually(session.Err).Should(Say("Service offering '%s' is provided by multiple service brokers. Specify a broker name by using the '-b' flag.", serviceOffering))
   289  				Eventually(session).Should(Say("FAILED"))
   290  				Eventually(session).Should(Exit(1))
   291  			})
   292  		})
   293  
   294  		When("there are no plans matching", func() {
   295  			var (
   296  				serviceOffering string
   297  				broker1         *servicebrokerstub.ServiceBrokerStub
   298  			)
   299  
   300  			BeforeEach(func() {
   301  				broker1 = servicebrokerstub.EnableServiceAccess()
   302  				serviceOffering = broker1.FirstServiceOfferingName()
   303  			})
   304  
   305  			AfterEach(func() {
   306  				helpers.QuickDeleteOrg(orgName)
   307  				broker1.Forget()
   308  			})
   309  			It("displays an error message", func() {
   310  				session := helpers.CF("create-service", serviceOffering, "another-service-plan", serviceInstanceName, "-b", broker1.Name)
   311  				assertCreateMessage(session)
   312  				Eventually(session.Err).Should(Say("The plan '%s' could not be found for service offering '%s' and broker '%s'.", "another-service-plan", serviceOffering, broker1.Name))
   313  				Eventually(session).Should(Say("FAILED"))
   314  				Eventually(session).Should(Exit(1))
   315  			})
   316  		})
   317  
   318  		When("invalid arguments are passed", func() {
   319  			When("with an invalid json for -c", func() {
   320  				It("displays an informative error message, exits 1", func() {
   321  					session := helpers.CF("create-service", "foo", "bar", serviceInstanceName, "-c", "{")
   322  					Eventually(session.Err).Should(Say("Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."))
   323  					Eventually(session).Should(Exit(1))
   324  				})
   325  			})
   326  
   327  			When("the provided file contains invalid json", func() {
   328  				var tempFilePath string
   329  
   330  				BeforeEach(func() {
   331  					tempFilePath = helpers.TempFileWithContent(`{"invalid"}`)
   332  				})
   333  
   334  				AfterEach(func() {
   335  					Expect(os.Remove(tempFilePath)).To(Succeed())
   336  				})
   337  
   338  				It("displays an informative message and exits 1", func() {
   339  					session := helpers.CF("create-service", "foo", "bar", serviceInstanceName, "-c", tempFilePath)
   340  					Eventually(session.Err).Should(Say("Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."))
   341  					Eventually(session).Should(Exit(1))
   342  				})
   343  			})
   344  
   345  			When("the provided file cannot be read", func() {
   346  				var emptyDir string
   347  
   348  				BeforeEach(func() {
   349  					var err error
   350  					emptyDir, err = ioutil.TempDir("", "")
   351  					Expect(err).NotTo(HaveOccurred())
   352  				})
   353  
   354  				AfterEach(func() {
   355  					Expect(os.RemoveAll(emptyDir)).To(Succeed())
   356  				})
   357  
   358  				It("displays an informative message and exits 1", func() {
   359  					session := helpers.CF("create-service", "foo", "bar", serviceInstanceName, "-c", filepath.Join(emptyDir, "nonexistent-file"))
   360  					Eventually(session.Err).Should(Say("Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object."))
   361  					Eventually(session).Should(Exit(1))
   362  				})
   363  			})
   364  		})
   365  	})
   366  })