aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/configs/provisioner.go
blob: b031dd0b0a6d90be80f28abe4b81180927036024 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package configs

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
)

// 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
	TypeRange hcl.Range
}

func decodeProvisionerBlock(block *hcl.Block) (*Provisioner, hcl.Diagnostics) {
	pv := &Provisioner{
		Type:      block.Labels[0],
		TypeRange: block.LabelRanges[0],
		DeclRange: block.DefRange,
		When:      ProvisionerWhenCreate,
		OnFailure: ProvisionerOnFailureFail,
	}

	content, config, diags := block.Body.PartialContent(provisionerBlockSchema)
	pv.Config = config

	if attr, exists := content.Attributes["when"]; exists {
		expr, shimDiags := shimTraversalInString(attr.Expr, true)
		diags = append(diags, shimDiags...)

		switch hcl.ExprAsKeyword(expr) {
		case "create":
			pv.When = ProvisionerWhenCreate
		case "destroy":
			pv.When = ProvisionerWhenDestroy
		default:
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid \"when\" keyword",
				Detail:   "The \"when\" argument requires one of the following keywords: create or destroy.",
				Subject:  expr.Range().Ptr(),
			})
		}
	}

	if attr, exists := content.Attributes["on_failure"]; exists {
		expr, shimDiags := shimTraversalInString(attr.Expr, true)
		diags = append(diags, shimDiags...)

		switch hcl.ExprAsKeyword(expr) {
		case "continue":
			pv.OnFailure = ProvisionerOnFailureContinue
		case "fail":
			pv.OnFailure = ProvisionerOnFailureFail
		default:
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid \"on_failure\" keyword",
				Detail:   "The \"on_failure\" argument requires one of the following keywords: continue or fail.",
				Subject:  attr.Expr.Range().Ptr(),
			})
		}
	}

	var seenConnection *hcl.Block
	for _, block := range content.Blocks {
		switch block.Type {

		case "connection":
			if seenConnection != nil {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Duplicate connection block",
					Detail:   fmt.Sprintf("This provisioner already has a connection block at %s.", seenConnection.DefRange),
					Subject:  &block.DefRange,
				})
				continue
			}
			seenConnection = block

			//conn, connDiags := decodeConnectionBlock(block)
			//diags = append(diags, connDiags...)
			pv.Connection = &Connection{
				Config:    block.Body,
				DeclRange: block.DefRange,
			}

		default:
			// Any other block types are ones we've reserved for future use,
			// so they get a generic message.
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Reserved block type name in provisioner block",
				Detail:   fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type),
				Subject:  &block.TypeRange,
			})
		}
	}

	return pv, diags
}

// Connection represents a "connection" block when used within either a
// "resource" or "provisioner" block in a module or file.
type Connection struct {
	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
)

var provisionerBlockSchema = &hcl.BodySchema{
	Attributes: []hcl.AttributeSchema{
		{Name: "when"},
		{Name: "on_failure"},
	},
	Blocks: []hcl.BlockHeaderSchema{
		{Type: "connection"},
		{Type: "lifecycle"}, // reserved for future use
	},
}