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