]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/backend.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / backend.go
1 package schema
2
3 import (
4 "context"
5 "fmt"
6
7 "github.com/hashicorp/terraform/tfdiags"
8 "github.com/zclconf/go-cty/cty"
9
10 "github.com/hashicorp/terraform/config/hcl2shim"
11 "github.com/hashicorp/terraform/configs/configschema"
12 "github.com/hashicorp/terraform/terraform"
13 ctyconvert "github.com/zclconf/go-cty/cty/convert"
14 )
15
16 // Backend represents a partial backend.Backend implementation and simplifies
17 // the creation of configuration loading and validation.
18 //
19 // Unlike other schema structs such as Provider, this struct is meant to be
20 // embedded within your actual implementation. It provides implementations
21 // only for Input and Configure and gives you a method for accessing the
22 // configuration in the form of a ResourceData that you're expected to call
23 // from the other implementation funcs.
24 type Backend struct {
25 // Schema is the schema for the configuration of this backend. If this
26 // Backend has no configuration this can be omitted.
27 Schema map[string]*Schema
28
29 // ConfigureFunc is called to configure the backend. Use the
30 // FromContext* methods to extract information from the context.
31 // This can be nil, in which case nothing will be called but the
32 // config will still be stored.
33 ConfigureFunc func(context.Context) error
34
35 config *ResourceData
36 }
37
38 var (
39 backendConfigKey = contextKey("backend config")
40 )
41
42 // FromContextBackendConfig extracts a ResourceData with the configuration
43 // from the context. This should only be called by Backend functions.
44 func FromContextBackendConfig(ctx context.Context) *ResourceData {
45 return ctx.Value(backendConfigKey).(*ResourceData)
46 }
47
48 func (b *Backend) ConfigSchema() *configschema.Block {
49 // This is an alias of CoreConfigSchema just to implement the
50 // backend.Backend interface.
51 return b.CoreConfigSchema()
52 }
53
54 func (b *Backend) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
55 if b == nil {
56 return configVal, nil
57 }
58 var diags tfdiags.Diagnostics
59 var err error
60
61 // In order to use Transform below, this needs to be filled out completely
62 // according the schema.
63 configVal, err = b.CoreConfigSchema().CoerceValue(configVal)
64 if err != nil {
65 return configVal, diags.Append(err)
66 }
67
68 // lookup any required, top-level attributes that are Null, and see if we
69 // have a Default value available.
70 configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) {
71 // we're only looking for top-level attributes
72 if len(path) != 1 {
73 return val, nil
74 }
75
76 // nothing to do if we already have a value
77 if !val.IsNull() {
78 return val, nil
79 }
80
81 // get the Schema definition for this attribute
82 getAttr, ok := path[0].(cty.GetAttrStep)
83 // these should all exist, but just ignore anything strange
84 if !ok {
85 return val, nil
86 }
87
88 attrSchema := b.Schema[getAttr.Name]
89 // continue to ignore anything that doesn't match
90 if attrSchema == nil {
91 return val, nil
92 }
93
94 // this is deprecated, so don't set it
95 if attrSchema.Deprecated != "" || attrSchema.Removed != "" {
96 return val, nil
97 }
98
99 // find a default value if it exists
100 def, err := attrSchema.DefaultValue()
101 if err != nil {
102 diags = diags.Append(fmt.Errorf("error getting default for %q: %s", getAttr.Name, err))
103 return val, err
104 }
105
106 // no default
107 if def == nil {
108 return val, nil
109 }
110
111 // create a cty.Value and make sure it's the correct type
112 tmpVal := hcl2shim.HCL2ValueFromConfigValue(def)
113
114 // helper/schema used to allow setting "" to a bool
115 if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) {
116 // return a warning about the conversion
117 diags = diags.Append("provider set empty string as default value for bool " + getAttr.Name)
118 tmpVal = cty.False
119 }
120
121 val, err = ctyconvert.Convert(tmpVal, val.Type())
122 if err != nil {
123 diags = diags.Append(fmt.Errorf("error setting default for %q: %s", getAttr.Name, err))
124 }
125
126 return val, err
127 })
128 if err != nil {
129 // any error here was already added to the diagnostics
130 return configVal, diags
131 }
132
133 shimRC := b.shimConfig(configVal)
134 warns, errs := schemaMap(b.Schema).Validate(shimRC)
135 for _, warn := range warns {
136 diags = diags.Append(tfdiags.SimpleWarning(warn))
137 }
138 for _, err := range errs {
139 diags = diags.Append(err)
140 }
141 return configVal, diags
142 }
143
144 func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics {
145 if b == nil {
146 return nil
147 }
148
149 var diags tfdiags.Diagnostics
150 sm := schemaMap(b.Schema)
151 shimRC := b.shimConfig(obj)
152
153 // Get a ResourceData for this configuration. To do this, we actually
154 // generate an intermediary "diff" although that is never exposed.
155 diff, err := sm.Diff(nil, shimRC, nil, nil, true)
156 if err != nil {
157 diags = diags.Append(err)
158 return diags
159 }
160
161 data, err := sm.Data(nil, diff)
162 if err != nil {
163 diags = diags.Append(err)
164 return diags
165 }
166 b.config = data
167
168 if b.ConfigureFunc != nil {
169 err = b.ConfigureFunc(context.WithValue(
170 context.Background(), backendConfigKey, data))
171 if err != nil {
172 diags = diags.Append(err)
173 return diags
174 }
175 }
176
177 return diags
178 }
179
180 // shimConfig turns a new-style cty.Value configuration (which must be of
181 // an object type) into a minimal old-style *terraform.ResourceConfig object
182 // that should be populated enough to appease the not-yet-updated functionality
183 // in this package. This should be removed once everything is updated.
184 func (b *Backend) shimConfig(obj cty.Value) *terraform.ResourceConfig {
185 shimMap, ok := hcl2shim.ConfigValueFromHCL2(obj).(map[string]interface{})
186 if !ok {
187 // If the configVal was nil, we still want a non-nil map here.
188 shimMap = map[string]interface{}{}
189 }
190 return &terraform.ResourceConfig{
191 Config: shimMap,
192 Raw: shimMap,
193 }
194 }
195
196 // Config returns the configuration. This is available after Configure is
197 // called.
198 func (b *Backend) Config() *ResourceData {
199 return b.config
200 }