| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package terraform |
| |
| import ( |
| "encoding/json" |
| "sync" |
| |
| "github.com/zclconf/go-cty/cty" |
| ctyjson "github.com/zclconf/go-cty/cty/json" |
| |
| "github.com/hashicorp/terraform/internal/configs/hcl2shim" |
| "github.com/hashicorp/terraform/internal/providers" |
| ) |
| |
| var _ providers.Interface = (*MockProvider)(nil) |
| |
| // MockProvider implements providers.Interface but mocks out all the |
| // calls for testing purposes. |
| type MockProvider struct { |
| sync.Mutex |
| |
| // Anything you want, in case you need to store extra data with the mock. |
| Meta interface{} |
| |
| GetSchemaCalled bool |
| GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetProviderSchemaResponse for compatibility with old tests |
| |
| ValidateProviderConfigCalled bool |
| ValidateProviderConfigResponse providers.ValidateProviderConfigResponse |
| ValidateProviderConfigRequest providers.ValidateProviderConfigRequest |
| ValidateProviderConfigFn func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse |
| |
| ValidateResourceConfigCalled bool |
| ValidateResourceConfigTypeName string |
| ValidateResourceConfigResponse providers.ValidateResourceConfigResponse |
| ValidateResourceConfigRequest providers.ValidateResourceConfigRequest |
| ValidateResourceConfigFn func(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse |
| |
| ValidateDataResourceConfigCalled bool |
| ValidateDataResourceConfigTypeName string |
| ValidateDataResourceConfigResponse providers.ValidateDataResourceConfigResponse |
| ValidateDataResourceConfigRequest providers.ValidateDataResourceConfigRequest |
| ValidateDataResourceConfigFn func(providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse |
| |
| UpgradeResourceStateCalled bool |
| UpgradeResourceStateTypeName string |
| UpgradeResourceStateResponse providers.UpgradeResourceStateResponse |
| UpgradeResourceStateRequest providers.UpgradeResourceStateRequest |
| UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse |
| |
| ConfigureProviderCalled bool |
| ConfigureProviderResponse providers.ConfigureProviderResponse |
| ConfigureProviderRequest providers.ConfigureProviderRequest |
| ConfigureProviderFn func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse |
| |
| StopCalled bool |
| StopFn func() error |
| StopResponse error |
| |
| ReadResourceCalled bool |
| ReadResourceResponse providers.ReadResourceResponse |
| ReadResourceRequest providers.ReadResourceRequest |
| ReadResourceFn func(providers.ReadResourceRequest) providers.ReadResourceResponse |
| |
| PlanResourceChangeCalled bool |
| PlanResourceChangeResponse providers.PlanResourceChangeResponse |
| PlanResourceChangeRequest providers.PlanResourceChangeRequest |
| PlanResourceChangeFn func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse |
| |
| ApplyResourceChangeCalled bool |
| ApplyResourceChangeResponse providers.ApplyResourceChangeResponse |
| ApplyResourceChangeRequest providers.ApplyResourceChangeRequest |
| ApplyResourceChangeFn func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse |
| |
| ImportResourceStateCalled bool |
| ImportResourceStateResponse providers.ImportResourceStateResponse |
| ImportResourceStateRequest providers.ImportResourceStateRequest |
| ImportResourceStateFn func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse |
| // Legacy return type for existing tests, which will be shimmed into an |
| // ImportResourceStateResponse if set |
| ImportStateReturn []*InstanceState |
| |
| ReadDataSourceCalled bool |
| ReadDataSourceResponse providers.ReadDataSourceResponse |
| ReadDataSourceRequest providers.ReadDataSourceRequest |
| ReadDataSourceFn func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse |
| |
| CloseCalled bool |
| CloseError error |
| } |
| |
| func (p *MockProvider) GetProviderSchema() providers.GetProviderSchemaResponse { |
| p.Lock() |
| defer p.Unlock() |
| p.GetSchemaCalled = true |
| return p.getSchema() |
| } |
| |
| func (p *MockProvider) getSchema() providers.GetProviderSchemaResponse { |
| // This version of getSchema doesn't do any locking, so it's suitable to |
| // call from other methods of this mock as long as they are already |
| // holding the lock. |
| |
| ret := providers.GetProviderSchemaResponse{ |
| Provider: providers.Schema{}, |
| DataSources: map[string]providers.Schema{}, |
| ResourceTypes: map[string]providers.Schema{}, |
| } |
| if p.GetSchemaReturn != nil { |
| ret.Provider.Block = p.GetSchemaReturn.Provider |
| ret.ProviderMeta.Block = p.GetSchemaReturn.ProviderMeta |
| for n, s := range p.GetSchemaReturn.DataSources { |
| ret.DataSources[n] = providers.Schema{ |
| Block: s, |
| } |
| } |
| for n, s := range p.GetSchemaReturn.ResourceTypes { |
| ret.ResourceTypes[n] = providers.Schema{ |
| Version: int64(p.GetSchemaReturn.ResourceTypeSchemaVersions[n]), |
| Block: s, |
| } |
| } |
| } |
| |
| return ret |
| } |
| |
| func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ValidateProviderConfigCalled = true |
| p.ValidateProviderConfigRequest = r |
| if p.ValidateProviderConfigFn != nil { |
| return p.ValidateProviderConfigFn(r) |
| } |
| return p.ValidateProviderConfigResponse |
| } |
| |
| func (p *MockProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ValidateResourceConfigCalled = true |
| p.ValidateResourceConfigRequest = r |
| |
| if p.ValidateResourceConfigFn != nil { |
| return p.ValidateResourceConfigFn(r) |
| } |
| |
| return p.ValidateResourceConfigResponse |
| } |
| |
| func (p *MockProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ValidateDataResourceConfigCalled = true |
| p.ValidateDataResourceConfigRequest = r |
| |
| if p.ValidateDataResourceConfigFn != nil { |
| return p.ValidateDataResourceConfigFn(r) |
| } |
| |
| return p.ValidateDataResourceConfigResponse |
| } |
| |
| func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| schemas := p.getSchema() |
| schema := schemas.ResourceTypes[r.TypeName] |
| schemaType := schema.Block.ImpliedType() |
| |
| p.UpgradeResourceStateCalled = true |
| p.UpgradeResourceStateRequest = r |
| |
| if p.UpgradeResourceStateFn != nil { |
| return p.UpgradeResourceStateFn(r) |
| } |
| |
| resp := p.UpgradeResourceStateResponse |
| |
| if resp.UpgradedState == cty.NilVal { |
| switch { |
| case r.RawStateFlatmap != nil: |
| v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) |
| if err != nil { |
| resp.Diagnostics = resp.Diagnostics.Append(err) |
| return resp |
| } |
| resp.UpgradedState = v |
| case len(r.RawStateJSON) > 0: |
| v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) |
| |
| if err != nil { |
| resp.Diagnostics = resp.Diagnostics.Append(err) |
| return resp |
| } |
| resp.UpgradedState = v |
| } |
| } |
| return resp |
| } |
| |
| func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ConfigureProviderCalled = true |
| p.ConfigureProviderRequest = r |
| |
| if p.ConfigureProviderFn != nil { |
| return p.ConfigureProviderFn(r) |
| } |
| |
| return p.ConfigureProviderResponse |
| } |
| |
| func (p *MockProvider) Stop() error { |
| // We intentionally don't lock in this one because the whole point of this |
| // method is to be called concurrently with another operation that can |
| // be cancelled. The provider itself is responsible for handling |
| // any concurrency concerns in this case. |
| |
| p.StopCalled = true |
| if p.StopFn != nil { |
| return p.StopFn() |
| } |
| |
| return p.StopResponse |
| } |
| |
| func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ReadResourceCalled = true |
| p.ReadResourceRequest = r |
| |
| if p.ReadResourceFn != nil { |
| return p.ReadResourceFn(r) |
| } |
| |
| resp := p.ReadResourceResponse |
| if resp.NewState != cty.NilVal { |
| // make sure the NewState fits the schema |
| // This isn't always the case for the existing tests |
| newState, err := p.GetSchemaReturn.ResourceTypes[r.TypeName].CoerceValue(resp.NewState) |
| if err != nil { |
| panic(err) |
| } |
| resp.NewState = newState |
| return resp |
| } |
| |
| // just return the same state we received |
| resp.NewState = r.PriorState |
| return resp |
| } |
| |
| func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.PlanResourceChangeCalled = true |
| p.PlanResourceChangeRequest = r |
| |
| if p.PlanResourceChangeFn != nil { |
| return p.PlanResourceChangeFn(r) |
| } |
| |
| return p.PlanResourceChangeResponse |
| } |
| |
| func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { |
| p.Lock() |
| p.ApplyResourceChangeCalled = true |
| p.ApplyResourceChangeRequest = r |
| p.Unlock() |
| |
| if p.ApplyResourceChangeFn != nil { |
| return p.ApplyResourceChangeFn(r) |
| } |
| |
| return p.ApplyResourceChangeResponse |
| } |
| |
| func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| if p.ImportStateReturn != nil { |
| for _, is := range p.ImportStateReturn { |
| if is.Attributes == nil { |
| is.Attributes = make(map[string]string) |
| } |
| is.Attributes["id"] = is.ID |
| |
| typeName := is.Ephemeral.Type |
| // Use the requested type if the resource has no type of it's own. |
| // We still return the empty type, which will error, but this prevents a panic. |
| if typeName == "" { |
| typeName = r.TypeName |
| } |
| |
| schema := p.GetSchemaReturn.ResourceTypes[typeName] |
| if schema == nil { |
| panic("no schema found for " + typeName) |
| } |
| |
| private, err := json.Marshal(is.Meta) |
| if err != nil { |
| panic(err) |
| } |
| |
| state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType()) |
| if err != nil { |
| panic(err) |
| } |
| |
| state, err = schema.CoerceValue(state) |
| if err != nil { |
| panic(err) |
| } |
| |
| p.ImportResourceStateResponse.ImportedResources = append( |
| p.ImportResourceStateResponse.ImportedResources, |
| providers.ImportedResource{ |
| TypeName: is.Ephemeral.Type, |
| State: state, |
| Private: private, |
| }) |
| } |
| } |
| |
| p.ImportResourceStateCalled = true |
| p.ImportResourceStateRequest = r |
| if p.ImportResourceStateFn != nil { |
| return p.ImportResourceStateFn(r) |
| } |
| |
| return p.ImportResourceStateResponse |
| } |
| |
| func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { |
| p.Lock() |
| defer p.Unlock() |
| |
| p.ReadDataSourceCalled = true |
| p.ReadDataSourceRequest = r |
| |
| if p.ReadDataSourceFn != nil { |
| return p.ReadDataSourceFn(r) |
| } |
| |
| return p.ReadDataSourceResponse |
| } |
| |
| func (p *MockProvider) Close() error { |
| p.CloseCalled = true |
| return p.CloseError |
| } |