func validateGetAttAttributeID(builtin parse.IntrinsicFunction, resourceID, attributeID interface{}, ctx PropertyContext) reporting.Reports { resource := ctx.Template().Resources[resourceID.(string)] definition := ctx.Definitions().Lookup(resource.Type) switch t := attributeID.(type) { case string: if attribute, ok := definition.Attributes[t]; ok { targetType := attribute.Type switch targetType.CoercibleTo(ctx.Property().Type) { case CoercionNever: return reporting.Reports{reporting.NewFailure(ctx, "GetAtt value of %s.%s is %s but is being assigned to a %s property", resourceID, t, targetType.Describe(), ctx.Property().Type.Describe())} case CoercionBegrudgingly: return reporting.Reports{reporting.NewWarning(ctx, "GetAtt value of %s.%s is %s but is being dangerously coerced to a %s property", resourceID, t, targetType.Describe(), ctx.Property().Type.Describe())} } return nil } case parse.IntrinsicFunction: getAttAttributeNameType := Schema{Type: ValueString} _, errs := ValidateIntrinsicFunctions(t, NewPropertyContext(ctx, getAttAttributeNameType), SupportedFunctions{ parse.FnRef: true, }) return errs } return reporting.Reports{reporting.NewFailure(ctx, "%s is not an attribute of %s", attributeID, resource.Type)} }
func coerce(from, to PropertyType, ctx PropertyContext) reporting.Reports { switch from.CoercibleTo(to) { case CoercionNever: return reporting.Reports{ reporting.NewFailure(ctx, "Value of %s used in %s property", from.Describe(), to.Describe()), } case CoercionBegrudgingly: return reporting.Reports{ reporting.NewWarning(ctx, "%s is dangerously coerced to a %s property", from.Describe(), to.Describe()), } case CoercionAlways: return nil } return nil }
func (p Properties) Validate(ctx ResourceContext) reporting.Reports { failures := make(reporting.Reports, 0, len(p)*2) visited := make(map[string]bool) self := ctx.CurrentResource() for key, schema := range p { visited[key] = true value, _ := self.PropertyValue(key) keyCtx := ResourceContextAdd(ctx, key) // Validate conflicting properties if value != nil && schema.Conflicts != nil && schema.Conflicts.Pass(self) { failures = append(failures, reporting.NewFailure(keyCtx, "Conflict: %s", schema.Conflicts.Describe(self))) } // Validate Required if value == nil && schema.Required != nil && schema.Required.Pass(self) { failures = append(failures, reporting.NewFailure(keyCtx, "%s is required because %s", key, schema.Required.Describe(self))) } // Validate Deprecated if value != nil && schema.Deprecated != nil { failures = append(failures, reporting.NewWarning(keyCtx, schema.Deprecated.Describe())) continue } // assuming the above either failed and logged some failures, or passed and // we can safely skip over a nil property if value == nil { continue } if _, errs := schema.Validate(value, keyCtx); errs != nil { failures = append(failures, errs...) } } // Reject any properties we weren't expecting for _, key := range self.Properties() { if !visited[key] { failures = append(failures, reporting.NewFailure(ResourceContextAdd(ctx, key), "%s is not a property of %s", key, self.AwsType())) } } return reporting.Safe(failures) }
// validateMapWhereArrayShouldBe runs validations against a map which was found // where an Array was expected; this is possibly valid, and could either be a // function reference or some attempt at coercion. func validateMapWhereArrayShouldBe(arrayType ArrayPropertyType, itemSchema Schema, value map[string]interface{}, ctx PropertyContext) (reporting.ValidateResult, reporting.Reports) { if ctx.Options()[OptionExperimentDisableObjectArrayCoercion] { return reporting.ValidateOK, reporting.Reports{reporting.NewFailure(ctx, "%s used instead of %s", arrayType.Unwrap().Describe(), arrayType.Describe())} } // CloudFormation appears to allow you to flatten a single item array // for array properties, e.g. X: [Y] can be specified as X: Y // // So in this case if we get a map here just validate it against the // schema for the item of the array and provide a warning. results := make(reporting.Reports, 0, 25) if _, errs := itemSchema.Validate(value, ctx); errs != nil { results = append(results, errs...) } results = append(results, reporting.NewWarning(ctx, "%s used instead of %s", arrayType.Unwrap().Describe(), arrayType.Describe())) return reporting.ValidateOK, reporting.Safe(results) }
func validateSelectIndex(builtin parse.IntrinsicFunction, index interface{}, array interface{}, ctx PropertyContext) reporting.Reports { if index == nil { return reporting.Reports{reporting.NewFailure(ctx, "Index cannot be null")} } switch t := index.(type) { case string: return reporting.Reports{reporting.NewWarning(ctx, "Wrong type for index %T (can't ensure validity of index)", index)} case float64: return validateIndexNumericalValue(t, array, ctx) case parse.IntrinsicFunction: indexType := Schema{Type: ValueNumber} _, errs := ValidateIntrinsicFunctions(t, NewPropertyContext(ctx, indexType), SupportedFunctions{ parse.FnRef: true, parse.FnFindInMap: true, }) return errs } return reporting.Reports{reporting.NewFailure(ctx, "Invalid value for index %#v", index)} }
func validateRef(builtin parse.IntrinsicFunction, ctx PropertyContext) reporting.Reports { if errs := validateIntrinsicFunctionBasicCriteria(parse.FnRef, builtin, ctx); errs != nil { return errs } refValue := builtin.UnderlyingMap[string(parse.FnRef)] refString, ok := refValue.(string) if !ok || refString == "" { return reporting.Reports{reporting.NewFailure(ctx, "Invalid type for \"Ref\" key: %T", refValue)} } target := resolveTarget(refString, ctx.Definitions(), ctx.Template()) if target == nil { return reporting.Reports{reporting.NewFailure(ctx, "Ref '%s' is not a resource, parameter, or pseudo-parameter", refString)} } targetType := target.TargetType() if targetType == nil { return reporting.Reports{reporting.NewFailure(ctx, "%s cannot be used in a Ref", refString)} } if refString == "AWS::NoValue" { // AWS::NoValue is a magic absent-value value so we don't do any type // assertions on it return nil } switch targetType.CoercibleTo(ctx.Property().Type) { case CoercionNever: return reporting.Reports{reporting.NewFailure(ctx, "Ref value of '%s' is %s but is being assigned to a %s property", refString, targetType.Describe(), ctx.Property().Type.Describe())} case CoercionBegrudgingly: return reporting.Reports{reporting.NewWarning(ctx, "Ref value of '%s' is %s but is being dangerously coerced to a %s property", refString, targetType.Describe(), ctx.Property().Type.Describe())} } return nil }
func (UnsupportedProperties) Validate(ctx ResourceContext) reporting.Reports { return reporting.Reports{ reporting.NewWarning(ctx, "<unsupported> cfval does support validating %s resources yet", ctx.CurrentResource().AwsType()), } }