github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/local/testing.go (about) 1 package local 2 3 import ( 4 "path/filepath" 5 "testing" 6 7 "github.com/zclconf/go-cty/cty" 8 9 "github.com/hashicorp/terraform/internal/addrs" 10 "github.com/hashicorp/terraform/internal/backend" 11 "github.com/hashicorp/terraform/internal/configs/configschema" 12 "github.com/hashicorp/terraform/internal/providers" 13 "github.com/hashicorp/terraform/internal/states" 14 "github.com/hashicorp/terraform/internal/states/statemgr" 15 "github.com/hashicorp/terraform/internal/terraform" 16 ) 17 18 // TestLocal returns a configured Local struct with temporary paths and 19 // in-memory ContextOpts. 20 // 21 // No operations will be called on the returned value, so you can still set 22 // public fields without any locks. 23 func TestLocal(t *testing.T) *Local { 24 t.Helper() 25 tempDir, err := filepath.EvalSymlinks(t.TempDir()) 26 if err != nil { 27 t.Fatal(err) 28 } 29 30 local := New() 31 local.StatePath = filepath.Join(tempDir, "state.tfstate") 32 local.StateOutPath = filepath.Join(tempDir, "state.tfstate") 33 local.StateBackupPath = filepath.Join(tempDir, "state.tfstate.bak") 34 local.StateWorkspaceDir = filepath.Join(tempDir, "state.tfstate.d") 35 local.ContextOpts = &terraform.ContextOpts{} 36 37 return local 38 } 39 40 // TestLocalProvider modifies the ContextOpts of the *Local parameter to 41 // have a provider with the given name. 42 func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.ProviderSchema) *terraform.MockProvider { 43 // Build a mock resource provider for in-memory operations 44 p := new(terraform.MockProvider) 45 46 if schema == nil { 47 schema = &terraform.ProviderSchema{} // default schema is empty 48 } 49 p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ 50 Provider: providers.Schema{Block: schema.Provider}, 51 ProviderMeta: providers.Schema{Block: schema.ProviderMeta}, 52 ResourceTypes: map[string]providers.Schema{}, 53 DataSources: map[string]providers.Schema{}, 54 } 55 for name, res := range schema.ResourceTypes { 56 p.GetProviderSchemaResponse.ResourceTypes[name] = providers.Schema{ 57 Block: res, 58 Version: int64(schema.ResourceTypeSchemaVersions[name]), 59 } 60 } 61 for name, dat := range schema.DataSources { 62 p.GetProviderSchemaResponse.DataSources[name] = providers.Schema{Block: dat} 63 } 64 65 p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 66 // this is a destroy plan, 67 if req.ProposedNewState.IsNull() { 68 resp.PlannedState = req.ProposedNewState 69 resp.PlannedPrivate = req.PriorPrivate 70 return resp 71 } 72 73 rSchema, _ := schema.SchemaForResourceType(addrs.ManagedResourceMode, req.TypeName) 74 if rSchema == nil { 75 rSchema = &configschema.Block{} // default schema is empty 76 } 77 plannedVals := map[string]cty.Value{} 78 for name, attrS := range rSchema.Attributes { 79 val := req.ProposedNewState.GetAttr(name) 80 if attrS.Computed && val.IsNull() { 81 val = cty.UnknownVal(attrS.Type) 82 } 83 plannedVals[name] = val 84 } 85 for name := range rSchema.BlockTypes { 86 // For simplicity's sake we just copy the block attributes over 87 // verbatim, since this package's mock providers are all relatively 88 // simple -- we're testing the backend, not esoteric provider features. 89 plannedVals[name] = req.ProposedNewState.GetAttr(name) 90 } 91 92 return providers.PlanResourceChangeResponse{ 93 PlannedState: cty.ObjectVal(plannedVals), 94 PlannedPrivate: req.PriorPrivate, 95 } 96 } 97 p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { 98 return providers.ReadResourceResponse{NewState: req.PriorState} 99 } 100 p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { 101 return providers.ReadDataSourceResponse{State: req.Config} 102 } 103 104 // Initialize the opts 105 if b.ContextOpts == nil { 106 b.ContextOpts = &terraform.ContextOpts{} 107 } 108 109 // Set up our provider 110 b.ContextOpts.Providers = map[addrs.Provider]providers.Factory{ 111 addrs.NewDefaultProvider(name): providers.FactoryFixed(p), 112 } 113 114 return p 115 116 } 117 118 // TestLocalSingleState is a backend implementation that wraps Local 119 // and modifies it to only support single states (returns 120 // ErrWorkspacesNotSupported for multi-state operations). 121 // 122 // This isn't an actual use case, this is exported just to provide a 123 // easy way to test that behavior. 124 type TestLocalSingleState struct { 125 *Local 126 } 127 128 // TestNewLocalSingle is a factory for creating a TestLocalSingleState. 129 // This function matches the signature required for backend/init. 130 func TestNewLocalSingle() backend.Backend { 131 return &TestLocalSingleState{Local: New()} 132 } 133 134 func (b *TestLocalSingleState) Workspaces() ([]string, error) { 135 return nil, backend.ErrWorkspacesNotSupported 136 } 137 138 func (b *TestLocalSingleState) DeleteWorkspace(string, bool) error { 139 return backend.ErrWorkspacesNotSupported 140 } 141 142 func (b *TestLocalSingleState) StateMgr(name string) (statemgr.Full, error) { 143 if name != backend.DefaultStateName { 144 return nil, backend.ErrWorkspacesNotSupported 145 } 146 147 return b.Local.StateMgr(name) 148 } 149 150 // TestLocalNoDefaultState is a backend implementation that wraps 151 // Local and modifies it to support named states, but not the 152 // default state. It returns ErrDefaultWorkspaceNotSupported when 153 // the DefaultStateName is used. 154 type TestLocalNoDefaultState struct { 155 *Local 156 } 157 158 // TestNewLocalNoDefault is a factory for creating a TestLocalNoDefaultState. 159 // This function matches the signature required for backend/init. 160 func TestNewLocalNoDefault() backend.Backend { 161 return &TestLocalNoDefaultState{Local: New()} 162 } 163 164 func (b *TestLocalNoDefaultState) Workspaces() ([]string, error) { 165 workspaces, err := b.Local.Workspaces() 166 if err != nil { 167 return nil, err 168 } 169 170 filtered := workspaces[:0] 171 for _, name := range workspaces { 172 if name != backend.DefaultStateName { 173 filtered = append(filtered, name) 174 } 175 } 176 177 return filtered, nil 178 } 179 180 func (b *TestLocalNoDefaultState) DeleteWorkspace(name string, force bool) error { 181 if name == backend.DefaultStateName { 182 return backend.ErrDefaultWorkspaceNotSupported 183 } 184 return b.Local.DeleteWorkspace(name, force) 185 } 186 187 func (b *TestLocalNoDefaultState) StateMgr(name string) (statemgr.Full, error) { 188 if name == backend.DefaultStateName { 189 return nil, backend.ErrDefaultWorkspaceNotSupported 190 } 191 return b.Local.StateMgr(name) 192 } 193 194 func testStateFile(t *testing.T, path string, s *states.State) { 195 stateFile := statemgr.NewFilesystem(path) 196 stateFile.WriteState(s) 197 } 198 199 func mustProviderConfig(s string) addrs.AbsProviderConfig { 200 p, diags := addrs.ParseAbsProviderConfigStr(s) 201 if diags.HasErrors() { 202 panic(diags.Err()) 203 } 204 return p 205 } 206 207 func mustResourceInstanceAddr(s string) addrs.AbsResourceInstance { 208 addr, diags := addrs.ParseAbsResourceInstanceStr(s) 209 if diags.HasErrors() { 210 panic(diags.Err()) 211 } 212 return addr 213 } 214 215 // assertBackendStateUnlocked attempts to lock the backend state. Failure 216 // indicates that the state was indeed locked and therefore this function will 217 // return true. 218 func assertBackendStateUnlocked(t *testing.T, b *Local) bool { 219 t.Helper() 220 stateMgr, _ := b.StateMgr(backend.DefaultStateName) 221 if _, err := stateMgr.Lock(statemgr.NewLockInfo()); err != nil { 222 t.Errorf("state is already locked: %s", err.Error()) 223 return false 224 } 225 return true 226 } 227 228 // assertBackendStateLocked attempts to lock the backend state. Failure 229 // indicates that the state was already locked and therefore this function will 230 // return false. 231 func assertBackendStateLocked(t *testing.T, b *Local) bool { 232 t.Helper() 233 stateMgr, _ := b.StateMgr(backend.DefaultStateName) 234 if _, err := stateMgr.Lock(statemgr.NewLockInfo()); err != nil { 235 return true 236 } 237 t.Error("unexpected success locking state") 238 return true 239 }