]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go
856c6758a8a128fd997283829d096596820c6ee2
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / provisioner.go
1 package schema
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "sync"
8
9 "github.com/hashicorp/go-multierror"
10 "github.com/hashicorp/terraform/config"
11 "github.com/hashicorp/terraform/terraform"
12 )
13
14 // Provisioner represents a resource provisioner in Terraform and properly
15 // implements all of the ResourceProvisioner API.
16 //
17 // This higher level structure makes it much easier to implement a new or
18 // custom provisioner for Terraform.
19 //
20 // The function callbacks for this structure are all passed a context object.
21 // This context object has a number of pre-defined values that can be accessed
22 // via the global functions defined in context.go.
23 type Provisioner struct {
24 // ConnSchema is the schema for the connection settings for this
25 // provisioner.
26 //
27 // The keys of this map are the configuration keys, and the value is
28 // the schema describing the value of the configuration.
29 //
30 // NOTE: The value of connection keys can only be strings for now.
31 ConnSchema map[string]*Schema
32
33 // Schema is the schema for the usage of this provisioner.
34 //
35 // The keys of this map are the configuration keys, and the value is
36 // the schema describing the value of the configuration.
37 Schema map[string]*Schema
38
39 // ApplyFunc is the function for executing the provisioner. This is required.
40 // It is given a context. See the Provisioner struct docs for more
41 // information.
42 ApplyFunc func(ctx context.Context) error
43
44 // ValidateFunc is a function for extended validation. This is optional
45 // and should be used when individual field validation is not enough.
46 ValidateFunc func(*ResourceData) ([]string, []error)
47
48 stopCtx context.Context
49 stopCtxCancel context.CancelFunc
50 stopOnce sync.Once
51 }
52
53 // Keys that can be used to access data in the context parameters for
54 // Provisioners.
55 var (
56 connDataInvalid = contextKey("data invalid")
57
58 // This returns a *ResourceData for the connection information.
59 // Guaranteed to never be nil.
60 ProvConnDataKey = contextKey("provider conn data")
61
62 // This returns a *ResourceData for the config information.
63 // Guaranteed to never be nil.
64 ProvConfigDataKey = contextKey("provider config data")
65
66 // This returns a terraform.UIOutput. Guaranteed to never be nil.
67 ProvOutputKey = contextKey("provider output")
68
69 // This returns the raw InstanceState passed to Apply. Guaranteed to
70 // be set, but may be nil.
71 ProvRawStateKey = contextKey("provider raw state")
72 )
73
74 // InternalValidate should be called to validate the structure
75 // of the provisioner.
76 //
77 // This should be called in a unit test to verify before release that this
78 // structure is properly configured for use.
79 func (p *Provisioner) InternalValidate() error {
80 if p == nil {
81 return errors.New("provisioner is nil")
82 }
83
84 var validationErrors error
85 {
86 sm := schemaMap(p.ConnSchema)
87 if err := sm.InternalValidate(sm); err != nil {
88 validationErrors = multierror.Append(validationErrors, err)
89 }
90 }
91
92 {
93 sm := schemaMap(p.Schema)
94 if err := sm.InternalValidate(sm); err != nil {
95 validationErrors = multierror.Append(validationErrors, err)
96 }
97 }
98
99 if p.ApplyFunc == nil {
100 validationErrors = multierror.Append(validationErrors, fmt.Errorf(
101 "ApplyFunc must not be nil"))
102 }
103
104 return validationErrors
105 }
106
107 // StopContext returns a context that checks whether a provisioner is stopped.
108 func (p *Provisioner) StopContext() context.Context {
109 p.stopOnce.Do(p.stopInit)
110 return p.stopCtx
111 }
112
113 func (p *Provisioner) stopInit() {
114 p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background())
115 }
116
117 // Stop implementation of terraform.ResourceProvisioner interface.
118 func (p *Provisioner) Stop() error {
119 p.stopOnce.Do(p.stopInit)
120 p.stopCtxCancel()
121 return nil
122 }
123
124 func (p *Provisioner) Validate(config *terraform.ResourceConfig) ([]string, []error) {
125 if err := p.InternalValidate(); err != nil {
126 return nil, []error{fmt.Errorf(
127 "Internal validation of the provisioner failed! This is always a bug\n"+
128 "with the provisioner itself, and not a user issue. Please report\n"+
129 "this bug:\n\n%s", err)}
130 }
131 w := []string{}
132 e := []error{}
133 if p.Schema != nil {
134 w2, e2 := schemaMap(p.Schema).Validate(config)
135 w = append(w, w2...)
136 e = append(e, e2...)
137 }
138 if p.ValidateFunc != nil {
139 data := &ResourceData{
140 schema: p.Schema,
141 config: config,
142 }
143 w2, e2 := p.ValidateFunc(data)
144 w = append(w, w2...)
145 e = append(e, e2...)
146 }
147 return w, e
148 }
149
150 // Apply implementation of terraform.ResourceProvisioner interface.
151 func (p *Provisioner) Apply(
152 o terraform.UIOutput,
153 s *terraform.InstanceState,
154 c *terraform.ResourceConfig) error {
155 var connData, configData *ResourceData
156
157 {
158 // We first need to turn the connection information into a
159 // terraform.ResourceConfig so that we can use that type to more
160 // easily build a ResourceData structure. We do this by simply treating
161 // the conn info as configuration input.
162 raw := make(map[string]interface{})
163 if s != nil {
164 for k, v := range s.Ephemeral.ConnInfo {
165 raw[k] = v
166 }
167 }
168
169 c, err := config.NewRawConfig(raw)
170 if err != nil {
171 return err
172 }
173
174 sm := schemaMap(p.ConnSchema)
175 diff, err := sm.Diff(nil, terraform.NewResourceConfig(c))
176 if err != nil {
177 return err
178 }
179 connData, err = sm.Data(nil, diff)
180 if err != nil {
181 return err
182 }
183 }
184
185 {
186 // Build the configuration data. Doing this requires making a "diff"
187 // even though that's never used. We use that just to get the correct types.
188 configMap := schemaMap(p.Schema)
189 diff, err := configMap.Diff(nil, c)
190 if err != nil {
191 return err
192 }
193 configData, err = configMap.Data(nil, diff)
194 if err != nil {
195 return err
196 }
197 }
198
199 // Build the context and call the function
200 ctx := p.StopContext()
201 ctx = context.WithValue(ctx, ProvConnDataKey, connData)
202 ctx = context.WithValue(ctx, ProvConfigDataKey, configData)
203 ctx = context.WithValue(ctx, ProvOutputKey, o)
204 ctx = context.WithValue(ctx, ProvRawStateKey, s)
205 return p.ApplyFunc(ctx)
206 }