github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/integration/v7/isolated/bind_service_command_test.go (about) 1 package isolated 2 3 import ( 4 "os" 5 "time" 6 7 "github.com/LukasHeimann/cloudfoundrycli/v8/integration/helpers" 8 "github.com/LukasHeimann/cloudfoundrycli/v8/integration/helpers/servicebrokerstub" 9 "github.com/LukasHeimann/cloudfoundrycli/v8/resources" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 . "github.com/onsi/gomega/gbytes" 13 . "github.com/onsi/gomega/gexec" 14 ) 15 16 var _ = Describe("bind-service command", func() { 17 const command = "bind-service" 18 19 Describe("help", func() { 20 matchHelpMessage := SatisfyAll( 21 Say(`NAME:\n`), 22 Say(`\s+bind-service - Bind a service instance to an app\n`), 23 Say(`\n`), 24 Say(`USAGE:\n`), 25 Say(`\s+cf bind-service APP_NAME SERVICE_INSTANCE \[-c PARAMETERS_AS_JSON\] \[--binding-name BINDING_NAME\]\n`), 26 Say(`\n`), 27 Say(`\s+Optionally provide service-specific configuration parameters in a valid JSON object in-line:\n`), 28 Say(`\n`), 29 Say(`\s+cf bind-service APP_NAME SERVICE_INSTANCE -c '\{"name":"value","name":"value"\}'\n`), 30 Say(`\n`), 31 Say(`\s+Optionally provide a file containing service-specific configuration parameters in a valid JSON object.\n`), 32 Say(`\s+The path to the parameters file can be an absolute or relative path to a file.\n`), 33 Say(`\s+cf bind-service APP_NAME SERVICE_INSTANCE -c PATH_TO_FILE\n`), 34 Say(`\n`), 35 Say(`\s+Example of valid JSON object:\n`), 36 Say(`\s+{\n`), 37 Say(`\s+"permissions": "read-only"\n`), 38 Say(`\s+}\n`), 39 Say(`\n`), 40 Say(`\s+Optionally provide a binding name for the association between an app and a service instance:\n`), 41 Say(`\n`), 42 Say(`\s+cf bind-service APP_NAME SERVICE_INSTANCE --binding-name BINDING_NAME\n`), 43 Say(`\n`), 44 Say(`EXAMPLES:\n`), 45 Say(`\s+Linux/Mac:\n`), 46 Say(`\s+cf bind-service myapp mydb -c '\{"permissions":"read-only"\}'\n`), 47 Say(`\n`), 48 Say(`\s+Windows Command Line:\n`), 49 Say(`\s+cf bind-service myapp mydb -c "\{\\"permissions\\":\\"read-only\\"\}"\n`), 50 Say(`\n`), 51 Say(`\s+Windows PowerShell:\n`), 52 Say(`\s+cf bind-service myapp mydb -c '\{\\"permissions\\":\\"read-only\\"\}'\n`), 53 Say(`\n`), 54 Say(`\s+cf bind-service myapp mydb -c ~/workspace/tmp/instance_config.json --binding-name BINDING_NAME\n`), 55 Say(`\n`), 56 Say(`ALIAS:\n`), 57 Say(`\s+bs\n`), 58 Say(`\n`), 59 Say(`OPTIONS:\n`), 60 Say(`\s+--binding-name\s+Name to expose service instance to app process with \(Default: service instance name\)\n`), 61 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`), 62 Say(`\s+--wait, -w\s+Wait for the operation to complete\n`), 63 Say(`\n`), 64 Say(`SEE ALSO:\n`), 65 Say(`\s+services\n`), 66 ) 67 68 When("the -h flag is specified", func() { 69 It("succeeds and prints help", func() { 70 session := helpers.CF(command, "-h") 71 Eventually(session).Should(Exit(0)) 72 Expect(session.Out).To(matchHelpMessage) 73 }) 74 }) 75 76 When("the --help flag is specified", func() { 77 It("succeeds and prints help", func() { 78 session := helpers.CF(command, "--help") 79 Eventually(session).Should(Exit(0)) 80 Expect(session.Out).To(matchHelpMessage) 81 }) 82 }) 83 84 When("no arguments are provided", func() { 85 It("displays a warning, the help text, and exits 1", func() { 86 session := helpers.CF(command) 87 Eventually(session).Should(Exit(1)) 88 Expect(session.Err).To(Say("Incorrect Usage: the required arguments `APP_NAME` and `SERVICE_INSTANCE` were not provided")) 89 Expect(session.Out).To(matchHelpMessage) 90 }) 91 }) 92 93 When("unknown flag is passed", func() { 94 It("displays a warning, the help text, and exits 1", func() { 95 session := helpers.CF(command, "-u") 96 Eventually(session).Should(Exit(1)) 97 Expect(session.Err).To(Say("Incorrect Usage: unknown flag `u")) 98 Expect(session.Out).To(matchHelpMessage) 99 }) 100 }) 101 102 When("-c is provided with invalid JSON", func() { 103 It("displays a warning, the help text, and exits 1", func() { 104 session := helpers.CF(command, "-c", `{"not":json"}`) 105 Eventually(session).Should(Exit(1)) 106 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.")) 107 Expect(session.Out).To(matchHelpMessage) 108 }) 109 }) 110 111 When("-c is provided with invalid JSON file", func() { 112 It("displays a warning, the help text, and exits 1", func() { 113 filename := helpers.TempFileWithContent(`{"not":json"}`) 114 defer os.Remove(filename) 115 116 session := helpers.CF(command, "-c", filename) 117 Eventually(session).Should(Exit(1)) 118 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.")) 119 Expect(session.Out).To(matchHelpMessage) 120 }) 121 }) 122 123 When("--binding-name is provided with empty value", func() { 124 It("displays a warning, the help text, and exits 1", func() { 125 session := helpers.CF(command, "appName", "serviceInstanceName", "--binding-name", "") 126 Eventually(session).Should(Exit(1)) 127 Expect(session.Err).To(Say("Incorrect Usage: --binding-name must be at least 1 character in length")) 128 Expect(session.Out).To(matchHelpMessage) 129 }) 130 }) 131 }) 132 133 When("the environment is not setup correctly", func() { 134 It("fails with the appropriate errors", func() { 135 helpers.CheckEnvironmentTargetedCorrectly(true, true, ReadOnlyOrg, "bind-service", "app-name", "service-name") 136 }) 137 }) 138 139 When("targeting a space", func() { 140 var ( 141 orgName string 142 spaceName string 143 username string 144 ) 145 146 getBinding := func(serviceInstanceName string) resources.ServiceCredentialBinding { 147 var receiver struct { 148 Resources []resources.ServiceCredentialBinding `json:"resources"` 149 } 150 helpers.Curl(&receiver, "/v3/service_credential_bindings?service_instance_names=%s", serviceInstanceName) 151 Expect(receiver.Resources).To(HaveLen(1)) 152 return receiver.Resources[0] 153 } 154 155 getParameters := func(serviceInstanceName string) (receiver map[string]interface{}) { 156 binding := getBinding(serviceInstanceName) 157 helpers.Curl(&receiver, "/v3/service_credential_bindings/%s/parameters", binding.GUID) 158 return 159 } 160 161 BeforeEach(func() { 162 orgName = helpers.NewOrgName() 163 spaceName = helpers.NewSpaceName() 164 helpers.SetupCF(orgName, spaceName) 165 166 username, _ = helpers.GetCredentials() 167 }) 168 169 AfterEach(func() { 170 helpers.QuickDeleteOrg(orgName) 171 }) 172 173 Context("user-provided route service", func() { 174 var ( 175 serviceInstanceName string 176 appName string 177 bindingName string 178 ) 179 180 BeforeEach(func() { 181 serviceInstanceName = helpers.NewServiceInstanceName() 182 Eventually(helpers.CF("cups", serviceInstanceName)).Should(Exit(0)) 183 184 appName = helpers.NewAppName() 185 helpers.WithHelloWorldApp(func(appDir string) { 186 Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) 187 }) 188 189 bindingName = helpers.RandomName() 190 }) 191 192 It("creates a binding", func() { 193 session := helpers.CF(command, appName, serviceInstanceName, "--binding-name", bindingName) 194 Eventually(session).Should(Exit(0)) 195 196 Expect(session.Out).To(SatisfyAll( 197 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 198 Say(`OK\n`), 199 Say(`\n`), 200 Say(`TIP: Use 'cf restage %s' to ensure your env variable changes take effect`, appName), 201 )) 202 203 Expect(string(session.Err.Contents())).To(BeEmpty()) 204 205 binding := getBinding(serviceInstanceName) 206 Expect(binding.Name).To(Equal(bindingName)) 207 Expect(binding.LastOperation.State).To(BeEquivalentTo("succeeded")) 208 }) 209 210 When("parameters are specified", func() { 211 It("fails with an error returned by the CC", func() { 212 session := helpers.CF(command, appName, serviceInstanceName, "-c", `{"foo":"bar"}`) 213 Eventually(session).Should(Exit(1)) 214 215 Expect(session.Out).To(SatisfyAll( 216 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 217 Say(`FAILED\n`), 218 )) 219 220 Expect(session.Err).To(Say(`Binding parameters are not supported for user-provided service instances\n`)) 221 }) 222 }) 223 }) 224 225 Context("managed service instance with synchronous broker response", func() { 226 var ( 227 broker *servicebrokerstub.ServiceBrokerStub 228 serviceInstanceName string 229 appName string 230 bindingName string 231 ) 232 233 BeforeEach(func() { 234 broker = servicebrokerstub.EnableServiceAccess() 235 serviceInstanceName = helpers.NewServiceInstanceName() 236 helpers.CreateManagedServiceInstance(broker.FirstServiceOfferingName(), broker.FirstServicePlanName(), serviceInstanceName) 237 238 appName = helpers.NewAppName() 239 helpers.WithHelloWorldApp(func(appDir string) { 240 Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) 241 }) 242 243 bindingName = helpers.RandomName() 244 }) 245 246 AfterEach(func() { 247 broker.Forget() 248 }) 249 250 It("creates a binding", func() { 251 session := helpers.CF(command, appName, serviceInstanceName, "--binding-name", bindingName) 252 Eventually(session).Should(Exit(0)) 253 254 Expect(session.Out).To(SatisfyAll( 255 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 256 Say(`OK\n`), 257 Say(`\n`), 258 Say(`TIP: Use 'cf restage %s' to ensure your env variable changes take effect`, appName), 259 )) 260 261 Expect(string(session.Err.Contents())).To(BeEmpty()) 262 263 binding := getBinding(serviceInstanceName) 264 Expect(binding.Name).To(Equal(bindingName)) 265 Expect(binding.LastOperation.State).To(BeEquivalentTo("succeeded")) 266 }) 267 268 When("parameters are specified", func() { 269 It("sends the parameters to the broker", func() { 270 session := helpers.CF(command, appName, serviceInstanceName, "-c", `{"foo":"bar"}`) 271 Eventually(session).Should(Exit(0)) 272 273 Expect(getParameters(serviceInstanceName)).To(Equal(map[string]interface{}{"foo": "bar"})) 274 }) 275 }) 276 }) 277 278 Context("managed service instance with asynchronous broker response", func() { 279 var ( 280 broker *servicebrokerstub.ServiceBrokerStub 281 serviceInstanceName string 282 appName string 283 bindingName string 284 ) 285 286 BeforeEach(func() { 287 broker = servicebrokerstub.New().WithAsyncDelay(time.Second).EnableServiceAccess() 288 serviceInstanceName = helpers.NewServiceInstanceName() 289 helpers.CreateManagedServiceInstance(broker.FirstServiceOfferingName(), broker.FirstServicePlanName(), serviceInstanceName) 290 291 appName = helpers.NewAppName() 292 helpers.WithHelloWorldApp(func(appDir string) { 293 Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) 294 }) 295 296 bindingName = helpers.RandomName() 297 }) 298 299 AfterEach(func() { 300 broker.Forget() 301 }) 302 303 It("start to create a binding", func() { 304 session := helpers.CF(command, appName, serviceInstanceName, "--binding-name", bindingName) 305 Eventually(session).Should(Exit(0)) 306 307 Expect(session.Out).To(SatisfyAll( 308 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 309 Say(`OK\n`), 310 Say(`\n`), 311 Say(`Binding in progress. Use 'cf service %s' to check operation status\.\n`, serviceInstanceName), 312 Say(`\n`), 313 Say(`TIP: Once this operation succeeds, use 'cf restage %s' to ensure your env variable changes take effect`, appName), 314 )) 315 316 Expect(string(session.Err.Contents())).To(BeEmpty()) 317 318 binding := getBinding(serviceInstanceName) 319 Expect(binding.Name).To(Equal(bindingName)) 320 Expect(binding.LastOperation.State).To(BeEquivalentTo("in progress")) 321 }) 322 323 When("--wait flag specified", func() { 324 It("waits for completion", func() { 325 session := helpers.CF(command, appName, serviceInstanceName, "--binding-name", bindingName, "--wait") 326 Eventually(session).Should(Exit(0)) 327 328 Expect(session.Out).To(SatisfyAll( 329 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 330 Say(`Waiting for the operation to complete\.+\n`), 331 Say(`\n`), 332 Say(`OK\n`), 333 )) 334 335 Expect(string(session.Err.Contents())).To(BeEmpty()) 336 337 Expect(getBinding(serviceInstanceName).LastOperation.State).To(BeEquivalentTo("succeeded")) 338 }) 339 }) 340 }) 341 342 Context("binding already exists", func() { 343 var ( 344 serviceInstanceName string 345 appName string 346 ) 347 348 BeforeEach(func() { 349 serviceInstanceName = helpers.NewServiceInstanceName() 350 Eventually(helpers.CF("cups", serviceInstanceName)).Should(Exit(0)) 351 352 appName = helpers.NewAppName() 353 helpers.WithHelloWorldApp(func(appDir string) { 354 Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) 355 }) 356 357 Eventually(helpers.CF(command, appName, serviceInstanceName)).Should(Exit(0)) 358 }) 359 360 It("says OK", func() { 361 session := helpers.CF(command, appName, serviceInstanceName) 362 Eventually(session).Should(Exit(0)) 363 364 Expect(session.Out).To(SatisfyAll( 365 Say(`Binding service instance %s to app %s in org %s / space %s as %s\.\.\.\n`, serviceInstanceName, appName, orgName, spaceName, username), 366 Say(`App %s is already bound to service instance %s.\n`, appName, serviceInstanceName), 367 Say(`OK\n`), 368 )) 369 370 Expect(string(session.Err.Contents())).To(BeEmpty()) 371 }) 372 }) 373 374 Context("app does not exist", func() { 375 var serviceInstanceName string 376 377 BeforeEach(func() { 378 serviceInstanceName = helpers.NewServiceInstanceName() 379 Eventually(helpers.CF("cups", serviceInstanceName)).Should(Exit(0)) 380 }) 381 382 It("displays FAILED and app not found", func() { 383 session := helpers.CF(command, "does-not-exist", serviceInstanceName) 384 Eventually(session).Should(Exit(1)) 385 Expect(session.Out).To(Say("FAILED")) 386 Expect(session.Err).To(Say("App 'does-not-exist' not found")) 387 }) 388 }) 389 390 Context("service instance does not exist", func() { 391 var appName string 392 393 BeforeEach(func() { 394 appName = helpers.NewAppName() 395 helpers.WithHelloWorldApp(func(appDir string) { 396 Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) 397 }) 398 }) 399 400 It("displays FAILED and service not found", func() { 401 session := helpers.CF(command, appName, "does-not-exist") 402 Eventually(session).Should(Exit(1)) 403 Expect(session.Out).To(Say("FAILED")) 404 Expect(session.Err).To(Say("Service instance 'does-not-exist' not found")) 405 }) 406 }) 407 }) 408 })