| // 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, | |
| ComputedBlocksAllowed: 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) | |
| } | |
| } |