mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-23 23:50:12 -06:00
9d9a7aab06
Signed-off-by: ollevche <ollevche@gmail.com>
208 lines
6.7 KiB
Go
208 lines
6.7 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package tofu
|
|
|
|
import (
|
|
"fmt"
|
|
"hash/fnv"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
|
|
"github.com/opentofu/opentofu/internal/providers"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
var _ providers.Interface = &providerForTest{}
|
|
|
|
// providerForTest is a wrapper around a real provider to allow certain resources to be overridden
|
|
// (by address) or mocked (by provider and resource type) for testing framework.
|
|
type providerForTest struct {
|
|
// providers.Interface is not embedded to make it safer to extend
|
|
// the interface without silently breaking providerForTest functionality.
|
|
internal providers.Interface
|
|
schema providers.ProviderSchema
|
|
|
|
managedResources resourceForTestByType
|
|
dataResources resourceForTestByType
|
|
}
|
|
|
|
func newProviderForTestWithSchema(internal providers.Interface, schema providers.ProviderSchema) *providerForTest {
|
|
return &providerForTest{
|
|
internal: internal,
|
|
schema: schema,
|
|
managedResources: make(resourceForTestByType),
|
|
dataResources: make(resourceForTestByType),
|
|
}
|
|
}
|
|
|
|
func newProviderForTest(internal providers.Interface, res []*configs.MockResource) (providers.Interface, error) {
|
|
schema := internal.GetProviderSchema()
|
|
if schema.Diagnostics.HasErrors() {
|
|
return nil, fmt.Errorf("getting provider schema for test wrapper: %w", schema.Diagnostics.Err())
|
|
}
|
|
|
|
p := newProviderForTestWithSchema(internal, schema)
|
|
|
|
p.addMockResources(res)
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func (p *providerForTest) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
|
|
var resp providers.ReadResourceResponse
|
|
|
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
|
|
|
overrideValues := p.managedResources.getOverrideValues(r.TypeName)
|
|
|
|
resp.NewState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
|
ComposeBySchema(resSchema, r.ProviderMeta, overrideValues)
|
|
|
|
return resp
|
|
}
|
|
|
|
func (p *providerForTest) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
|
if r.Config.IsNull() {
|
|
return providers.PlanResourceChangeResponse{
|
|
PlannedState: r.ProposedNewState, // null
|
|
}
|
|
}
|
|
|
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.ManagedResourceMode, r.TypeName)
|
|
|
|
overrideValues := p.managedResources.getOverrideValues(r.TypeName)
|
|
|
|
var resp providers.PlanResourceChangeResponse
|
|
|
|
resp.PlannedState, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
|
ComposeBySchema(resSchema, r.Config, overrideValues)
|
|
|
|
return resp
|
|
}
|
|
|
|
func (p *providerForTest) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
|
return providers.ApplyResourceChangeResponse{
|
|
NewState: r.PlannedState,
|
|
}
|
|
}
|
|
|
|
func (p *providerForTest) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
|
resSchema, _ := p.schema.SchemaForResourceType(addrs.DataResourceMode, r.TypeName)
|
|
|
|
var resp providers.ReadDataSourceResponse
|
|
|
|
overrideValues := p.dataResources.getOverrideValues(r.TypeName)
|
|
|
|
resp.State, resp.Diagnostics = newMockValueComposer(r.TypeName).
|
|
ComposeBySchema(resSchema, r.Config, overrideValues)
|
|
|
|
return resp
|
|
}
|
|
|
|
// Calling the internal provider ensures providerForTest has the same behaviour as if
|
|
// it wasn't overridden or mocked. The only exception is ImportResourceState, which panics
|
|
// if called via providerForTest because importing is not supported in testing framework.
|
|
|
|
func (p *providerForTest) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|
return p.internal.GetProviderSchema()
|
|
}
|
|
|
|
func (p *providerForTest) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
|
return p.internal.ValidateProviderConfig(r)
|
|
}
|
|
|
|
func (p *providerForTest) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
|
|
return p.internal.ValidateResourceConfig(r)
|
|
}
|
|
|
|
func (p *providerForTest) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse {
|
|
return p.internal.ValidateDataResourceConfig(r)
|
|
}
|
|
|
|
func (p *providerForTest) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
|
return p.internal.UpgradeResourceState(r)
|
|
}
|
|
|
|
// providerForTest doesn't configure its internal provider because it is mocked.
|
|
func (p *providerForTest) ConfigureProvider(_ providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
|
return providers.ConfigureProviderResponse{}
|
|
}
|
|
|
|
func (p *providerForTest) Stop() error {
|
|
return p.internal.Stop()
|
|
}
|
|
|
|
func (p *providerForTest) GetFunctions() providers.GetFunctionsResponse {
|
|
return p.internal.GetFunctions()
|
|
}
|
|
|
|
func (p *providerForTest) CallFunction(r providers.CallFunctionRequest) providers.CallFunctionResponse {
|
|
return p.internal.CallFunction(r)
|
|
}
|
|
|
|
func (p *providerForTest) Close() error {
|
|
return p.internal.Close()
|
|
}
|
|
|
|
func (p *providerForTest) ImportResourceState(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
|
panic("Importing is not supported in testing context. providerForTest must not be used to call ImportResourceState")
|
|
}
|
|
|
|
func (p *providerForTest) setSingleResource(addr addrs.Resource, overrides map[string]cty.Value) {
|
|
res := resourceForTest{
|
|
overrideValues: overrides,
|
|
}
|
|
|
|
switch addr.Mode {
|
|
case addrs.ManagedResourceMode:
|
|
p.managedResources[addr.Type] = res
|
|
case addrs.DataResourceMode:
|
|
p.dataResources[addr.Type] = res
|
|
case addrs.InvalidResourceMode:
|
|
panic("BUG: invalid mock resource mode")
|
|
default:
|
|
panic("BUG: unsupported resource mode: " + addr.Mode.String())
|
|
}
|
|
}
|
|
|
|
func (p *providerForTest) addMockResources(mockResources []*configs.MockResource) {
|
|
for _, mockRes := range mockResources {
|
|
var resources resourceForTestByType
|
|
|
|
switch mockRes.Mode {
|
|
case addrs.ManagedResourceMode:
|
|
resources = p.managedResources
|
|
case addrs.DataResourceMode:
|
|
resources = p.dataResources
|
|
case addrs.InvalidResourceMode:
|
|
panic("BUG: invalid mock resource mode")
|
|
default:
|
|
panic("BUG: unsupported mock resource mode: " + mockRes.Mode.String())
|
|
}
|
|
|
|
resources[mockRes.Type] = resourceForTest{
|
|
overrideValues: mockRes.Defaults,
|
|
}
|
|
}
|
|
}
|
|
|
|
type resourceForTest struct {
|
|
overrideValues map[string]cty.Value
|
|
}
|
|
|
|
type resourceForTestByType map[string]resourceForTest
|
|
|
|
func (m resourceForTestByType) getOverrideValues(typeName string) map[string]cty.Value {
|
|
return m[typeName].overrideValues
|
|
}
|
|
|
|
func newMockValueComposer(typeName string) hcl2shim.MockValueComposer {
|
|
hash := fnv.New64()
|
|
hash.Write([]byte(typeName))
|
|
return hcl2shim.NewMockValueComposer(int64(hash.Sum64()))
|
|
}
|