]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "log" | |
107c1cdb ND |
6 | |
7 | "github.com/zclconf/go-cty/cty" | |
8 | ||
9 | "github.com/hashicorp/terraform/addrs" | |
10 | "github.com/hashicorp/terraform/providers" | |
11 | "github.com/hashicorp/terraform/states" | |
12 | "github.com/hashicorp/terraform/tfdiags" | |
bae9f6d2 JC |
13 | ) |
14 | ||
15 | // EvalRefresh is an EvalNode implementation that does a refresh for | |
16 | // a resource. | |
17 | type EvalRefresh struct { | |
107c1cdb ND |
18 | Addr addrs.ResourceInstance |
19 | ProviderAddr addrs.AbsProviderConfig | |
20 | Provider *providers.Interface | |
21 | ProviderSchema **ProviderSchema | |
22 | State **states.ResourceInstanceObject | |
23 | Output **states.ResourceInstanceObject | |
bae9f6d2 JC |
24 | } |
25 | ||
26 | // TODO: test | |
27 | func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) { | |
bae9f6d2 | 28 | state := *n.State |
107c1cdb ND |
29 | absAddr := n.Addr.Absolute(ctx.Path()) |
30 | ||
31 | var diags tfdiags.Diagnostics | |
bae9f6d2 JC |
32 | |
33 | // If we have no state, we don't do any refreshing | |
34 | if state == nil { | |
107c1cdb ND |
35 | log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", n.Addr.Absolute(ctx.Path())) |
36 | return nil, diags.ErrWithWarnings() | |
37 | } | |
38 | ||
39 | schema, _ := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) | |
40 | if schema == nil { | |
41 | // Should be caught during validation, so we don't bother with a pretty error here | |
42 | return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) | |
bae9f6d2 JC |
43 | } |
44 | ||
45 | // Call pre-refresh hook | |
46 | err := ctx.Hook(func(h Hook) (HookAction, error) { | |
107c1cdb | 47 | return h.PreRefresh(absAddr, states.CurrentGen, state.Value) |
bae9f6d2 JC |
48 | }) |
49 | if err != nil { | |
107c1cdb | 50 | return nil, diags.ErrWithWarnings() |
bae9f6d2 JC |
51 | } |
52 | ||
53 | // Refresh! | |
107c1cdb ND |
54 | priorVal := state.Value |
55 | req := providers.ReadResourceRequest{ | |
56 | TypeName: n.Addr.Resource.Type, | |
57 | PriorState: priorVal, | |
bae9f6d2 JC |
58 | } |
59 | ||
107c1cdb ND |
60 | provider := *n.Provider |
61 | resp := provider.ReadResource(req) | |
62 | diags = diags.Append(resp.Diagnostics) | |
63 | if diags.HasErrors() { | |
64 | return nil, diags.Err() | |
65 | } | |
66 | ||
67 | if resp.NewState == cty.NilVal { | |
68 | // This ought not to happen in real cases since it's not possible to | |
69 | // send NilVal over the plugin RPC channel, but it can come up in | |
70 | // tests due to sloppy mocking. | |
71 | panic("new state is cty.NilVal") | |
72 | } | |
73 | ||
74 | for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) { | |
75 | diags = diags.Append(tfdiags.Sourceless( | |
76 | tfdiags.Error, | |
77 | "Provider produced invalid object", | |
78 | fmt.Sprintf( | |
79 | "Provider %q planned an invalid value for %s during refresh: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", | |
80 | n.ProviderAddr.ProviderConfig.Type, absAddr, tfdiags.FormatError(err), | |
81 | ), | |
82 | )) | |
83 | } | |
84 | if diags.HasErrors() { | |
85 | return nil, diags.Err() | |
86 | } | |
87 | ||
88 | newState := state.DeepCopy() | |
89 | newState.Value = resp.NewState | |
90 | ||
bae9f6d2 JC |
91 | // Call post-refresh hook |
92 | err = ctx.Hook(func(h Hook) (HookAction, error) { | |
107c1cdb | 93 | return h.PostRefresh(absAddr, states.CurrentGen, priorVal, newState.Value) |
bae9f6d2 JC |
94 | }) |
95 | if err != nil { | |
96 | return nil, err | |
97 | } | |
98 | ||
99 | if n.Output != nil { | |
107c1cdb | 100 | *n.Output = newState |
bae9f6d2 JC |
101 | } |
102 | ||
107c1cdb | 103 | return nil, diags.ErrWithWarnings() |
bae9f6d2 | 104 | } |