func()

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
}