github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/builtin/providers/terraform/resource_data.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-uuid" 7 "github.com/hashicorp/terraform/internal/configs/configschema" 8 "github.com/hashicorp/terraform/internal/providers" 9 "github.com/hashicorp/terraform/internal/tfdiags" 10 "github.com/zclconf/go-cty/cty" 11 ctyjson "github.com/zclconf/go-cty/cty/json" 12 ) 13 14 func dataStoreResourceSchema() providers.Schema { 15 return providers.Schema{ 16 Block: &configschema.Block{ 17 Attributes: map[string]*configschema.Attribute{ 18 "input": {Type: cty.DynamicPseudoType, Optional: true}, 19 "output": {Type: cty.DynamicPseudoType, Computed: true}, 20 "triggers_replace": {Type: cty.DynamicPseudoType, Optional: true}, 21 "id": {Type: cty.String, Computed: true}, 22 }, 23 }, 24 } 25 } 26 27 func validateDataStoreResourceConfig(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { 28 if req.Config.IsNull() { 29 return resp 30 } 31 32 // Core does not currently validate computed values are not set in the 33 // configuration. 34 for _, attr := range []string{"id", "output"} { 35 if !req.Config.GetAttr(attr).IsNull() { 36 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf(`%q attribute is read-only`, attr)) 37 } 38 } 39 return resp 40 } 41 42 func upgradeDataStoreResourceState(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { 43 ty := dataStoreResourceSchema().Block.ImpliedType() 44 val, err := ctyjson.Unmarshal(req.RawStateJSON, ty) 45 if err != nil { 46 resp.Diagnostics = resp.Diagnostics.Append(err) 47 return resp 48 } 49 50 resp.UpgradedState = val 51 return resp 52 } 53 54 func readDataStoreResourceState(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { 55 resp.NewState = req.PriorState 56 return resp 57 } 58 59 func planDataStoreResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 60 if req.ProposedNewState.IsNull() { 61 // destroy op 62 resp.PlannedState = req.ProposedNewState 63 return resp 64 } 65 66 planned := req.ProposedNewState.AsValueMap() 67 68 input := req.ProposedNewState.GetAttr("input") 69 trigger := req.ProposedNewState.GetAttr("triggers_replace") 70 71 switch { 72 case req.PriorState.IsNull(): 73 // Create 74 // Set the id value to unknown. 75 planned["id"] = cty.UnknownVal(cty.String) 76 77 // Output type must always match the input, even when it's null. 78 if input.IsNull() { 79 planned["output"] = input 80 } else { 81 planned["output"] = cty.UnknownVal(input.Type()) 82 } 83 84 resp.PlannedState = cty.ObjectVal(planned) 85 return resp 86 87 case !req.PriorState.GetAttr("triggers_replace").RawEquals(trigger): 88 // trigger changed, so we need to replace the entire instance 89 resp.RequiresReplace = append(resp.RequiresReplace, cty.GetAttrPath("triggers_replace")) 90 planned["id"] = cty.UnknownVal(cty.String) 91 92 // We need to check the input for the replacement instance to compute a 93 // new output. 94 if input.IsNull() { 95 planned["output"] = input 96 } else { 97 planned["output"] = cty.UnknownVal(input.Type()) 98 } 99 100 case !req.PriorState.GetAttr("input").RawEquals(input): 101 // only input changed, so we only need to re-compute output 102 planned["output"] = cty.UnknownVal(input.Type()) 103 } 104 105 resp.PlannedState = cty.ObjectVal(planned) 106 return resp 107 } 108 109 var testUUIDHook func() string 110 111 func applyDataStoreResourceChange(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 112 if req.PlannedState.IsNull() { 113 resp.NewState = req.PlannedState 114 return resp 115 } 116 117 newState := req.PlannedState.AsValueMap() 118 119 if !req.PlannedState.GetAttr("output").IsKnown() { 120 newState["output"] = req.PlannedState.GetAttr("input") 121 } 122 123 if !req.PlannedState.GetAttr("id").IsKnown() { 124 idString, err := uuid.GenerateUUID() 125 // Terraform would probably never get this far without a good random 126 // source, but catch the error anyway. 127 if err != nil { 128 diag := tfdiags.AttributeValue( 129 tfdiags.Error, 130 "Error generating id", 131 err.Error(), 132 cty.GetAttrPath("id"), 133 ) 134 135 resp.Diagnostics = resp.Diagnostics.Append(diag) 136 } 137 138 if testUUIDHook != nil { 139 idString = testUUIDHook() 140 } 141 142 newState["id"] = cty.StringVal(idString) 143 } 144 145 resp.NewState = cty.ObjectVal(newState) 146 147 return resp 148 } 149 150 // TODO: This isn't very useful even for examples, because terraform_data has 151 // no way to refresh the full resource value from only the import ID. This 152 // minimal implementation allows the import to succeed, and can be extended 153 // once the configuration is available during import. 154 func importDataStore(req providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { 155 schema := dataStoreResourceSchema() 156 v := cty.ObjectVal(map[string]cty.Value{ 157 "id": cty.StringVal(req.ID), 158 }) 159 state, err := schema.Block.CoerceValue(v) 160 resp.Diagnostics = resp.Diagnostics.Append(err) 161 162 resp.ImportedResources = []providers.ImportedResource{ 163 { 164 TypeName: req.TypeName, 165 State: state, 166 }, 167 } 168 return resp 169 }