in pkg/broker/api.go [322:455]
func (b *AwsBroker) Bind(request *osb.BindRequest, c *broker.RequestContext) (*broker.BindResponse, error) {
glog.V(10).Infof("request=%+v", *request)
binding := &serviceinstance.ServiceBinding{
ID: request.BindingID,
InstanceID: request.InstanceID,
}
// Get the binding params
for k, v := range request.Parameters {
if strings.EqualFold(k, bindParamRoleName) {
binding.RoleName = paramValue(v)
} else if strings.EqualFold(k, bindParamScope) {
binding.Scope = paramValue(v)
} else {
desc := fmt.Sprintf("The parameter %s is not supported.", k)
return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
}
}
// Verify that the binding doesn't already exist
sb, err := b.db.DataStorePort.GetServiceBinding(binding.ID)
if err != nil {
desc := fmt.Sprintf("Failed to get the service binding %s: %v", binding.ID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
} else if sb != nil {
if sb.Match(binding) {
glog.Infof("Service binding %s already exists.", binding.ID)
response := broker.BindResponse{}
response.Exists = true
return &response, nil
}
desc := fmt.Sprintf("Service binding %s already exists but with different attributes.", binding.ID)
return nil, newHTTPStatusCodeError(http.StatusConflict, "", desc)
}
// Get the service (this is only required because the USER_KEY_ID and
// USER_SECRET_KEY credentials need to be prefixed with the service name for
// backward compatibility)
service, err := b.db.DataStorePort.GetServiceDefinition(request.ServiceID)
if err != nil {
desc := fmt.Sprintf("Failed to get the service %s: %v", request.ServiceID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
} else if service == nil {
desc := fmt.Sprintf("The service %s was not found.", request.ServiceID)
return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
}
// Get the instance
instance, err := b.db.DataStorePort.GetServiceInstance(binding.InstanceID)
if err != nil {
desc := fmt.Sprintf("Failed to get the service instance %s: %v", binding.InstanceID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
} else if instance == nil {
desc := fmt.Sprintf("The service instance %s was not found.", binding.InstanceID)
return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
}
sess := b.GetSession(b.keyid, b.secretkey, b.region, b.accountId, b.profile, instance.Params)
// Get the CFN stack outputs
resp, err := b.Clients.NewCfn(sess).Client.DescribeStacks(&cloudformation.DescribeStacksInput{
StackName: aws.String(instance.StackID),
})
if err != nil {
desc := fmt.Sprintf("Failed to describe the CloudFormation stack %s: %v", instance.StackID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
}
// Get the credentials from the CFN stack outputs
credentials, err := getCredentials(service, resp.Stacks[0].Outputs, b.Clients.NewSsm(sess))
if err != nil {
desc := fmt.Sprintf("Failed to get the credentials from CloudFormation stack %s: %v", instance.StackID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
}
if binding.RoleName != "" {
policyArn, err := getPolicyArn(resp.Stacks[0].Outputs, binding.Scope)
if err != nil {
desc := fmt.Sprintf("The CloudFormation stack %s does not support binding with scope '%s': %v", instance.StackID, binding.Scope, err)
return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
}
// Attach the scoped policy to the role
_, err = b.Clients.NewIam(sess).AttachRolePolicy(&iam.AttachRolePolicyInput{
PolicyArn: aws.String(policyArn),
RoleName: aws.String(binding.RoleName),
})
if err != nil {
desc := fmt.Sprintf("Failed to attach the policy %s to role %s: %v", policyArn, binding.RoleName, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
}
binding.PolicyArn = policyArn
}
if bindViaLambda(service) {
// Copy instance and binding IDs into credentials to
// be used as identifiers for resources we create in
// lambda so that we can reference them when we unbind
// (for example, you can build a unique path for an
// IAM User with this information, and avoid the need
// to have persist extra identifiers, or have users
// provide them.
credentials["INSTANCE_ID"] = binding.InstanceID
credentials["BINDING_ID"] = binding.ID
// Replace credentials with a derived set calculated by a lambda function
credentials, err = invokeLambdaBindFunc(sess, b.Clients.NewLambda, credentials, "bind")
if err != nil {
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", err.Error())
}
}
// Store the binding
err = b.db.DataStorePort.PutServiceBinding(*binding)
if err != nil {
desc := fmt.Sprintf("Failed to store the service binding %s: %v", binding.ID, err)
return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
}
b.metrics.Actions.With(
prom.Labels{
"action": "bind",
"service": service.Name,
"plan": "",
}).Inc()
return &broker.BindResponse{
BindResponse: osb.BindResponse{
Credentials: credentials,
},
}, nil
}