// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package graph import ( "fmt" "log" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/moduletest" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/version" ) var ( _ GraphNodeExecutable = (*NodeProviderConfigure)(nil) _ GraphNodeReferencer = (*NodeProviderConfigure)(nil) ) type NodeProviderConfigure struct { name, alias string Addr addrs.RootProviderConfig File *moduletest.File Config *configs.Provider Provider providers.Interface Schema providers.GetProviderSchemaResponse } func (n *NodeProviderConfigure) Name() string { if len(n.alias) > 0 { return fmt.Sprintf("provider.%s.%s", n.name, n.alias) } return fmt.Sprintf("provider.%s", n.name) } func (n *NodeProviderConfigure) Execute(ctx *EvalContext) { log.Printf("[TRACE]: NodeProviderConfigure: configuring provider %s for tests", n.Addr) if ctx.Cancelled() || ctx.Stopped() { return } // first, set the provider so everything else can use it ctx.SetProvider(n.Addr, n.Provider) spec := n.Schema.Provider.Body.DecoderSpec() var references []*addrs.Reference var referenceDiags tfdiags.Diagnostics for _, traversal := range hcldec.Variables(n.Config.Config, spec) { ref, moreDiags := addrs.ParseRefFromTestingScope(traversal) referenceDiags = referenceDiags.Append(moreDiags) if ref != nil { references = append(references, ref) } } n.File.AppendDiagnostics(referenceDiags) if referenceDiags.HasErrors() { ctx.SetProviderStatus(n.Addr, moduletest.Error) return } if !ctx.ReferencesCompleted(references) { ctx.SetProviderStatus(n.Addr, moduletest.Skip) return } hclContext, moreDiags := ctx.HclContext(references) n.File.AppendDiagnostics(moreDiags) if moreDiags.HasErrors() { ctx.SetProviderStatus(n.Addr, moduletest.Error) return } // This means we are using a mock provider, which may contain not-yet-evaluated // mock data, so we will evaluate the data here. if mock, ok := n.Provider.(*providers.Mock); ok { for _, res := range mock.Data.MockResources { values, exprHclDiags := res.RawExpr.Value(hclContext) moreDiags = moreDiags.Append(exprHclDiags) res.Defaults = values } for _, res := range mock.Data.MockDataSources { values, exprHclDiags := res.RawExpr.Value(hclContext) moreDiags = moreDiags.Append(exprHclDiags) res.Defaults = values } } body, decHclDiags := hcldec.Decode(n.Config.Config, spec, hclContext) moreDiags = moreDiags.Append(decHclDiags) if moreDiags.HasErrors() { n.File.AppendDiagnostics(moreDiags) ctx.SetProviderStatus(n.Addr, moduletest.Error) return } unmarkedBody, _ := body.UnmarkDeep() response := n.Provider.ConfigureProvider(providers.ConfigureProviderRequest{ TerraformVersion: version.SemVer.String(), Config: unmarkedBody, ClientCapabilities: providers.ClientCapabilities{ DeferralAllowed: ctx.deferralAllowed, WriteOnlyAttributesAllowed: true, }, }) n.File.AppendDiagnostics(response.Diagnostics) if response.Diagnostics.HasErrors() { ctx.SetProviderStatus(n.Addr, moduletest.Error) return } } func (n *NodeProviderConfigure) References() []*addrs.Reference { var refs []*addrs.Reference for _, variable := range hcldec.Variables(n.Config.Config, n.Schema.Provider.Body.DecoderSpec()) { ref, _ := addrs.ParseRefFromTestingScope(variable) if ref != nil { refs = append(refs, ref) } } return refs } var ( _ GraphNodeExecutable = (*NodeProviderClose)(nil) ) type NodeProviderClose struct { name, alias string Addr addrs.RootProviderConfig File *moduletest.File Config *configs.Provider Provider providers.Interface } func (n *NodeProviderClose) Name() string { if len(n.alias) > 0 { return fmt.Sprintf("provider.%s.%s (close)", n.name, n.alias) } return fmt.Sprintf("provider.%s (close)", n.name) } func (n *NodeProviderClose) Execute(ctx *EvalContext) { log.Printf("[TRACE]: NodeProviderClose: closing provider %s for tests", n.Addr) // we don't check for cancelled or stopped here - we still want to kill // any running providers even if someone has stopped or cancelled the // process if err := n.Provider.Close(); err != nil { var diags tfdiags.Diagnostics diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Failed to close provider", Detail: fmt.Sprintf("Failed to close provider: %s", err.Error()), Subject: n.Config.DeclRange.Ptr(), }) n.File.AppendDiagnostics(diags) } }