in frontend/pkg/frontend/frontend.go [990:1134]
func (f *Frontend) ArmDeploymentPreflight(writer http.ResponseWriter, request *http.Request) {
var subscriptionID = request.PathValue(PathSegmentSubscriptionID)
var resourceGroup = request.PathValue(PathSegmentResourceGroupName)
ctx := request.Context()
logger := LoggerFromContext(ctx)
body, err := BodyFromContext(ctx)
if err != nil {
logger.Error(err.Error())
arm.WriteInternalServerError(writer)
return
}
deploymentPreflight, cloudError := arm.UnmarshalDeploymentPreflight(body)
if cloudError != nil {
logger.Error(cloudError.Error())
arm.WriteCloudError(writer, cloudError)
return
}
validate := api.NewValidator()
preflightErrors := []arm.CloudErrorBody{}
for index, raw := range deploymentPreflight.Resources {
var cloudError *arm.CloudError
preflightResource := &arm.DeploymentPreflightResource{}
err = json.Unmarshal(raw, preflightResource)
if err != nil {
cloudError = arm.NewInvalidRequestContentError(err)
// Preflight is best-effort: a malformed resource is not a validation failure.
logger.Warn(cloudError.Message)
}
switch strings.ToLower(preflightResource.Type) {
case strings.ToLower(api.ClusterResourceType.String()):
// This is just "preliminary" validation to ensure all the base resource
// fields are present and the API version is valid.
resourceErrors := api.ValidateRequest(validate, request.Method, preflightResource)
if len(resourceErrors) > 0 {
// Preflight is best-effort: a malformed resource is not a validation failure.
logger.Warn(
fmt.Sprintf("Resource #%d failed preliminary validation (see details)", index+1),
"details", resourceErrors)
continue
}
// API version is already validated by this point.
versionedInterface, _ := api.Lookup(preflightResource.APIVersion)
versionedCluster := versionedInterface.NewHCPOpenShiftCluster(nil)
err = preflightResource.Convert(versionedCluster)
if err != nil {
// Preflight is best effort: failure to parse a resource is not a validation failure.
logger.Warn(fmt.Sprintf("Failed to unmarshal %s resource named '%s': %s", preflightResource.Type, preflightResource.Name, err))
continue
}
// Perform static validation as if for a cluster creation request.
cloudError = versionedCluster.ValidateStatic(versionedCluster, false, http.MethodPut)
case strings.ToLower(api.NodePoolResourceType.String()):
// This is just "preliminary" validation to ensure all the base resource
// fields are present and the API version is valid.
resourceErrors := api.ValidateRequest(validate, request.Method, preflightResource)
if len(resourceErrors) > 0 {
// Preflight is best-effort: a malformed resource is not a validation failure.
logger.Warn(
fmt.Sprintf("Resource #%d failed preliminary validation (see details)", index+1),
"details", resourceErrors)
continue
}
// API version is already validated by this point.
versionedInterface, _ := api.Lookup(preflightResource.APIVersion)
versionedNodePool := versionedInterface.NewHCPOpenShiftClusterNodePool(nil)
err = preflightResource.Convert(versionedNodePool)
if err != nil {
// Preflight is best effort: failure to parse a resource is not a validation failure.
logger.Warn(fmt.Sprintf("Failed to unmarshal %s resource named '%s': %s", preflightResource.Type, preflightResource.Name, err))
continue
}
// Perform static validation as if for a node pool creation request.
cloudError = versionedNodePool.ValidateStatic(versionedNodePool, nil, false, http.MethodPut)
default:
// Disregard foreign resource types.
continue
}
if cloudError != nil {
var details []arm.CloudErrorBody
// This avoids double-nesting details when there's multiple errors.
//
// To illustrate, instead of:
//
// {
// "code": "MultipleErrorsOccurred"
// "message": "Content validation failed for {{RESOURCE_NAME}}"
// "target": "{{RESOURCE_ID}}"
// "details": [
// {
// "code": "MultipleErrorsOccurred"
// "message": "Content validation failed on multiple fields"
// "details": [
// ...field-specific validation errors...
// ]
// }
// ]
// }
//
// we want:
//
// {
// "code": "MultipleErrorsOccurred"
// "message": "Content validation failed for {{RESOURCE_NAME}}"
// "target": "{{RESOURCE_ID}}"
// "details": [
// ...field-specific validation errors...
// ]
// }
//
if len(cloudError.Details) > 0 {
details = cloudError.Details
} else {
details = []arm.CloudErrorBody{*cloudError.CloudErrorBody}
}
preflightErrors = append(preflightErrors, arm.CloudErrorBody{
Code: cloudError.Code,
Message: fmt.Sprintf("Content validation failed for '%s'", preflightResource.Name),
Target: preflightResource.ResourceID(subscriptionID, resourceGroup),
Details: details,
})
continue
}
// FIXME Further preflight steps go here.
}
arm.WriteDeploymentPreflightResponse(writer, preflightErrors)
}