blob: 7f0faea443aeb0cf757670e120e39cee06be8132 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package graph
import (
"fmt"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/moduletest"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/terraform"
)
// TestProvidersTransformer is a GraphTransformer that gathers all the providers
// from the module configurations that the test runs depend on and attaches the
// required providers to the test run nodes.
type TestProvidersTransformer struct {
Config *configs.Config
File *moduletest.File
Providers map[addrs.Provider]providers.Factory
}
func (t *TestProvidersTransformer) Transform(g *terraform.Graph) error {
type tuple struct {
configure *NodeProviderConfigure
close *NodeProviderClose
}
nodes := make(map[string]map[string]tuple)
for _, config := range t.File.Config.Providers {
provider := t.Config.ProviderForConfigAddr(config.Addr())
factory, ok := t.Providers[provider]
if !ok {
return fmt.Errorf("unknown provider %s", provider)
}
impl, err := factory()
if err != nil {
return fmt.Errorf("could not create provider instance: %w", err)
}
if config.Mock {
impl = &providers.Mock{
Provider: impl,
Data: config.MockData,
}
}
addr := addrs.RootProviderConfig{
Provider: provider,
Alias: config.Alias,
}
configure := &NodeProviderConfigure{
name: config.Name,
alias: config.Alias,
Addr: addr,
File: t.File,
Config: config,
Provider: impl,
Schema: impl.GetProviderSchema(),
}
g.Add(configure)
close := &NodeProviderClose{
name: config.Name,
alias: config.Alias,
Addr: addr,
File: t.File,
Config: config,
Provider: impl,
}
g.Add(close)
if _, exists := nodes[config.Name]; !exists {
nodes[config.Name] = make(map[string]tuple)
}
nodes[config.Name][config.Alias] = tuple{
configure: configure,
close: close,
}
}
for vertex := range g.VerticesSeq() {
if vertex, ok := vertex.(*NodeTestRun); ok {
// providers aren't referenceable so the automatic reference
// transformer won't do this.
if len(vertex.Run().Config.Providers) > 0 {
for _, ref := range vertex.run.Config.Providers {
if node, ok := nodes[ref.InParent.Name][ref.InParent.Alias]; ok {
g.Connect(dag.BasicEdge(vertex, node.configure))
}
}
} else {
for provider := range requiredProviders(vertex.run.ModuleConfig) {
name := t.Config.Module.LocalNameForProvider(provider.Provider)
if node, ok := nodes[name][provider.Alias]; ok {
g.Connect(dag.BasicEdge(vertex, node.configure))
}
}
}
}
if vertex, ok := vertex.(*TeardownSubgraph); ok {
for _, node := range nodes {
for _, node := range node {
// close all the providers after the states have been
// cleaned up.
g.Connect(dag.BasicEdge(node.close, vertex))
}
}
}
}
return nil
}