github.com/kjmkznr/terraform@v0.5.2-0.20180216194316-1d0f5fdac99e/configs/named_values.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl2/gohcl" 7 "github.com/hashicorp/hcl2/hcl" 8 "github.com/hashicorp/hcl2/hcl/hclsyntax" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 // A consistent detail message for all "not a valid identifier" diagnostics. 13 const badIdentifierDetail = "A name must start with a letter and may contain only letters, digits, underscores, and dashes." 14 15 // Variable represents a "variable" block in a module or file. 16 type Variable struct { 17 Name string 18 Description string 19 Default cty.Value 20 TypeHint VariableTypeHint 21 22 DescriptionSet bool 23 24 DeclRange hcl.Range 25 } 26 27 func decodeVariableBlock(block *hcl.Block) (*Variable, hcl.Diagnostics) { 28 v := &Variable{ 29 Name: block.Labels[0], 30 DeclRange: block.DefRange, 31 } 32 33 content, diags := block.Body.Content(variableBlockSchema) 34 35 if !hclsyntax.ValidIdentifier(v.Name) { 36 diags = append(diags, &hcl.Diagnostic{ 37 Severity: hcl.DiagError, 38 Summary: "Invalid variable name", 39 Detail: badIdentifierDetail, 40 Subject: &block.LabelRanges[0], 41 }) 42 } 43 44 // Don't allow declaration of variables that would conflict with the 45 // reserved attribute and block type names in a "module" block, since 46 // these won't be usable for child modules. 47 for _, attr := range moduleBlockSchema.Attributes { 48 if attr.Name == v.Name { 49 diags = append(diags, &hcl.Diagnostic{ 50 Severity: hcl.DiagError, 51 Summary: "Invalid variable name", 52 Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", attr.Name), 53 Subject: &block.LabelRanges[0], 54 }) 55 } 56 } 57 58 if attr, exists := content.Attributes["description"]; exists { 59 valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description) 60 diags = append(diags, valDiags...) 61 v.DescriptionSet = true 62 } 63 64 if attr, exists := content.Attributes["default"]; exists { 65 val, valDiags := attr.Expr.Value(nil) 66 diags = append(diags, valDiags...) 67 v.Default = val 68 } 69 70 if attr, exists := content.Attributes["type"]; exists { 71 expr, shimDiags := shimTraversalInString(attr.Expr, true) 72 diags = append(diags, shimDiags...) 73 74 switch hcl.ExprAsKeyword(expr) { 75 case "string": 76 v.TypeHint = TypeHintString 77 case "list": 78 v.TypeHint = TypeHintList 79 case "map": 80 v.TypeHint = TypeHintMap 81 default: 82 diags = append(diags, &hcl.Diagnostic{ 83 Severity: hcl.DiagError, 84 Summary: "Invalid variable type hint", 85 Detail: "The type argument requires one of the following keywords: string, list, or map.", 86 Subject: expr.Range().Ptr(), 87 }) 88 } 89 } 90 91 return v, diags 92 } 93 94 // Output represents an "output" block in a module or file. 95 type Output struct { 96 Name string 97 Description string 98 Expr hcl.Expression 99 DependsOn []hcl.Traversal 100 Sensitive bool 101 102 DescriptionSet bool 103 SensitiveSet bool 104 105 DeclRange hcl.Range 106 } 107 108 func decodeOutputBlock(block *hcl.Block, override bool) (*Output, hcl.Diagnostics) { 109 o := &Output{ 110 Name: block.Labels[0], 111 DeclRange: block.DefRange, 112 } 113 114 schema := outputBlockSchema 115 if override { 116 schema = schemaForOverrides(schema) 117 } 118 119 content, diags := block.Body.Content(schema) 120 121 if !hclsyntax.ValidIdentifier(o.Name) { 122 diags = append(diags, &hcl.Diagnostic{ 123 Severity: hcl.DiagError, 124 Summary: "Invalid output name", 125 Detail: badIdentifierDetail, 126 Subject: &block.LabelRanges[0], 127 }) 128 } 129 130 if attr, exists := content.Attributes["description"]; exists { 131 valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Description) 132 diags = append(diags, valDiags...) 133 o.DescriptionSet = true 134 } 135 136 if attr, exists := content.Attributes["value"]; exists { 137 o.Expr = attr.Expr 138 } 139 140 if attr, exists := content.Attributes["sensitive"]; exists { 141 valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Sensitive) 142 diags = append(diags, valDiags...) 143 o.SensitiveSet = true 144 } 145 146 if attr, exists := content.Attributes["depends_on"]; exists { 147 deps, depsDiags := decodeDependsOn(attr) 148 diags = append(diags, depsDiags...) 149 o.DependsOn = append(o.DependsOn, deps...) 150 } 151 152 return o, diags 153 } 154 155 // Local represents a single entry from a "locals" block in a module or file. 156 // The "locals" block itself is not represented, because it serves only to 157 // provide context for us to interpret its contents. 158 type Local struct { 159 Name string 160 Expr hcl.Expression 161 162 DeclRange hcl.Range 163 } 164 165 func decodeLocalsBlock(block *hcl.Block) ([]*Local, hcl.Diagnostics) { 166 attrs, diags := block.Body.JustAttributes() 167 if len(attrs) == 0 { 168 return nil, diags 169 } 170 171 locals := make([]*Local, 0, len(attrs)) 172 for name, attr := range attrs { 173 if !hclsyntax.ValidIdentifier(name) { 174 diags = append(diags, &hcl.Diagnostic{ 175 Severity: hcl.DiagError, 176 Summary: "Invalid local value name", 177 Detail: badIdentifierDetail, 178 Subject: &attr.NameRange, 179 }) 180 } 181 182 locals = append(locals, &Local{ 183 Name: name, 184 Expr: attr.Expr, 185 DeclRange: attr.Range, 186 }) 187 } 188 return locals, diags 189 } 190 191 var variableBlockSchema = &hcl.BodySchema{ 192 Attributes: []hcl.AttributeSchema{ 193 { 194 Name: "description", 195 }, 196 { 197 Name: "default", 198 }, 199 { 200 Name: "type", 201 }, 202 }, 203 } 204 205 var outputBlockSchema = &hcl.BodySchema{ 206 Attributes: []hcl.AttributeSchema{ 207 { 208 Name: "description", 209 }, 210 { 211 Name: "value", 212 Required: true, 213 }, 214 { 215 Name: "depends_on", 216 }, 217 { 218 Name: "sensitive", 219 }, 220 }, 221 }