func()

in pkg/broker/api.go [58:201]


func (b *AwsBroker) Provision(request *osb.ProvisionRequest, c *broker.RequestContext) (*broker.ProvisionResponse, error) {
	glog.V(10).Infof("request=%+v", *request)

	if !request.AcceptsIncomplete {
		return nil, newAsyncError()
	}

	// Get the context
	cluster := getCluster(request.Context)
	namespace := getNamespace(request.Context)

	// Get the service
	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 plan
	plan := getPlan(service, request.PlanID)
	glog.V(10).Infof("plan=%v", plan)
	if plan == nil {
		desc := fmt.Sprintf("The service plan %s was not found.", request.PlanID)
		return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
	}

	// Get the parameters and verify that all required parameters are set
	params := getPlanDefaults(plan)
	glog.V(10).Infof("params=%v", params)
	availableParams := getAvailableParams(plan)
	glog.V(10).Infof("availableParams=%v", availableParams)
	for k, v := range getOverrides(b.brokerid, availableParams, namespace, service.Name, cluster) {
		params[k] = v
	}
	glog.V(10).Infof("params=%v", params)
	for k, v := range getPlanPrescribedParams(plan.Schemas.ServiceInstance.Create.Parameters) {
		params[k] = paramValue(v)
	}
	glog.V(10).Infof("params=%v", params)
	for k, v := range request.Parameters {
		if !stringInSlice(k, availableParams) {
			desc := fmt.Sprintf("The parameter %s is not available.", k)
			return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
		}
		params[k] = paramValue(v)
	}
	for _, p := range getRequiredParams(plan) {
		if _, ok := params[p]; !ok {
			desc := fmt.Sprintf("The parameter %s is required.", p)
			return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
		}
	}
	glog.V(10).Infof("params=%v", params)

	instance := &serviceinstance.ServiceInstance{
		ID:        request.InstanceID,
		ServiceID: request.ServiceID,
		Params:    params,
		PlanID:    request.PlanID,
	}

	// Verify that the instance doesn't already exist
	i, err := b.db.DataStorePort.GetServiceInstance(instance.ID)
	if err != nil {
		desc := fmt.Sprintf("Failed to get the service instance %s: %v", instance.ID, err)
		return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
	} else if i != nil {
		// TODO: This logic could use some love. The docs state that 200 OK MUST be
		// returned if the service instance already exists, is fully provisioned,
		// and the requested parameters are identical to the existing service
		// instance. Right now, this doesn't check whether the instance is fully
		// provisioned, and the reflect.DeepEqual check in Match will return false
		// if the parameter order is different.
		if i.Match(instance) {
			glog.Infof("Service instance %s already exists.", instance.ID)
			response := broker.ProvisionResponse{}
			response.Exists = true
			return &response, nil
		}
		glog.V(10).Infof("i=%+v instance=%+v", *i, *instance)
		desc := fmt.Sprintf("Service instance %s already exists but with different attributes.", instance.ID)
		return nil, newHTTPStatusCodeError(http.StatusConflict, "", desc)
	}

	tags, err := buildTags(b.brokerid, request.InstanceID, cluster, namespace, params)
	if err != nil {
		desc := fmt.Sprintf("failed to parse tags: %v", err)
		return nil, newHTTPStatusCodeError(http.StatusBadRequest, "", desc)
	}

	urlP := b.generateS3HTTPUrl(service.Name)
	stackName := getStackName(service.Name, instance.ID)
	capabilities := []string{cloudformation.CapabilityCapabilityNamedIam}
	cfnParams := toCFNParams(params)

	// Create the CFN stack
	cfnSvc := b.Clients.NewCfn(b.GetSession(b.keyid, b.secretkey, b.region, b.accountId, b.profile, params))
	resp, err := cfnSvc.Client.CreateStack(&cloudformation.CreateStackInput{
		Capabilities: aws.StringSlice(capabilities),
		Parameters:   cfnParams,
		StackName:    aws.String(stackName),
		Tags:         tags,
		TemplateURL:  urlP,
	})
	if err != nil {
		var url string
		if urlP != nil {
			url = *urlP
		}
		glog.Errorf("TemplateURL: %s", url)
		glog.Errorf("Tags: %v", tags)
		glog.Errorf("StackName:  %s", stackName)
		glog.Errorf("Parameters: %v", toCFNParams(params))
		glog.Errorf("Capabilities: %v ", capabilities)
		desc := fmt.Sprintf("Failed to create the CloudFormation stack: %v", err)
		return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
	}

	instance.StackID = aws.StringValue(resp.StackId)
	err = b.db.DataStorePort.PutServiceInstance(*instance)
	if err != nil {
		// Try to delete the stack
		if _, err := cfnSvc.Client.DeleteStack(&cloudformation.DeleteStackInput{StackName: aws.String(instance.StackID)}); err != nil {
			glog.Errorf("Failed to delete the CloudFormation stack %s: %v", instance.StackID, err)
		}

		desc := fmt.Sprintf("Failed to create the service instance %s: %v", request.InstanceID, err)
		return nil, newHTTPStatusCodeError(http.StatusInternalServerError, "", desc)
	}

	b.metrics.Actions.With(
		prom.Labels{
			"action":  "provision",
			"service": service.Name,
			"plan":    plan.Name,
		}).Inc()

	response := broker.ProvisionResponse{}
	response.Async = true
	return &response, nil
}