kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/terraform/node_resource_plan_orphan.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "kubeform.dev/terraform-backend-sdk/addrs" 8 "kubeform.dev/terraform-backend-sdk/plans" 9 "kubeform.dev/terraform-backend-sdk/states" 10 "kubeform.dev/terraform-backend-sdk/tfdiags" 11 ) 12 13 // NodePlannableResourceInstanceOrphan represents a resource that is "applyable": 14 // it is ready to be applied and is represented by a diff. 15 type NodePlannableResourceInstanceOrphan struct { 16 *NodeAbstractResourceInstance 17 18 // skipRefresh indicates that we should skip refreshing individual instances 19 skipRefresh bool 20 21 // skipPlanChanges indicates we should skip trying to plan change actions 22 // for any instances. 23 skipPlanChanges bool 24 } 25 26 var ( 27 _ GraphNodeModuleInstance = (*NodePlannableResourceInstanceOrphan)(nil) 28 _ GraphNodeReferenceable = (*NodePlannableResourceInstanceOrphan)(nil) 29 _ GraphNodeReferencer = (*NodePlannableResourceInstanceOrphan)(nil) 30 _ GraphNodeConfigResource = (*NodePlannableResourceInstanceOrphan)(nil) 31 _ GraphNodeResourceInstance = (*NodePlannableResourceInstanceOrphan)(nil) 32 _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstanceOrphan)(nil) 33 _ GraphNodeAttachResourceState = (*NodePlannableResourceInstanceOrphan)(nil) 34 _ GraphNodeExecutable = (*NodePlannableResourceInstanceOrphan)(nil) 35 _ GraphNodeProviderConsumer = (*NodePlannableResourceInstanceOrphan)(nil) 36 ) 37 38 func (n *NodePlannableResourceInstanceOrphan) Name() string { 39 return n.ResourceInstanceAddr().String() + " (orphan)" 40 } 41 42 // GraphNodeExecutable 43 func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { 44 addr := n.ResourceInstanceAddr() 45 46 // Eval info is different depending on what kind of resource this is 47 switch addr.Resource.Resource.Mode { 48 case addrs.ManagedResourceMode: 49 return n.managedResourceExecute(ctx) 50 case addrs.DataResourceMode: 51 return n.dataResourceExecute(ctx) 52 default: 53 panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) 54 } 55 } 56 57 func (n *NodePlannableResourceInstanceOrphan) ProvidedBy() (addr addrs.ProviderConfig, exact bool) { 58 if n.Addr.Resource.Resource.Mode == addrs.DataResourceMode { 59 // indicate that this node does not require a configured provider 60 return nil, true 61 } 62 return n.NodeAbstractResourceInstance.ProvidedBy() 63 } 64 65 func (n *NodePlannableResourceInstanceOrphan) dataResourceExecute(ctx EvalContext) tfdiags.Diagnostics { 66 // A data source that is no longer in the config is removed from the state 67 log.Printf("[TRACE] NodePlannableResourceInstanceOrphan: removing state object for %s", n.Addr) 68 69 // we need to update both the refresh state to refresh the current data 70 // source, and the working state for plan-time evaluations. 71 refreshState := ctx.RefreshState() 72 refreshState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) 73 74 workingState := ctx.State() 75 workingState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) 76 return nil 77 } 78 79 func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { 80 addr := n.ResourceInstanceAddr() 81 82 oldState, readDiags := n.readResourceInstanceState(ctx, addr) 83 diags = diags.Append(readDiags) 84 if diags.HasErrors() { 85 return diags 86 } 87 88 // Note any upgrades that readResourceInstanceState might've done in the 89 // prevRunState, so that it'll conform to current schema. 90 diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, prevRunState)) 91 if diags.HasErrors() { 92 return diags 93 } 94 // Also the refreshState, because that should still reflect schema upgrades 95 // even if not refreshing. 96 diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, refreshState)) 97 if diags.HasErrors() { 98 return diags 99 } 100 101 if !n.skipRefresh { 102 // Refresh this instance even though it is going to be destroyed, in 103 // order to catch missing resources. If this is a normal plan, 104 // providers expect a Read request to remove missing resources from the 105 // plan before apply, and may not handle a missing resource during 106 // Delete correctly. If this is a simple refresh, Terraform is 107 // expected to remove the missing resource from the state entirely 108 refreshedState, refreshDiags := n.refresh(ctx, states.NotDeposed, oldState) 109 diags = diags.Append(refreshDiags) 110 if diags.HasErrors() { 111 return diags 112 } 113 114 diags = diags.Append(n.writeResourceInstanceState(ctx, refreshedState, refreshState)) 115 if diags.HasErrors() { 116 return diags 117 } 118 119 // If we refreshed then our subsequent planning should be in terms of 120 // the new object, not the original object. 121 oldState = refreshedState 122 } 123 124 if !n.skipPlanChanges { 125 var change *plans.ResourceInstanceChange 126 change, destroyPlanDiags := n.planDestroy(ctx, oldState, "") 127 diags = diags.Append(destroyPlanDiags) 128 if diags.HasErrors() { 129 return diags 130 } 131 132 diags = diags.Append(n.checkPreventDestroy(change)) 133 if diags.HasErrors() { 134 return diags 135 } 136 137 diags = diags.Append(n.writeChange(ctx, change, "")) 138 if diags.HasErrors() { 139 return diags 140 } 141 142 diags = diags.Append(n.writeResourceInstanceState(ctx, nil, workingState)) 143 } else { 144 // The working state should at least be updated with the result 145 // of upgrading and refreshing from above. 146 diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, workingState)) 147 } 148 149 return diags 150 }