From 13fa73c63e36b5fb22867d094c7d3e1c958c6dae Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 1 Feb 2018 20:33:06 -0800 Subject: [PATCH] configs: stub out main configuration structs These types represent the individual elements within configuration, the modules a configuration is made of, and the configuration (static module tree) itself. --- configs/backend.go | 14 +++++ configs/config.go | 30 +++++++++ configs/module.go | 66 ++++++++++++++++++++ configs/module_call.go | 18 ++++++ configs/named_values.go | 37 +++++++++++ configs/provider.go | 28 +++++++++ configs/provisioneronfailure_string.go | 16 +++++ configs/provisionerwhen_string.go | 16 +++++ configs/resource.go | 86 ++++++++++++++++++++++++++ configs/variable_type_hint.go | 45 ++++++++++++++ configs/variabletypehint_string.go | 29 +++++++++ configs/version_constraint.go | 15 +++++ 12 files changed, 400 insertions(+) create mode 100644 configs/backend.go create mode 100644 configs/config.go create mode 100644 configs/module.go create mode 100644 configs/module_call.go create mode 100644 configs/named_values.go create mode 100644 configs/provider.go create mode 100644 configs/provisioneronfailure_string.go create mode 100644 configs/provisionerwhen_string.go create mode 100644 configs/resource.go create mode 100644 configs/variable_type_hint.go create mode 100644 configs/variabletypehint_string.go create mode 100644 configs/version_constraint.go diff --git a/configs/backend.go b/configs/backend.go new file mode 100644 index 000000000..43e872bed --- /dev/null +++ b/configs/backend.go @@ -0,0 +1,14 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" +) + +// Backend represents a "backend" block inside a "terraform" block in a module +// or file. +type Backend struct { + Type string + Config hcl.Body + + DeclRange hcl.Range +} diff --git a/configs/config.go b/configs/config.go new file mode 100644 index 000000000..07f6ed0a5 --- /dev/null +++ b/configs/config.go @@ -0,0 +1,30 @@ +package configs + +// A Config is a node in the tree of modules within a configuration. +// +// The module tree is constructed by following ModuleCall instances recursively +// through the root module transitively into descendent modules. +// +// A module tree described in *this* package represents the static tree +// represented by configuration. During evaluation a static ModuleNode may +// expand into zero or more module instances depending on the use of count and +// for_each configuration attributes within each call. +type Config struct { + // RootModule points to the Config for the root module within the same + // module tree as this module. If this module _is_ the root module then + // this is self-referential. + Root *Config + + // ParentModule points to the Config for the module that directly calls + // this module. If this is the root module then this field is nil. + Parent *Config + + // ChildModules points to the Config for each of the direct child modules + // called from this module. The keys in this map match the keys in + // Module.ModuleCalls. + Children map[string]*Config + + // Elements points to the object describing the configuration for the + // various elements (variables, resources, etc) defined by this module. + Elements *Module +} diff --git a/configs/module.go b/configs/module.go new file mode 100644 index 000000000..5b9f51041 --- /dev/null +++ b/configs/module.go @@ -0,0 +1,66 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" +) + +// Module is a container for a set of configuration constructs that are +// evaluated within a common namespace. +type Module struct { + CoreVersionConstraints []VersionConstraint + + Backend *Backend + ProviderConfigs map[string]*Provider + ProviderRequirements map[string][]VersionConstraint + + Variables map[string]*Variable + Locals map[string]*Local + Outputs map[string]*Output + + ModuleCalls map[string]*ModuleCall + + ManagedResources map[string]*ManagedResource + DataResources map[string]*DataResource +} + +// NewModule takes a list of primary files and a list of override files and +// produces a *Module by combining the files together. +// +// If there are any conflicting declarations in the given files -- for example, +// if the same variable name is defined twice -- then the resulting module +// will be incomplete and error diagnostics will be returned. Careful static +// analysis of the returned Module is still possible in this case, but the +// module will probably not be semantically valid. +func NewModule(primaryFiles, overrideFiles []*File) (*Module, hcl.Diagnostics) { + // TODO: process each file in turn, combining and merging as necessary + // to produce a single flat *Module. + panic("NewModule not yet implemented") +} + +// File describes the contents of a single configuration file. +// +// Individual files are not usually used alone, but rather combined together +// with other files (conventionally, those in the same directory) to produce +// a *Module, using NewModule. +// +// At the level of an individual file we represent directly the structural +// elements present in the file, without any attempt to detect conflicting +// declarations. A File object can therefore be used for some basic static +// analysis of individual elements, but must be built into a Module to detect +// duplicate declarations. +type File struct { + CoreVersionConstraints []*VersionConstraint + + Backends []*Backend + ProviderConfigs []*Provider + ProviderRequirements []*ProviderRequirement + + Variables []*Variable + Locals []*Local + Outputs []*Output + + ModuleCalls []*ModuleCall + + ManagedResources []*ManagedResource + DataResources []*DataResource +} diff --git a/configs/module_call.go b/configs/module_call.go new file mode 100644 index 000000000..ca3205cc4 --- /dev/null +++ b/configs/module_call.go @@ -0,0 +1,18 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" +) + +// ModuleCall represents a "module" block in a module or file. +type ModuleCall struct { + Source string + SourceRange hcl.Range + + Version VersionConstraint + + Count hcl.Expression + ForEach hcl.Expression + + DeclRange hcl.Range +} diff --git a/configs/named_values.go b/configs/named_values.go new file mode 100644 index 000000000..c2d2c8b83 --- /dev/null +++ b/configs/named_values.go @@ -0,0 +1,37 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" + "github.com/zclconf/go-cty/cty" +) + +// Variable represents a "variable" block in a module or file. +type Variable struct { + Name string + Description string + Default cty.Value + TypeHint VariableTypeHint + + DeclRange hcl.Range +} + +// Output represents an "output" block in a module or file. +type Output struct { + Name string + Description string + Expr hcl.Expression + DependsOn []hcl.Traversal + Sensitive bool + + DeclRange hcl.Range +} + +// Local represents a single entry from a "locals" block in a module or file. +// The "locals" block itself is not represented, because it serves only to +// provide context for us to interpret its contents. +type Local struct { + Name string + Expr hcl.Expression + + DeclRange hcl.Range +} diff --git a/configs/provider.go b/configs/provider.go new file mode 100644 index 000000000..8145a87dc --- /dev/null +++ b/configs/provider.go @@ -0,0 +1,28 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" +) + +// Provider represents a "provider" block in a module or file. A provider +// block is a provider configuration, and there can be zero or more +// configurations for each actual provider. +type Provider struct { + Name string + Alias string + AliasRange hcl.Range + + Version VersionConstraint + + Config hcl.Body + + DeclRange hcl.Range +} + +// ProviderRequirement represents a declaration of a dependency on a particular +// provider version without actually configuring that provider. This is used in +// child modules that expect a provider to be passed in from their parent. +type ProviderRequirement struct { + Name string + Requirement VersionConstraint +} diff --git a/configs/provisioneronfailure_string.go b/configs/provisioneronfailure_string.go new file mode 100644 index 000000000..8704b0861 --- /dev/null +++ b/configs/provisioneronfailure_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type ProvisionerOnFailure"; DO NOT EDIT. + +package configs + +import "strconv" + +const _ProvisionerOnFailure_name = "ProvisionerOnFailureInvalidProvisionerOnFailureContinueProvisionerOnFailureFail" + +var _ProvisionerOnFailure_index = [...]uint8{0, 27, 55, 79} + +func (i ProvisionerOnFailure) String() string { + if i < 0 || i >= ProvisionerOnFailure(len(_ProvisionerOnFailure_index)-1) { + return "ProvisionerOnFailure(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ProvisionerOnFailure_name[_ProvisionerOnFailure_index[i]:_ProvisionerOnFailure_index[i+1]] +} diff --git a/configs/provisionerwhen_string.go b/configs/provisionerwhen_string.go new file mode 100644 index 000000000..cbecb20ae --- /dev/null +++ b/configs/provisionerwhen_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type ProvisionerWhen"; DO NOT EDIT. + +package configs + +import "strconv" + +const _ProvisionerWhen_name = "ProvisionerWhenInvalidProvisionerWhenCreateProvisionerWhenDestroy" + +var _ProvisionerWhen_index = [...]uint8{0, 22, 43, 65} + +func (i ProvisionerWhen) String() string { + if i < 0 || i >= ProvisionerWhen(len(_ProvisionerWhen_index)-1) { + return "ProvisionerWhen(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ProvisionerWhen_name[_ProvisionerWhen_index[i]:_ProvisionerWhen_index[i+1]] +} diff --git a/configs/resource.go b/configs/resource.go new file mode 100644 index 000000000..459ce0d3d --- /dev/null +++ b/configs/resource.go @@ -0,0 +1,86 @@ +package configs + +import ( + "github.com/hashicorp/hcl2/hcl" +) + +// ManagedResource represents a "resource" block in a module or file. +type ManagedResource struct { + Name string + Type string + Config hcl.Body + Count hcl.Expression + ForEach hcl.Expression + + ProviderConfigAddr hcl.Traversal + + DependsOn []hcl.Traversal + + Connection *Connection + Provisioners []*Provisioner + + CreateBeforeDestroy bool + PreventDestroy bool + IgnoreChanges []hcl.Traversal + + DeclRange hcl.Range +} + +// DataResource represents a "data" block in a module or file. +type DataResource struct { + Name string + Type string + Config hcl.Body + Count hcl.Expression + ForEach hcl.Expression + + ProviderConfigAddr hcl.Traversal + + DependsOn []hcl.Traversal + + DeclRange hcl.Range +} + +// Provisioner represents a "provisioner" block when used within a +// "resource" block in a module or file. +type Provisioner struct { + Type string + Config hcl.Body + Connection *Connection + When ProvisionerWhen + OnFailure ProvisionerOnFailure + + DeclRange hcl.Range +} + +// Connection represents a "connection" block when used within either a +// "resource" or "provisioner" block in a module or file. +type Connection struct { + Type string + Config hcl.Body + + DeclRange hcl.Range +} + +// ProvisionerWhen is an enum for valid values for when to run provisioners. +type ProvisionerWhen int + +//go:generate stringer -type ProvisionerWhen + +const ( + ProvisionerWhenInvalid ProvisionerWhen = iota + ProvisionerWhenCreate + ProvisionerWhenDestroy +) + +// ProvisionerOnFailure is an enum for valid values for on_failure options +// for provisioners. +type ProvisionerOnFailure int + +//go:generate stringer -type ProvisionerOnFailure + +const ( + ProvisionerOnFailureInvalid ProvisionerOnFailure = iota + ProvisionerOnFailureContinue + ProvisionerOnFailureFail +) diff --git a/configs/variable_type_hint.go b/configs/variable_type_hint.go new file mode 100644 index 000000000..204efd120 --- /dev/null +++ b/configs/variable_type_hint.go @@ -0,0 +1,45 @@ +package configs + +// VariableTypeHint is an enumeration used for the Variable.TypeHint field, +// which is an incompletely-specified type for the variable which is used +// as a hint for whether a value provided in an ambiguous context (on the +// command line or in an environment variable) should be taken literally as a +// string or parsed as an HCL expression to produce a data structure. +// +// The type hint is applied to runtime values as well, but since it does not +// accurately describe a precise type it is not fully-sufficient to infer +// the dynamic type of a value passed through a variable. +// +// These hints use inaccurate terminology for historical reasons. Full details +// are in the documentation for each constant in this enumeration, but in +// summary: +// +// TypeHintString requires a primitive type +// TypeHintList requires a type that could be converted to a tuple +// TypeHintMap requires a type that could be converted to an object +type VariableTypeHint rune + +//go:generate stringer -type VariableTypeHint + +// TypeHintNone indicates the absense of a type hint. Values specified in +// ambiguous contexts will be treated as literal strings, as if TypeHintString +// were selected, but no runtime value checks will be applied. This is reasonable +// type hint for a module that is never intended to be used at the top-level +// of a configuration, since descendent modules never recieve values from +// ambiguous contexts. +const TypeHintNone VariableTypeHint = 0 + +// TypeHintString spec indicates that a value provided in an ambiguous context +// should be treated as a literal string, and additionally requires that the +// runtime value for the variable is of a primitive type (string, number, bool). +const TypeHintString VariableTypeHint = 'S' + +// TypeHintList indicates that a value provided in an ambiguous context should +// be treated as an HCL expression, and additionally requires that the +// runtime value for the variable is of an tuple, list, or set type. +const TypeHintList VariableTypeHint = 'L' + +// TypeHintMap indicates that a value provided in an ambiguous context should +// be treated as an HCL expression, and additionally requires that the +// runtime value for the variable is of an object or map type. +const TypeHintMap VariableTypeHint = 'M' diff --git a/configs/variabletypehint_string.go b/configs/variabletypehint_string.go new file mode 100644 index 000000000..4447cf556 --- /dev/null +++ b/configs/variabletypehint_string.go @@ -0,0 +1,29 @@ +// Code generated by "stringer -type VariableTypeHint"; DO NOT EDIT. + +package configs + +import "strconv" + +const ( + _VariableTypeHint_name_0 = "TypeHintNone" + _VariableTypeHint_name_1 = "TypeHintListTypeHintMap" + _VariableTypeHint_name_2 = "TypeHintString" +) + +var ( + _VariableTypeHint_index_1 = [...]uint8{0, 12, 23} +) + +func (i VariableTypeHint) String() string { + switch { + case i == 0: + return _VariableTypeHint_name_0 + case 76 <= i && i <= 77: + i -= 76 + return _VariableTypeHint_name_1[_VariableTypeHint_index_1[i]:_VariableTypeHint_index_1[i+1]] + case i == 83: + return _VariableTypeHint_name_2 + default: + return "VariableTypeHint(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/configs/version_constraint.go b/configs/version_constraint.go new file mode 100644 index 000000000..6b2882cee --- /dev/null +++ b/configs/version_constraint.go @@ -0,0 +1,15 @@ +package configs + +import ( + version "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl2/hcl" +) + +// VersionConstraint represents a version constraint on some resource +// (e.g. Terraform Core, a provider, a module, ...) that carries with it +// a source range so that a helpful diagnostic can be printed in the event +// that a particular constraint does not match. +type VersionConstraint struct { + Required []version.Constraints + DeclRange hcl.Range +}