Files
opentf/internal/tofu/transform_attach_schema.go
Martin Atkins 098d23ecdf tofu: Plumb context.Context through the resource handling functions
The graph node types related to resources and resource instances use a
bunch of helper functions in different combinations, rather than calling
directly into the provider API.

This commit plumbs context.Context through to the functions that _do_
eventually call methods directly on the provider object, leaving us just
one more step away from plumbing the context through to the actual gRPC
calls. The next step (in a future commit) will be to update the
providers.Interface methods to take context.Context arguments and then
have the gRPC-based implementations of that interface pass the context
through to the gRPC client stub calls, and then we should be pretty close
to being able to turn on OTel tracing instrumentation for our gRPC
client requests.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-05-22 08:05:43 -07:00

118 lines
4.1 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 (
"context"
"fmt"
"log"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/dag"
)
// GraphNodeAttachResourceSchema is an interface implemented by node types
// that need a resource schema attached.
type GraphNodeAttachResourceSchema interface {
GraphNodeConfigResource
GraphNodeProviderConsumer
AttachResourceSchema(schema *configschema.Block, version uint64)
}
// GraphNodeAttachProviderConfigSchema is an interface implemented by node types
// that need a provider configuration schema attached.
type GraphNodeAttachProviderConfigSchema interface {
GraphNodeProvider
AttachProviderConfigSchema(*configschema.Block)
}
// GraphNodeAttachProvisionerSchema is an interface implemented by node types
// that need one or more provisioner schemas attached.
type GraphNodeAttachProvisionerSchema interface {
ProvisionedBy() []string
// SetProvisionerSchema is called during transform for each provisioner
// type returned from ProvisionedBy, providing the configuration schema
// for each provisioner in turn. The implementer should save these for
// later use in evaluating provisioner configuration blocks.
AttachProvisionerSchema(name string, schema *configschema.Block)
}
// AttachSchemaTransformer finds nodes that implement
// GraphNodeAttachResourceSchema, GraphNodeAttachProviderConfigSchema, or
// GraphNodeAttachProvisionerSchema, looks up the needed schemas for each
// and then passes them to a method implemented by the node.
type AttachSchemaTransformer struct {
Plugins *contextPlugins
Config *configs.Config
}
func (t *AttachSchemaTransformer) Transform(ctx context.Context, g *Graph) error {
if t.Plugins == nil {
// Should never happen with a reasonable caller, but we'll return a
// proper error here anyway so that we'll fail gracefully.
return fmt.Errorf("AttachSchemaTransformer used with nil Plugins")
}
for _, v := range g.Vertices() {
if tv, ok := v.(GraphNodeAttachResourceSchema); ok {
addr := tv.ResourceAddr()
mode := addr.Resource.Mode
typeName := addr.Resource.Type
providerFqn := tv.Provider()
// TODO: Plumb a useful context.Context through to here.
schema, version, err := t.Plugins.ResourceTypeSchema(ctx, providerFqn, mode, typeName)
if err != nil {
return fmt.Errorf("failed to read schema for %s in %s: %w", addr, providerFqn, err)
}
if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
continue
}
log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v))
tv.AttachResourceSchema(schema, version)
}
if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok {
providerAddr := tv.ProviderAddr()
// TODO: Plumb a useful context.Context through to here.
schema, err := t.Plugins.ProviderConfigSchema(ctx, providerAddr.Provider)
if err != nil {
return fmt.Errorf("failed to read provider configuration schema for %s: %w", providerAddr.Provider, err)
}
if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No provider config schema available for %s", providerAddr)
continue
}
log.Printf("[TRACE] AttachSchemaTransformer: attaching provider config schema to %s", dag.VertexName(v))
tv.AttachProviderConfigSchema(schema)
}
if tv, ok := v.(GraphNodeAttachProvisionerSchema); ok {
names := tv.ProvisionedBy()
for _, name := range names {
schema, err := t.Plugins.ProvisionerSchema(name)
if err != nil {
return fmt.Errorf("failed to read provisioner configuration schema for %q: %w", name, err)
}
if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No schema available for provisioner %q on %q", name, dag.VertexName(v))
continue
}
log.Printf("[TRACE] AttachSchemaTransformer: attaching provisioner %q config schema to %s", name, dag.VertexName(v))
tv.AttachProvisionerSchema(name, schema)
}
}
}
return nil
}