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  }