github.com/opentofu/opentofu@v1.7.1/internal/tofu/node_resource_abstract_instance_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "fmt" 10 "testing" 11 12 "github.com/opentofu/opentofu/internal/addrs" 13 "github.com/opentofu/opentofu/internal/configs" 14 "github.com/opentofu/opentofu/internal/configs/configschema" 15 "github.com/opentofu/opentofu/internal/states" 16 "github.com/zclconf/go-cty/cty" 17 ) 18 19 func TestNodeAbstractResourceInstanceProvider(t *testing.T) { 20 tests := []struct { 21 Addr addrs.AbsResourceInstance 22 Config *configs.Resource 23 StoredProviderConfig addrs.AbsProviderConfig 24 Want addrs.Provider 25 }{ 26 { 27 Addr: addrs.Resource{ 28 Mode: addrs.ManagedResourceMode, 29 Type: "null_resource", 30 Name: "baz", 31 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 32 Want: addrs.Provider{ 33 Hostname: addrs.DefaultProviderRegistryHost, 34 Namespace: "hashicorp", 35 Type: "null", 36 }, 37 }, 38 { 39 Addr: addrs.Resource{ 40 Mode: addrs.DataResourceMode, 41 Type: "terraform_remote_state", 42 Name: "baz", 43 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 44 Want: addrs.Provider{ 45 // As a special case, the type prefix "terraform_" maps to 46 // the builtin provider, not the default one. 47 Hostname: addrs.BuiltInProviderHost, 48 Namespace: addrs.BuiltInProviderNamespace, 49 Type: "terraform", 50 }, 51 }, 52 { 53 Addr: addrs.Resource{ 54 Mode: addrs.ManagedResourceMode, 55 Type: "null_resource", 56 Name: "baz", 57 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 58 Config: &configs.Resource{ 59 // Just enough configs.Resource for the Provider method. Not 60 // actually valid for general use. 61 Provider: addrs.Provider{ 62 Hostname: addrs.DefaultProviderRegistryHost, 63 Namespace: "awesomecorp", 64 Type: "happycloud", 65 }, 66 }, 67 // The config overrides the default behavior. 68 Want: addrs.Provider{ 69 Hostname: addrs.DefaultProviderRegistryHost, 70 Namespace: "awesomecorp", 71 Type: "happycloud", 72 }, 73 }, 74 { 75 Addr: addrs.Resource{ 76 Mode: addrs.DataResourceMode, 77 Type: "terraform_remote_state", 78 Name: "baz", 79 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 80 Config: &configs.Resource{ 81 // Just enough configs.Resource for the Provider method. Not 82 // actually valid for general use. 83 Provider: addrs.Provider{ 84 Hostname: addrs.DefaultProviderRegistryHost, 85 Namespace: "awesomecorp", 86 Type: "happycloud", 87 }, 88 }, 89 // The config overrides the default behavior. 90 Want: addrs.Provider{ 91 Hostname: addrs.DefaultProviderRegistryHost, 92 Namespace: "awesomecorp", 93 Type: "happycloud", 94 }, 95 }, 96 { 97 Addr: addrs.Resource{ 98 Mode: addrs.DataResourceMode, 99 Type: "null_resource", 100 Name: "baz", 101 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 102 Config: nil, 103 StoredProviderConfig: addrs.AbsProviderConfig{ 104 Module: addrs.RootModule, 105 Provider: addrs.Provider{ 106 Hostname: addrs.DefaultProviderRegistryHost, 107 Namespace: "awesomecorp", 108 Type: "null", 109 }, 110 }, 111 // The stored provider config overrides the default behavior. 112 Want: addrs.Provider{ 113 Hostname: addrs.DefaultProviderRegistryHost, 114 Namespace: "awesomecorp", 115 Type: "null", 116 }, 117 }, 118 } 119 120 for _, test := range tests { 121 var name string 122 if test.Config != nil { 123 name = fmt.Sprintf("%s with configured %s", test.Addr, test.Config.Provider) 124 } else { 125 name = fmt.Sprintf("%s with no configuration", test.Addr) 126 } 127 t.Run(name, func(t *testing.T) { 128 node := &NodeAbstractResourceInstance{ 129 // Just enough NodeAbstractResourceInstance for the Provider 130 // function. (This would not be valid for some other functions.) 131 Addr: test.Addr, 132 NodeAbstractResource: NodeAbstractResource{ 133 Addr: test.Addr.ConfigResource(), 134 Config: test.Config, 135 storedProviderConfig: test.StoredProviderConfig, 136 }, 137 } 138 got := node.Provider() 139 if got != test.Want { 140 t.Errorf("wrong result\naddr: %s\nconfig: %#v\ngot: %s\nwant: %s", test.Addr, test.Config, got, test.Want) 141 } 142 }) 143 } 144 } 145 146 func TestNodeAbstractResourceInstance_WriteResourceInstanceState(t *testing.T) { 147 state := states.NewState() 148 ctx := new(MockEvalContext) 149 ctx.StateState = state.SyncWrapper() 150 ctx.PathPath = addrs.RootModuleInstance 151 152 mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ 153 Attributes: map[string]*configschema.Attribute{ 154 "id": { 155 Type: cty.String, 156 Optional: true, 157 }, 158 }, 159 }) 160 161 obj := &states.ResourceInstanceObject{ 162 Value: cty.ObjectVal(map[string]cty.Value{ 163 "id": cty.StringVal("i-abc123"), 164 }), 165 Status: states.ObjectReady, 166 } 167 168 node := &NodeAbstractResourceInstance{ 169 Addr: mustResourceInstanceAddr("aws_instance.foo"), 170 // instanceState: obj, 171 NodeAbstractResource: NodeAbstractResource{ 172 ResolvedProvider: mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`), 173 }, 174 } 175 ctx.ProviderProvider = mockProvider 176 ctx.ProviderSchemaSchema = mockProvider.GetProviderSchema() 177 178 err := node.writeResourceInstanceState(ctx, obj, workingState) 179 if err != nil { 180 t.Fatalf("unexpected error: %s", err.Error()) 181 } 182 183 checkStateString(t, state, ` 184 aws_instance.foo: 185 ID = i-abc123 186 provider = provider["registry.opentofu.org/hashicorp/aws"] 187 `) 188 }