mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
In "package tofu" today we try to do everything using a generic acyclic graph model and generic graph walk, which _works_ but tends to make every other part of the problem very hard to follow because we rely a lot on sidecar shared mutable data structures to propagate results between the isolated operations. This is the beginning of an experimental new way to do it where the "graph" is implied by a model that more closely represents how the language itself works, with explicit modelling of the relationships between different types of objects and letting results flow directly from one object to another without any big shared mutable state. There's still a lot to do before this is actually complete enough to evaluate whether it's a viable new design, but I'm considering this a good starting checkpoint since there's enough here to run a simple test of propagating data all the way from input variables to output values via intermediate local values. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
137 lines
4.9 KiB
Go
137 lines
4.9 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 eval
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/apparentlymart/go-versions/versions"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
|
"github.com/opentofu/opentofu/internal/providers"
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
// ModulesForTesting returns an [ExternalModules] implementation that just
|
|
// returns module objects directly from the provided map, without any additional
|
|
// logic.
|
|
//
|
|
// This is intended for unit testing only, and only supports local module
|
|
// source addresses because it has no means to resolve remote sources or
|
|
// selected versions for registry-based modules.
|
|
//
|
|
// [configs.ModulesFromStringsForTesting] is a convenient way to build a
|
|
// suitable map to pass to this function when the required configuration is
|
|
// relatively small.
|
|
func ModulesForTesting(modules map[addrs.ModuleSourceLocal]*configs.Module) ExternalModules {
|
|
return externalModulesStatic{modules}
|
|
}
|
|
|
|
// ProvidersForTesting returns a [Providers] implementation that just returns
|
|
// information directly from the given map.
|
|
//
|
|
// This is intended for unit testing only.
|
|
func ProvidersForTesting(schemas map[addrs.Provider]*providers.GetProviderSchemaResponse) Providers {
|
|
return providersStatic{schemas}
|
|
}
|
|
|
|
// ProvisionersForTesting returns a [Provisioners] implementation that just
|
|
// returns information directly from the given map.
|
|
//
|
|
// This is intended for unit testing only.
|
|
func ProvisionersForTesting(schemas map[string]*configschema.Block) Provisioners {
|
|
return provisionersStatic{schemas}
|
|
}
|
|
|
|
type externalModulesStatic struct {
|
|
modules map[addrs.ModuleSourceLocal]*configs.Module
|
|
}
|
|
|
|
// ModuleConfig implements ExternalModules.
|
|
func (ms externalModulesStatic) ModuleConfig(_ context.Context, source addrs.ModuleSource, _ versions.Set, _ *addrs.AbsModuleCall) (*configs.Module, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
localSource, ok := source.(addrs.ModuleSourceLocal)
|
|
if !ok {
|
|
diags = diags.Append(fmt.Errorf("only local module source addresses are supported for this test"))
|
|
return nil, diags
|
|
}
|
|
ret, ok := ms.modules[localSource]
|
|
if !ok {
|
|
diags = diags.Append(fmt.Errorf("module path %q is not available to this test", localSource))
|
|
return nil, diags
|
|
}
|
|
return ret, diags
|
|
}
|
|
|
|
type providersStatic struct {
|
|
schemas map[addrs.Provider]*providers.GetProviderSchemaResponse
|
|
}
|
|
|
|
// ProviderConfigSchema implements Providers.
|
|
func (p providersStatic) ProviderConfigSchema(ctx context.Context, provider addrs.Provider) (*providers.Schema, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
topSchema, ok := p.schemas[provider]
|
|
if !ok {
|
|
diags = diags.Append(fmt.Errorf("provider %q is not available to this test", provider))
|
|
return nil, diags
|
|
}
|
|
return &topSchema.Provider, diags
|
|
}
|
|
|
|
// ValidateProviderConfig implements Providers by doing nothing at all, because
|
|
// in this implementation providers consist only of schema and have no behavior.
|
|
func (p providersStatic) ValidateProviderConfig(ctx context.Context, provider addrs.Provider, configVal cty.Value) tfdiags.Diagnostics {
|
|
return nil
|
|
}
|
|
|
|
// ResourceTypeSchema implements Providers.
|
|
func (p providersStatic) ResourceTypeSchema(_ context.Context, provider addrs.Provider, mode addrs.ResourceMode, typeName string) (*providers.Schema, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
topSchema, ok := p.schemas[provider]
|
|
if !ok {
|
|
diags = diags.Append(fmt.Errorf("provider %q is not available to this test", provider))
|
|
return nil, diags
|
|
}
|
|
var typesOfMode map[string]providers.Schema
|
|
switch mode {
|
|
case addrs.ManagedResourceMode:
|
|
typesOfMode = topSchema.ResourceTypes
|
|
case addrs.DataResourceMode:
|
|
typesOfMode = topSchema.DataSources
|
|
case addrs.EphemeralResourceMode:
|
|
typesOfMode = topSchema.EphemeralResources
|
|
default:
|
|
typesOfMode = nil // no other modes supported
|
|
}
|
|
schema, ok := typesOfMode[typeName]
|
|
if !ok {
|
|
// The requirements for this interface say we should return nil to
|
|
// represent that there is no such resource type, so that the caller
|
|
// can provide its own error message for that.
|
|
return nil, diags
|
|
}
|
|
return &schema, diags
|
|
}
|
|
|
|
// ValidateResourceConfig implements Providers by doing nothing at all, because
|
|
// in this implementation providers consist only of schema and have no behavior.
|
|
func (p providersStatic) ValidateResourceConfig(ctx context.Context, provider addrs.Provider, mode addrs.ResourceMode, typeName string, configVal cty.Value) tfdiags.Diagnostics {
|
|
return nil
|
|
}
|
|
|
|
type provisionersStatic struct {
|
|
schemas map[string]*configschema.Block
|
|
}
|
|
|
|
// ProvisionerConfigSchema implements Provisioners.
|
|
func (p provisionersStatic) ProvisionerConfigSchema(_ context.Context, typeName string) (*configschema.Block, tfdiags.Diagnostics) {
|
|
return p.schemas[typeName], nil
|
|
}
|