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
}