github.com/jameswoolfenden/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  }