@@ -2,7 +2,6 @@ package broker
22
33import (
44 "fmt"
5- "github.com/aws/aws-sdk-go/aws/session"
65 "net/http"
76 "strings"
87
@@ -17,11 +16,6 @@ import (
1716 prom "github.com/prometheus/client_golang/prometheus"
1817)
1918
20- const (
21- instanceId = "INSTANCE_ID"
22- bindingId = "BINDING_ID"
23- )
24-
2519// GetCatalog is executed on a /v2/catalog/ osb api call
2620// https://github.com/openservicebrokerapi/servicebroker/blob/v2.13/spec.md#catalog-management
2721func (b * AwsBroker ) GetCatalog (c * broker.RequestContext ) (* broker.CatalogResponse , error ) {
@@ -131,31 +125,17 @@ func (b *AwsBroker) Provision(request *osb.ProvisionRequest, c *broker.RequestCo
131125 desc := fmt .Sprintf ("Failed to get the service instance %s: %v" , instance .ID , err )
132126 return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
133127 } else if i != nil {
128+ // TODO: This logic could use some love. The docs state that 200 OK MUST be
129+ // returned if the service instance already exists, is fully provisioned,
130+ // and the requested parameters are identical to the existing service
131+ // instance. Right now, this doesn't check whether the instance is fully
132+ // provisioned, and the reflect.DeepEqual check in Match will return false
133+ // if the parameter order is different.
134134 if i .Match (instance ) {
135135 glog .Infof ("Service instance %s already exists." , instance .ID )
136-
137- glog .Infof ("Checking if service instance %s is fully provisioned." , instance .ID )
138- cfnSvc := getCfn (b , instance .Params )
139-
140136 response := broker.ProvisionResponse {}
141- status , _ , err := getStackStatusAndReason (cfnSvc , i .StackID )
142-
143- switch {
144- case err != nil :
145- desc := fmt .Sprintf ("Failed to get the stack %s: %v" , i .StackID , err )
146- return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
147- case instanceIsFullyProvisioned (status ):
148- glog .Infof ("Service instance %s is fully provisioned." , instance .ID )
149- response .Exists = true
150- return & response , nil
151- case instanceProvisioningIsInProgress (status ):
152- glog .Infof ("Service instance %s provisioning is in progress." , instance .ID )
153- response .Async = true
154- return & response , nil
155- default :
156- glog .Infof ("Service instance %s provisioning failed." , instance .ID )
157- return & response , newHTTPStatusCodeError (http .StatusBadRequest , "CloudFormationError" , * getCfnError (i .StackID , cfnSvc ))
158- }
137+ response .Exists = true
138+ return & response , nil
159139 }
160140 glog .V (10 ).Infof ("i=%+v instance=%+v" , * i , * instance )
161141 desc := fmt .Sprintf ("Service instance %s already exists but with different attributes." , instance .ID )
@@ -291,33 +271,38 @@ func (b *AwsBroker) LastOperation(request *osb.LastOperationRequest, c *broker.R
291271 }
292272
293273 // Get the CFN stack status
294- cfnSvc := getCfn (b , instance .Params )
295-
296- status , reason , err := getStackStatusAndReason (cfnSvc , instance .StackID )
274+ cfnSvc := b .Clients .NewCfn (b .GetSession (b .keyid , b .secretkey , b .region , b .accountId , b .profile , instance .Params ))
275+ resp , err := cfnSvc .Client .DescribeStacks (& cloudformation.DescribeStacksInput {
276+ StackName : aws .String (instance .StackID ),
277+ })
297278 if err != nil {
298- return nil , err
279+ desc := fmt .Sprintf ("Failed to describe the CloudFormation stack %s: %v" , instance .StackID , err )
280+ return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
299281 }
282+ status := aws .StringValue (resp .Stacks [0 ].StackStatus )
283+ reason := aws .StringValue (resp .Stacks [0 ].StackStatusReason )
284+ glog .V (10 ).Infof ("stack=%s status=%s reason=%s" , instance .StackID , status , reason )
300285
301286 response := broker.LastOperationResponse {}
302- if instanceIsFullyProvisioned (status ) {
287+ if status == cloudformation .StackStatusCreateComplete ||
288+ status == cloudformation .StackStatusDeleteComplete ||
289+ status == cloudformation .StackStatusUpdateComplete {
303290 response .State = osb .StateSucceeded
304291 if status == cloudformation .StackStatusDeleteComplete {
305292 // If the resources were successfully deleted, try to delete the instance
306293 if err := b .db .DataStorePort .DeleteServiceInstance (instance .ID ); err != nil {
307294 glog .Errorf ("Failed to delete the service instance %s: %v" , instance .ID , err )
308295 }
309296 }
310- } else if instanceProvisioningIsInProgress (status ) {
297+ } else if strings . HasSuffix (status , "_IN_PROGRESS" ) && ! strings . Contains ( status , "ROLLBACK" ) {
311298 response .State = osb .StateInProgress
312299 } else {
313300 glog .Errorf ("CloudFormation stack %s failed with status %s: %s" , instance .StackID , status , reason )
314- response := broker.LastOperationResponse {}
315301 response .State = osb .StateFailed
316302 response .Description = getCfnError (instance .StackID , cfnSvc )
317303 if * response .Description == "" {
318304 response .Description = & reason
319305 }
320-
321306 // workaround for https://github.com/kubernetes-incubator/service-catalog/issues/2505
322307 originatingIdentity := strings .Split (c .Request .Header .Get ("X-Broker-Api-Originating-Identity" ), " " )[0 ]
323308 if originatingIdentity == "kubernetes" {
@@ -332,35 +317,6 @@ func (b *AwsBroker) LastOperation(request *osb.LastOperationRequest, c *broker.R
332317 return & response , nil
333318}
334319
335- func getCfn (b * AwsBroker , instanceParams map [string ]string ) CfnClient {
336- return b .Clients .NewCfn (b .GetSession (b .keyid , b .secretkey , b .region , b .accountId , b .profile , instanceParams ))
337- }
338-
339- func getStackStatusAndReason (cfnSvc CfnClient , stackId string ) (status string , reason string , err error ) {
340- resp , err := cfnSvc .Client .DescribeStacks (& cloudformation.DescribeStacksInput {
341- StackName : aws .String (stackId ),
342- })
343- if err != nil {
344- desc := fmt .Sprintf ("Failed to describe the CloudFormation stack %s: %v" , stackId , err )
345- return "" , "" , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
346- }
347- status = aws .StringValue (resp .Stacks [0 ].StackStatus )
348- reason = aws .StringValue (resp .Stacks [0 ].StackStatusReason )
349- glog .V (10 ).Infof ("stack=%s status=%s reason=%s" , stackId , status , reason )
350-
351- return
352- }
353-
354- func instanceIsFullyProvisioned (status string ) bool {
355- return status == cloudformation .StackStatusCreateComplete ||
356- status == cloudformation .StackStatusDeleteComplete ||
357- status == cloudformation .StackStatusUpdateComplete
358- }
359-
360- func instanceProvisioningIsInProgress (status string ) bool {
361- return strings .HasSuffix (status , "_IN_PROGRESS" ) && ! strings .Contains (status , "ROLLBACK" )
362- }
363-
364320// Bind is executed when the OSB API receives `PUT /v2/service_instances/:instance_id/service_bindings/:binding_id`
365321// (https://github.com/openservicebrokerapi/servicebroker/blob/v2.13/spec.md#request-4).
366322func (b * AwsBroker ) Bind (request * osb.BindRequest , c * broker.RequestContext ) (* broker.BindResponse , error ) {
@@ -383,6 +339,22 @@ func (b *AwsBroker) Bind(request *osb.BindRequest, c *broker.RequestContext) (*b
383339 }
384340 }
385341
342+ // Verify that the binding doesn't already exist
343+ sb , err := b .db .DataStorePort .GetServiceBinding (binding .ID )
344+ if err != nil {
345+ desc := fmt .Sprintf ("Failed to get the service binding %s: %v" , binding .ID , err )
346+ return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
347+ } else if sb != nil {
348+ if sb .Match (binding ) {
349+ glog .Infof ("Service binding %s already exists." , binding .ID )
350+ response := broker.BindResponse {}
351+ response .Exists = true
352+ return & response , nil
353+ }
354+ desc := fmt .Sprintf ("Service binding %s already exists but with different attributes." , binding .ID )
355+ return nil , newHTTPStatusCodeError (http .StatusConflict , "" , desc )
356+ }
357+
386358 // Get the service (this is only required because the USER_KEY_ID and
387359 // USER_SECRET_KEY credentials need to be prefixed with the service name for
388360 // backward compatibility)
@@ -416,18 +388,11 @@ func (b *AwsBroker) Bind(request *osb.BindRequest, c *broker.RequestContext) (*b
416388 return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
417389 }
418390
419- // Verify that the binding doesn't already exist
420- sb , err := b . db . DataStorePort . GetServiceBinding ( binding . ID )
391+ // Get the credentials from the CFN stack outputs
392+ credentials , err := getCredentials ( service , resp . Stacks [ 0 ]. Outputs , b . Clients . NewSsm ( sess ) )
421393 if err != nil {
422- desc := fmt .Sprintf ("Failed to get the service binding %s: %v" , binding . ID , err )
394+ desc := fmt .Sprintf ("Failed to get the credentials from CloudFormation stack %s: %v" , instance . StackID , err )
423395 return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
424- } else if sb != nil {
425- if sb .Match (binding ) {
426- glog .Infof ("Service binding %s already exists." , binding .ID )
427- return createBindResponse (service , resp , b , sess , instance , binding )
428- }
429- desc := fmt .Sprintf ("Service binding %s already exists but with different attributes." , binding .ID )
430- return nil , newHTTPStatusCodeError (http .StatusConflict , "" , desc )
431396 }
432397
433398 if binding .RoleName != "" {
@@ -450,68 +415,6 @@ func (b *AwsBroker) Bind(request *osb.BindRequest, c *broker.RequestContext) (*b
450415 binding .PolicyArn = policyArn
451416 }
452417
453- credentials , err := getBindingCredentials (service , resp , b , sess , instance , binding )
454- if err != nil {
455- return nil , err
456- }
457-
458- // Store the binding
459- err = b .db .DataStorePort .PutServiceBinding (* binding )
460- if err != nil {
461- desc := fmt .Sprintf ("Failed to store the service binding %s: %v" , binding .ID , err )
462- return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
463- }
464-
465- b .metrics .Actions .With (
466- prom.Labels {
467- "action" : "bind" ,
468- "service" : service .Name ,
469- "plan" : "" ,
470- }).Inc ()
471-
472- return & broker.BindResponse {
473- BindResponse : osb.BindResponse {
474- Credentials : credentials ,
475- },
476- }, nil
477- }
478-
479- func createBindResponse (
480- service * osb.Service ,
481- resp * cloudformation.DescribeStacksOutput ,
482- b * AwsBroker ,
483- sess * session.Session ,
484- instance * serviceinstance.ServiceInstance ,
485- binding * serviceinstance.ServiceBinding ) (* broker.BindResponse , error ) {
486-
487- response := broker.BindResponse {}
488- response .Exists = true
489-
490- credentials , err := getBindingCredentials (service , resp , b , sess , instance , binding )
491- if err != nil {
492- return nil , err
493- }
494-
495- response .Credentials = credentials
496- return & response , nil
497- }
498-
499- func getBindingCredentials (
500- service * osb.Service ,
501- resp * cloudformation.DescribeStacksOutput ,
502- b * AwsBroker ,
503- sess * session.Session ,
504- instance * serviceinstance.ServiceInstance ,
505- binding * serviceinstance.ServiceBinding ) (map [string ]interface {}, error ) {
506-
507- // Get the credentials from the CFN stack outputs
508- credentials , err := getCredentials (service , resp .Stacks [0 ].Outputs , b .Clients .NewSsm (sess ))
509- if err != nil {
510- desc := fmt .Sprintf ("Failed to get the credentials from CloudFormation stack %s: %v" , instance .StackID , err )
511- glog .Error (desc )
512- return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
513- }
514-
515418 if bindViaLambda (service ) {
516419 // Copy instance and binding IDs into credentials to
517420 // be used as identifiers for resources we create in
@@ -520,18 +423,35 @@ func getBindingCredentials(
520423 // IAM User with this information, and avoid the need
521424 // to have persist extra identifiers, or have users
522425 // provide them.
523- credentials [instanceId ] = binding .InstanceID
524- credentials [bindingId ] = binding .ID
426+ credentials ["INSTANCE_ID" ] = binding .InstanceID
427+ credentials ["BINDING_ID" ] = binding .ID
525428
526429 // Replace credentials with a derived set calculated by a lambda function
527430 credentials , err = invokeLambdaBindFunc (sess , b .Clients .NewLambda , credentials , "bind" )
528431 if err != nil {
529- glog .Error (err )
530432 return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , err .Error ())
531433 }
532434 }
533435
534- return credentials , nil
436+ // Store the binding
437+ err = b .db .DataStorePort .PutServiceBinding (* binding )
438+ if err != nil {
439+ desc := fmt .Sprintf ("Failed to store the service binding %s: %v" , binding .ID , err )
440+ return nil , newHTTPStatusCodeError (http .StatusInternalServerError , "" , desc )
441+ }
442+
443+ b .metrics .Actions .With (
444+ prom.Labels {
445+ "action" : "bind" ,
446+ "service" : service .Name ,
447+ "plan" : "" ,
448+ }).Inc ()
449+
450+ return & broker.BindResponse {
451+ BindResponse : osb.BindResponse {
452+ Credentials : credentials ,
453+ },
454+ }, nil
535455}
536456
537457// Unbind is executed when the OSB API receives `DELETE /v2/service_instances/:instance_id/service_bindings/:binding_id`
0 commit comments