Skip to content

Commit ce13226

Browse files
committed
Revert "Return correct response for creating instances and bindings that already exist"
This reverts commit 5ae429d.
1 parent 764edfc commit ce13226

File tree

2 files changed

+62
-147
lines changed

2 files changed

+62
-147
lines changed

pkg/broker/api.go

Lines changed: 61 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package broker
22

33
import (
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
2721
func (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).
366322
func (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`

pkg/serviceinstance/serviceinstance.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,8 @@ type ServiceInstance struct {
1111
StackID string
1212
}
1313

14-
// Match returns true if the other service instance has the same attributes.
15-
// StackID is ignored for correct comparing ServiceInstance got from database and ServiceInstance got from API request
1614
func (i *ServiceInstance) Match(other *ServiceInstance) bool {
17-
return i.ID == other.ID &&
18-
i.ServiceID == other.ServiceID &&
19-
i.PlanID == other.PlanID &&
20-
reflect.DeepEqual(i.Params, other.Params)
15+
return reflect.DeepEqual(i, other)
2116
}
2217

2318
// ServiceBinding represents a service binding.

0 commit comments

Comments
 (0)