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