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