|
4 | 4 | package providers |
5 | 5 |
|
6 | 6 | import ( |
7 | | - "crypto/sha256" |
8 | 7 | "fmt" |
9 | | - "io" |
10 | | - "log" |
11 | | - "sync" |
12 | 8 |
|
13 | 9 | "github.com/zclconf/go-cty/cty" |
14 | 10 | "github.com/zclconf/go-cty/cty/function" |
15 | 11 |
|
16 | 12 | "github.com/hashicorp/terraform/internal/addrs" |
17 | 13 | "github.com/hashicorp/terraform/internal/configs/configschema" |
| 14 | + "github.com/hashicorp/terraform/internal/lang" |
18 | 15 | ) |
19 | 16 |
|
20 | 17 | type FunctionDecl struct { |
@@ -59,7 +56,7 @@ type FunctionParam struct { |
59 | 56 | // |
60 | 57 | // The resTable argument is a shared instance of *FunctionResults, used to |
61 | 58 | // check the result values from each function call. |
62 | | -func (d FunctionDecl) BuildFunction(providerAddr addrs.Provider, name string, resTable *FunctionResults, factory func() (Interface, error)) function.Function { |
| 59 | +func (d FunctionDecl) BuildFunction(providerAddr addrs.Provider, name string, resTable *lang.FunctionResults, factory func() (Interface, error)) function.Function { |
63 | 60 |
|
64 | 61 | var params []function.Parameter |
65 | 62 | var varParam *function.Parameter |
@@ -123,7 +120,7 @@ func (d FunctionDecl) BuildFunction(providerAddr addrs.Provider, name string, re |
123 | 120 | } |
124 | 121 |
|
125 | 122 | if resTable != nil { |
126 | | - err = resTable.checkPrior(providerAddr, name, args, resp.Result) |
| 123 | + err = resTable.CheckPrior(providerAddr, name, args, resp.Result) |
127 | 124 | if err != nil { |
128 | 125 | return cty.UnknownVal(retType), err |
129 | 126 | } |
@@ -154,113 +151,3 @@ func (p *FunctionParam) ctyParameter() function.Parameter { |
154 | 151 | AllowUnknown: p.AllowUnknownValues, |
155 | 152 | } |
156 | 153 | } |
157 | | - |
158 | | -type priorResult struct { |
159 | | - hash [sha256.Size]byte |
160 | | - // when the result was from a current run, we keep a record of the result |
161 | | - // value to aid in debugging. Results stored in the plan will only have the |
162 | | - // hash to avoid bloating the plan with what could be many very large |
163 | | - // values. |
164 | | - value cty.Value |
165 | | -} |
166 | | - |
167 | | -type FunctionResults struct { |
168 | | - mu sync.Mutex |
169 | | - // results stores the prior result from a provider function call, keyed by |
170 | | - // the hash of the function name and arguments. |
171 | | - results map[[sha256.Size]byte]priorResult |
172 | | -} |
173 | | - |
174 | | -// NewFunctionResultsTable initializes a mapping of function calls to prior |
175 | | -// results used to validate provider function calls. The hashes argument is an |
176 | | -// optional slice of prior result hashes used to preload the cache. |
177 | | -func NewFunctionResultsTable(hashes []FunctionHash) *FunctionResults { |
178 | | - res := &FunctionResults{ |
179 | | - results: make(map[[sha256.Size]byte]priorResult), |
180 | | - } |
181 | | - |
182 | | - res.insertHashes(hashes) |
183 | | - return res |
184 | | -} |
185 | | - |
186 | | -// checkPrior compares the function call against any cached results, and |
187 | | -// returns an error if the result does not match a prior call. |
188 | | -func (f *FunctionResults) checkPrior(provider addrs.Provider, name string, args []cty.Value, result cty.Value) error { |
189 | | - argSum := sha256.New() |
190 | | - |
191 | | - io.WriteString(argSum, provider.String()) |
192 | | - io.WriteString(argSum, "|"+name) |
193 | | - |
194 | | - for _, arg := range args { |
195 | | - // cty.Values have a Hash method, but it is not collision resistant. We |
196 | | - // are going to rely on the GoString formatting instead, which gives |
197 | | - // detailed results for all values. |
198 | | - io.WriteString(argSum, "|"+arg.GoString()) |
199 | | - } |
200 | | - |
201 | | - f.mu.Lock() |
202 | | - defer f.mu.Unlock() |
203 | | - |
204 | | - argHash := [sha256.Size]byte(argSum.Sum(nil)) |
205 | | - resHash := sha256.Sum256([]byte(result.GoString())) |
206 | | - |
207 | | - res, ok := f.results[argHash] |
208 | | - if !ok { |
209 | | - f.results[argHash] = priorResult{ |
210 | | - hash: resHash, |
211 | | - value: result, |
212 | | - } |
213 | | - return nil |
214 | | - } |
215 | | - |
216 | | - if resHash != res.hash { |
217 | | - // Log the args for debugging in case the hcl context is |
218 | | - // insufficient. The error should be adequate most of the time, and |
219 | | - // could already be quite long, so we don't want to add all |
220 | | - // arguments too. |
221 | | - log.Printf("[ERROR] provider %s returned an inconsistent result for function %q with args: %#v\n", provider, name, args) |
222 | | - // The hcl package will add the necessary context around the error in |
223 | | - // the diagnostic, but we add the differing results when we can. |
224 | | - // TODO: maybe we should add a call to action, since this is a bug in |
225 | | - // the provider. |
226 | | - if res.value != cty.NilVal { |
227 | | - return fmt.Errorf("provider function returned an inconsistent result,\nwas: %#v,\nnow: %#v", res.value, result) |
228 | | - |
229 | | - } |
230 | | - return fmt.Errorf("provider function returned an inconsistent result") |
231 | | - } |
232 | | - |
233 | | - return nil |
234 | | -} |
235 | | - |
236 | | -// insertHashes insert key-value pairs to the functionResults map. This is used |
237 | | -// to preload stored values before any Verify calls are made. |
238 | | -func (f *FunctionResults) insertHashes(hashes []FunctionHash) { |
239 | | - f.mu.Lock() |
240 | | - defer f.mu.Unlock() |
241 | | - |
242 | | - for _, res := range hashes { |
243 | | - f.results[[sha256.Size]byte(res.Key)] = priorResult{ |
244 | | - hash: [sha256.Size]byte(res.Result), |
245 | | - } |
246 | | - } |
247 | | -} |
248 | | - |
249 | | -// FunctionHash contains the key and result hash values from a prior function |
250 | | -// call. |
251 | | -type FunctionHash struct { |
252 | | - Key []byte |
253 | | - Result []byte |
254 | | -} |
255 | | - |
256 | | -// copy the hash values into a struct which can be recorded in the plan. |
257 | | -func (f *FunctionResults) GetHashes() []FunctionHash { |
258 | | - f.mu.Lock() |
259 | | - defer f.mu.Unlock() |
260 | | - |
261 | | - var res []FunctionHash |
262 | | - for k, r := range f.results { |
263 | | - res = append(res, FunctionHash{Key: k[:], Result: r.hash[:]}) |
264 | | - } |
265 | | - return res |
266 | | -} |
0 commit comments