From 6cca17532574baee9836fb9e199b7eaf2caa309b Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 14 May 2023 04:22:46 -0700 Subject: [PATCH] Update complex constraints to allow interpolation --- internal/schema/0.12/provisioners.go | 21 ++++++--- schema/convert_json.go | 69 +++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/internal/schema/0.12/provisioners.go b/internal/schema/0.12/provisioners.go index 1138b817..2d891537 100644 --- a/internal/schema/0.12/provisioners.go +++ b/internal/schema/0.12/provisioners.go @@ -82,8 +82,11 @@ var LocalExecProvisioner = &schema.BodySchema{ }, "interpreter": { IsOptional: true, - Constraint: schema.List{ - Elem: schema.AnyExpression{OfType: cty.String}, + Constraint: schema.OneOf{ + schema.AnyExpression{OfType: cty.List(cty.String), SkipLiteralComplexTypes: true}, + schema.List{ + Elem: schema.AnyExpression{OfType: cty.String}, + }, }, Description: lang.Markdown("If provided, this is a list of interpreter arguments used to execute " + "the command. The first argument is the interpreter itself. It can be provided as a relative " + @@ -117,8 +120,11 @@ var RemoteExecProvisioner = &schema.BodySchema{ Attributes: map[string]*schema.AttributeSchema{ "inline": { IsOptional: true, - Constraint: schema.List{ - Elem: schema.AnyExpression{OfType: cty.String}, + Constraint: schema.OneOf{ + schema.AnyExpression{OfType: cty.List(cty.String), SkipLiteralComplexTypes: true}, + schema.List{ + Elem: schema.AnyExpression{OfType: cty.String}, + }, }, Description: lang.Markdown("A list of command strings. They are executed in the order they are provided." + " This cannot be provided with `script` or `scripts`."), @@ -131,8 +137,11 @@ var RemoteExecProvisioner = &schema.BodySchema{ }, "scripts": { IsOptional: true, - Constraint: schema.List{ - Elem: schema.AnyExpression{OfType: cty.String}, + Constraint: schema.OneOf{ + schema.AnyExpression{OfType: cty.List(cty.String), SkipLiteralComplexTypes: true}, + schema.List{ + Elem: schema.AnyExpression{OfType: cty.String}, + }, }, Description: lang.Markdown("A list of paths (relative or absolute) to local scripts that will be copied " + "to the remote resource and then executed. They are executed in the order they are provided." + diff --git a/schema/convert_json.go b/schema/convert_json.go index ca044153..24ecf1d6 100644 --- a/schema/convert_json.go +++ b/schema/convert_json.go @@ -195,27 +195,47 @@ func exprConstraintFromSchemaAttribute(attr *tfjson.SchemaAttribute) schema.Cons return convertAttributeTypeToConstraint(attr.AttributeType) } if attr.AttributeNestedType != nil { + var attrType cty.Type + var cons schema.Constraint + + objType := convertJsonAttributesToCtyObject(attr.AttributeNestedType.Attributes) + objectCons := convertJsonAttributesToObjectConstraint(attr.AttributeNestedType.Attributes) + switch attr.AttributeNestedType.NestingMode { case tfjson.SchemaNestingModeSingle: - return convertJsonAttributesToObjectConstraint(attr.AttributeNestedType.Attributes) + attrType = objType + cons = objectCons case tfjson.SchemaNestingModeList: - return schema.List{ - Elem: convertJsonAttributesToObjectConstraint(attr.AttributeNestedType.Attributes), + attrType = cty.List(objType) + cons = schema.List{ + Elem: objectCons, MinItems: attr.AttributeNestedType.MinItems, MaxItems: attr.AttributeNestedType.MaxItems, } case tfjson.SchemaNestingModeSet: - return schema.Set{ - Elem: convertJsonAttributesToObjectConstraint(attr.AttributeNestedType.Attributes), + attrType = cty.Set(objType) + cons = schema.Set{ + Elem: objectCons, MinItems: attr.AttributeNestedType.MinItems, MaxItems: attr.AttributeNestedType.MaxItems, } case tfjson.SchemaNestingModeMap: - return schema.Map{ - Elem: convertJsonAttributesToObjectConstraint(attr.AttributeNestedType.Attributes), + attrType = cty.Map(objType) + cons = schema.Map{ + Elem: objectCons, MinItems: attr.AttributeNestedType.MinItems, MaxItems: attr.AttributeNestedType.MaxItems, } + default: + return nil + } + + return schema.OneOf{ + schema.AnyExpression{ + OfType: attrType, + SkipLiteralComplexTypes: true, + }, + cons, } } return nil @@ -300,6 +320,41 @@ func convertJsonAttributesToObjectConstraint(attrs map[string]*tfjson.SchemaAttr } } +func convertJsonAttributesToCtyObject(attrs map[string]*tfjson.SchemaAttribute) cty.Type { + optional := make([]string, 0) + attributes := make(map[string]cty.Type, 0) + + for name, attr := range attrs { + attributes[name] = convertJsonAttributeToCtyType(attr) + if attr.Optional { + optional = append(optional, name) + } + } + + return cty.ObjectWithOptionalAttrs(attributes, optional) +} + +func convertJsonAttributeToCtyType(attr *tfjson.SchemaAttribute) cty.Type { + if attr.AttributeType != cty.NilType { + return attr.AttributeType + } + if attr.AttributeNestedType != nil { + objType := convertJsonAttributesToCtyObject(attr.AttributeNestedType.Attributes) + + switch attr.AttributeNestedType.NestingMode { + case tfjson.SchemaNestingModeSingle: + return objType + case tfjson.SchemaNestingModeList: + return cty.List(objType) + case tfjson.SchemaNestingModeSet: + return cty.Set(objType) + case tfjson.SchemaNestingModeMap: + return cty.Map(objType) + } + } + return cty.NilType +} + func markupContent(value string, kind tfjson.SchemaDescriptionKind) lang.MarkupContent { if value == "" { return lang.MarkupContent{}