1package configs
2
3import (
4	"fmt"
5
6	"github.com/hashicorp/hcl2/hcl"
7)
8
9// Provisioner represents a "provisioner" block when used within a
10// "resource" block in a module or file.
11type Provisioner struct {
12	Type       string
13	Config     hcl.Body
14	Connection *Connection
15	When       ProvisionerWhen
16	OnFailure  ProvisionerOnFailure
17
18	DeclRange hcl.Range
19	TypeRange hcl.Range
20}
21
22func decodeProvisionerBlock(block *hcl.Block) (*Provisioner, hcl.Diagnostics) {
23	pv := &Provisioner{
24		Type:      block.Labels[0],
25		TypeRange: block.LabelRanges[0],
26		DeclRange: block.DefRange,
27		When:      ProvisionerWhenCreate,
28		OnFailure: ProvisionerOnFailureFail,
29	}
30
31	content, config, diags := block.Body.PartialContent(provisionerBlockSchema)
32	pv.Config = config
33
34	if attr, exists := content.Attributes["when"]; exists {
35		expr, shimDiags := shimTraversalInString(attr.Expr, true)
36		diags = append(diags, shimDiags...)
37
38		switch hcl.ExprAsKeyword(expr) {
39		case "create":
40			pv.When = ProvisionerWhenCreate
41		case "destroy":
42			pv.When = ProvisionerWhenDestroy
43		default:
44			diags = append(diags, &hcl.Diagnostic{
45				Severity: hcl.DiagError,
46				Summary:  "Invalid \"when\" keyword",
47				Detail:   "The \"when\" argument requires one of the following keywords: create or destroy.",
48				Subject:  expr.Range().Ptr(),
49			})
50		}
51	}
52
53	if attr, exists := content.Attributes["on_failure"]; exists {
54		expr, shimDiags := shimTraversalInString(attr.Expr, true)
55		diags = append(diags, shimDiags...)
56
57		switch hcl.ExprAsKeyword(expr) {
58		case "continue":
59			pv.OnFailure = ProvisionerOnFailureContinue
60		case "fail":
61			pv.OnFailure = ProvisionerOnFailureFail
62		default:
63			diags = append(diags, &hcl.Diagnostic{
64				Severity: hcl.DiagError,
65				Summary:  "Invalid \"on_failure\" keyword",
66				Detail:   "The \"on_failure\" argument requires one of the following keywords: continue or fail.",
67				Subject:  attr.Expr.Range().Ptr(),
68			})
69		}
70	}
71
72	var seenConnection *hcl.Block
73	for _, block := range content.Blocks {
74		switch block.Type {
75
76		case "connection":
77			if seenConnection != nil {
78				diags = append(diags, &hcl.Diagnostic{
79					Severity: hcl.DiagError,
80					Summary:  "Duplicate connection block",
81					Detail:   fmt.Sprintf("This provisioner already has a connection block at %s.", seenConnection.DefRange),
82					Subject:  &block.DefRange,
83				})
84				continue
85			}
86			seenConnection = block
87
88			//conn, connDiags := decodeConnectionBlock(block)
89			//diags = append(diags, connDiags...)
90			pv.Connection = &Connection{
91				Config:    block.Body,
92				DeclRange: block.DefRange,
93			}
94
95		default:
96			// Any other block types are ones we've reserved for future use,
97			// so they get a generic message.
98			diags = append(diags, &hcl.Diagnostic{
99				Severity: hcl.DiagError,
100				Summary:  "Reserved block type name in provisioner block",
101				Detail:   fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type),
102				Subject:  &block.TypeRange,
103			})
104		}
105	}
106
107	return pv, diags
108}
109
110// Connection represents a "connection" block when used within either a
111// "resource" or "provisioner" block in a module or file.
112type Connection struct {
113	Config hcl.Body
114
115	DeclRange hcl.Range
116}
117
118// ProvisionerWhen is an enum for valid values for when to run provisioners.
119type ProvisionerWhen int
120
121//go:generate go run golang.org/x/tools/cmd/stringer -type ProvisionerWhen
122
123const (
124	ProvisionerWhenInvalid ProvisionerWhen = iota
125	ProvisionerWhenCreate
126	ProvisionerWhenDestroy
127)
128
129// ProvisionerOnFailure is an enum for valid values for on_failure options
130// for provisioners.
131type ProvisionerOnFailure int
132
133//go:generate go run golang.org/x/tools/cmd/stringer -type ProvisionerOnFailure
134
135const (
136	ProvisionerOnFailureInvalid ProvisionerOnFailure = iota
137	ProvisionerOnFailureContinue
138	ProvisionerOnFailureFail
139)
140
141var provisionerBlockSchema = &hcl.BodySchema{
142	Attributes: []hcl.AttributeSchema{
143		{Name: "when"},
144		{Name: "on_failure"},
145	},
146	Blocks: []hcl.BlockHeaderSchema{
147		{Type: "connection"},
148		{Type: "lifecycle"}, // reserved for future use
149	},
150}
151