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