blob: ec55ef7a71dacb82cb467861c595fa84fedfccc0 [file] [edit]
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package e2etest
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/e2e"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/getproviders/providerreqs"
"github.com/hashicorp/terraform/internal/grpcwrap"
tfplugin "github.com/hashicorp/terraform/internal/plugin6"
simple "github.com/hashicorp/terraform/internal/provider-simple-v6"
proto "github.com/hashicorp/terraform/internal/tfplugin6"
)
// TestProviderProtocols verifies that Terraform can execute provider plugins
// with both supported protocol versions.
func TestProviderProtocols(t *testing.T) {
if !canRunGoBuild {
// We're running in a separate-build-then-run context, so we can't
// currently execute this test which depends on being able to build
// new executable at runtime.
//
// (See the comment on canRunGoBuild's declaration for more information.)
t.Skip("can't run without building a new provider executable")
}
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, "testdata/provider-plugin")
// In order to do a decent end-to-end test for this case we will need a real
// enough provider plugin to try to run and make sure we are able to
// actually run it. Here will build the simple and simple6 (built with
// protocol v6) providers.
simple6Provider := filepath.Join(tf.WorkDir(), "terraform-provider-simple6")
simple6ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple-v6/main", simple6Provider)
simpleProvider := filepath.Join(tf.WorkDir(), "terraform-provider-simple")
simpleProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple/main", simpleProvider)
// Move the provider binaries into a directory that we will point terraform
// to using the -plugin-dir cli flag.
platform := getproviders.CurrentPlatform.String()
hashiDir := "cache/registry.terraform.io/hashicorp/"
if err := os.MkdirAll(tf.Path(hashiDir, "simple6/0.0.1/", platform), os.ModePerm); err != nil {
t.Fatal(err)
}
if err := os.Rename(simple6ProviderExe, tf.Path(hashiDir, "simple6/0.0.1/", platform, "terraform-provider-simple6")); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(tf.Path(hashiDir, "simple/0.0.1/", platform), os.ModePerm); err != nil {
t.Fatal(err)
}
if err := os.Rename(simpleProviderExe, tf.Path(hashiDir, "simple/0.0.1/", platform, "terraform-provider-simple")); err != nil {
t.Fatal(err)
}
//// INIT
_, stderr, err := tf.Run("init", "-plugin-dir=cache")
if err != nil {
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
}
//// PLAN
_, stderr, err = tf.Run("plan", "-out=tfplan")
if err != nil {
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
}
//// APPLY
stdout, stderr, err := tf.Run("apply", "tfplan")
if err != nil {
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Apply complete! Resources: 2 added, 0 changed, 0 destroyed.") {
t.Fatalf("wrong output:\nstdout:%s\nstderr%s", stdout, stderr)
}
/// DESTROY
stdout, stderr, err = tf.Run("destroy", "-auto-approve")
if err != nil {
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Resources: 2 destroyed") {
t.Fatalf("wrong destroy output\nstdout:%s\nstderr:%s", stdout, stderr)
}
}
// TestProviderInstall_dev_override verifies provider plugin installation behaviour
// when a dev_override is in use.
func TestProviderInstall_dev_override(t *testing.T) {
if !canRunGoBuild {
// We're running in a separate-build-then-run context, so we can't
// currently execute this test which depends on being able to build
// new executable at runtime.
//
// (See the comment on canRunGoBuild's declaration for more information.)
t.Skip("can't run without building a new provider executable")
}
fixturePath := "testdata/provider-plugin" // Reused
// In temp dir create a plugin cache to be used in the test cases.
// The cache is supplied to commands using the -plugin-dir init flag.
// There are 4 providers total:
// - simple provider with versions 1.0.0 and 2.0.0 available
// - simple6 provider with versions 1.0.0 and 2.0.0 available
td := t.TempDir()
providerVersionOld := "1.0.0"
providerVersionNew := "2.0.0"
platform := getproviders.CurrentPlatform.String()
absolutePathToCache := filepath.Join(td, "cache")
simple5Provider := filepath.Join(td, "terraform-provider-simple")
simple5ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple/main", simple5Provider)
for _, v := range []string{providerVersionOld, providerVersionNew} {
dir := filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", v, platform)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
t.Fatal(err)
}
// Create an executable copy of the simple5ProviderExe file per version in the cache dir
data, err := os.ReadFile(simple5ProviderExe)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "terraform-provider-simple"), data, 0755); err != nil {
t.Fatal(err)
}
}
simple6Provider := filepath.Join(td, "terraform-provider-simple6")
simple6ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple-v6/main", simple6Provider)
for _, v := range []string{providerVersionOld, providerVersionNew} {
dir := filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple6", v, platform)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
t.Fatal(err)
}
// Create an executable copy of the simple6ProviderExe file per version in the cache dir
data, err := os.ReadFile(simple6ProviderExe)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "terraform-provider-simple6"), data, 0755); err != nil {
t.Fatal(err)
}
}
// Get hashes of 3 of the 4 providers
// These are used when creating or asserting against lock files.
var simple5v1_0_0Hash providerreqs.Hash
var simple6v1_0_0Hash providerreqs.Hash
var simple5v2_0_0Hash providerreqs.Hash
var err error
loc := getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", providerVersionOld, platform))
if simple5v1_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
loc = getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", providerVersionNew, platform))
if simple5v2_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
loc = getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple6", providerVersionOld, platform))
if simple6v1_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
// Set up a reused CLI configuration file that sets simple6 as a dev_override,
// Tests will use this via the TF_CLI_CONFIG_FILE environment variable.
cliCfg := fmt.Sprintf(`provider_installation {
dev_overrides {
"hashicorp/simple6" = "%s"
}
# For all other providers, install them directly from their origin provider
# registries as normal. If you omit this, Terraform will _only_ use
# the dev_overrides block, and so no other providers will be available.
direct {}
}
`, filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple6", providerVersionOld, platform))
cliConfigFilePath := filepath.Join(td, "dev_override.tfrc")
if err := os.WriteFile(cliConfigFilePath, []byte(cliCfg), 0644); err != nil {
t.Fatalf("err: %s", err)
}
t.Run("dev_override not installed during init when provider not present in dependency lock file", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// There is no lock file present at start
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
_, err := os.Stat(lockFile)
if err == nil {
t.Fatal("expected error due to file not existing, got no error")
}
if !os.IsNotExist(err) {
t.Fatalf("expected error due to file not existing, got different error: %s", err)
}
// The simple6 provider is a dev_override
tf.AddEnv("TF_CLI_CONFIG_FILE=" + cliConfigFilePath)
// The init process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-no-color",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache),
)
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Provider development overrides are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about overridden providers\nwant: %s\n%s", want, got)
}
// Lockfile is created
// simple provider is installed using the latest, 2.0.0 version,
// but the dev_override simple6 provider is not added to the lockfile.
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
expectedLockFileContent := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "2.0.0"
hashes = [
"%s",
]
}`,
simple5v2_0_0Hash,
)
if diff := cmp.Diff(expectedLockFileContent, string(buf)); diff != "" {
t.Errorf("unexpected difference in lock file content: %s", diff)
}
})
t.Run("dev_override providers are still represented in the dependency lock file after init", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// Lockfile contains both simple and simple6 providers already
priorLockFile := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "1.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v1_0_0Hash,
simple6v1_0_0Hash,
)
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
if err := os.WriteFile(lockFile, []byte(priorLockFile), 0644); err != nil {
t.Fatalf("error writing prior lock file: %s", err)
}
// The simple6 provider is a dev_override
tf.AddEnv("TF_CLI_CONFIG_FILE=" + cliConfigFilePath)
// The init process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-no-color",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache),
)
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Provider development overrides are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about overridden providers\nwant: %s\n%s", want, got)
}
// Lockfile is unchanged despite use of a dev_override simple6 provider
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
if diff := cmp.Diff(priorLockFile, string(buf)); diff != "" {
t.Fatalf("unexpected difference in lock file content: %s", diff)
}
})
t.Run("dev_override providers are unchanged in the dependency lock file during init -upgrade", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// Lockfile contains both simple and simple6 providers already
priorLockFile := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "1.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v1_0_0Hash,
simple6v1_0_0Hash,
)
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
if err := os.WriteFile(lockFile, []byte(priorLockFile), 0644); err != nil {
t.Fatalf("error writing prior lock file: %s", err)
}
// The simple6 provider is a dev_override
tf.AddEnv("TF_CLI_CONFIG_FILE=" + cliConfigFilePath)
// The init -upgrade process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-no-color",
"-upgrade",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache),
)
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Provider development overrides are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about overridden providers\nwant: %s\n%s", want, got)
}
// Lockfile shows evidence of upgrade process
// simple provider is upgraded to the newer 2.0.0 version,
// but the dev_override simple6 provider is removed from the lockfile.
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
expectedLockFileContent := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "2.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v2_0_0Hash,
simple6v1_0_0Hash, // not upgraded to 2.0.0
)
if diff := cmp.Diff(expectedLockFileContent, string(buf)); diff != "" {
t.Errorf("unexpected difference in lock file content: %s", diff)
}
})
}
// TestProviderInstall_unmanaged verifies provider plugin installation behaviour
// when an unmanaged provider is in use.
func TestProviderInstall_unmanaged(t *testing.T) {
if !canRunGoBuild {
// We're running in a separate-build-then-run context, so we can't
// currently execute this test which depends on being able to build
// new executable at runtime.
//
// (See the comment on canRunGoBuild's declaration for more information.)
t.Skip("can't run without building a new provider executable")
}
fixturePath := "testdata/provider-plugin" // Reused
// In temp dir create a plugin cache to be used in the test cases.
// The cache is supplied to commands using the -plugin-dir init flag.
// There are 4 providers total:
// - simple provider with versions 1.0.0 and 2.0.0 available
// - simple6 provider with versions 1.0.0 and 2.0.0 available
td := t.TempDir()
providerVersionOld := "1.0.0"
providerVersionNew := "2.0.0"
platform := getproviders.CurrentPlatform.String()
absolutePathToCache := filepath.Join(td, "cache")
simple5Provider := filepath.Join(td, "terraform-provider-simple")
simple5ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple/main", simple5Provider)
for _, v := range []string{providerVersionOld, providerVersionNew} {
dir := filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", v, platform)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
t.Fatal(err)
}
// Create an executable copy of the simple5ProviderExe file per version in the cache dir
data, err := os.ReadFile(simple5ProviderExe)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "terraform-provider-simple"), data, 0755); err != nil {
t.Fatal(err)
}
}
simple6Provider := filepath.Join(td, "terraform-provider-simple6")
simple6ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple-v6/main", simple6Provider)
for _, v := range []string{providerVersionOld, providerVersionNew} {
dir := filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple6", v, platform)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
t.Fatal(err)
}
// Create an executable copy of the simple6ProviderExe file per version in the cache dir
data, err := os.ReadFile(simple6ProviderExe)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "terraform-provider-simple6"), data, 0755); err != nil {
t.Fatal(err)
}
}
// Get hashes of 3 of the 4 providers
// These are used when creating or asserting against lock files.
var simple5v1_0_0Hash providerreqs.Hash
var simple6v1_0_0Hash providerreqs.Hash
var simple5v2_0_0Hash providerreqs.Hash
var err error
loc := getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", providerVersionOld, platform))
if simple5v1_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
loc = getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple", providerVersionNew, platform))
if simple5v2_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
loc = getproviders.PackageLocalDir(filepath.Join(absolutePathToCache, "registry.terraform.io/hashicorp", "simple6", providerVersionOld, platform))
if simple6v1_0_0Hash, err = getproviders.PackageHash(loc); err != nil {
t.Fatal(err)
}
// Launch a separate simple6 provider process to be re-used as an unmanaged provider.
// Tests will use this via the TF_REATTACH_PROVIDERS environment variable.
reattachConfig, _ := reattachedProviderForTest(t, addrs.NewDefaultProvider("simple6"), 6)
t.Run("unmanaged provider not installed when provider not present in dependency lock file", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// There is no lock file present at start
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
_, err := os.Stat(lockFile)
if err == nil {
t.Fatal("expected error due to file not existing, got no error")
}
if !os.IsNotExist(err) {
t.Fatalf("expected error due to file not existing, got different error: %s", err)
}
// The simple6 provider is unmanaged
tf.AddEnv("TF_REATTACH_PROVIDERS=" + reattachConfig)
// The init process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-no-color",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache))
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Unmanaged providers are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about unmanaged providers\nwant: %s\n%s", want, got)
}
// Lock file should have been created
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
// We expect the lock file to not contain the simple6 provider that's being unmanaged,
// because that provider is skipped during the installation process.
// The simple (v5) provider is installed as usual, pulling in the latest version.
expectedLockFileContent := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "2.0.0"
hashes = [
"%s",
]
}`, simple5v2_0_0Hash)
if diff := cmp.Diff(expectedLockFileContent, string(buf)); diff != "" {
t.Errorf("unexpected difference in lock file content: %s", diff)
}
})
t.Run("unmanaged providers are still represented in the dependency lock file after init", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// Lockfile contains both simple and simple6 providers already
priorLockFile := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "1.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v1_0_0Hash,
simple6v1_0_0Hash,
)
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
if err := os.WriteFile(lockFile, []byte(priorLockFile), 0644); err != nil {
t.Fatalf("error writing prior lock file: %s", err)
}
// The simple6 provider is unmanaged
tf.AddEnv("TF_REATTACH_PROVIDERS=" + reattachConfig)
// The init process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-no-color",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache))
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Unmanaged providers are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about unmanaged providers\nwant: %s\n%s", want, got)
}
// Lockfile is unchanged despite use of an unmanaged simple6 provider
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
if diff := cmp.Diff(priorLockFile, string(buf)); diff != "" {
t.Fatalf("unexpected difference in lock file content: %s", diff)
}
})
t.Run("unmanaged providers are unchanged in the dependency lock file during init -upgrade", func(t *testing.T) {
t.Parallel()
tf := e2e.NewBinary(t, terraformBin, fixturePath)
// Lockfile contains both simple and simple6 providers already at older version 1.0.0
priorLockFile := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "1.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v1_0_0Hash,
simple6v1_0_0Hash,
)
lockFile := filepath.Join(tf.WorkDir(), ".terraform.lock.hcl")
if err := os.WriteFile(lockFile, []byte(priorLockFile), 0644); err != nil {
t.Fatalf("error writing prior lock file: %s", err)
}
// The simple6 provider is unmanaged
tf.AddEnv("TF_REATTACH_PROVIDERS=" + reattachConfig)
// The init -upgrade process should succeed.
stdout, stderr, err := tf.Run(
"init",
"-upgrade",
"-no-color",
fmt.Sprintf("-plugin-dir=%s", absolutePathToCache))
if err != nil {
t.Fatalf("unexpected error: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if got, want := stdout, "Warning: Unmanaged providers are in effect"; !strings.Contains(got, want) {
t.Fatalf("stdout doesn't include the warning about unmanaged providers\nwant: %s\n%s", want, got)
}
// Lockfile shows evidence of upgrade process
// simple provider is upgraded to the newer 2.0.0 version,
// but the unmanaged simple6 provider is unchanged due to being unmanaged.
buf, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("unexpected error accessing lock file: %s", err)
}
buf = bytes.TrimSpace(buf)
expectedLockFileContent := fmt.Sprintf(`# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/simple" {
version = "2.0.0"
hashes = [
"%s",
]
}
provider "registry.terraform.io/hashicorp/simple6" {
version = "1.0.0"
hashes = [
"%s",
]
}`,
simple5v2_0_0Hash,
simple6v1_0_0Hash, // not upgraded to 2.0.0
)
if diff := cmp.Diff(expectedLockFileContent, string(buf)); diff != "" {
t.Errorf("unexpected difference in lock file content: %s", diff)
}
})
}
// reattachConfigForTest launches a provider process and returns a reattach config string
// that can be used as the value for the TF_REATTACH_PROVIDERS environment variable in tests.
// Cleanup of the provider process is handled internally.
func reattachedProviderForTest(t *testing.T, provider addrs.Provider, protocol int) (string, *providerServer) {
t.Helper()
if !slices.Contains([]int{5, 6}, protocol) {
t.Fatalf("test setup tried to create a provider using protocol version %d, which is unsupported. Choose between 5 and 6.", protocol)
}
reattachCh := make(chan *plugin.ReattachConfig)
closeCh := make(chan struct{})
server := &providerServer{
ProviderServer: grpcwrap.Provider6(simple.Provider()),
}
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(func() {
cancel()
<-closeCh
})
go plugin.Serve(&plugin.ServeConfig{
Logger: hclog.New(&hclog.LoggerOptions{
Name: "plugintest",
Level: hclog.Trace,
Output: io.Discard,
}),
Test: &plugin.ServeTestConfig{
Context: ctx,
ReattachConfigCh: reattachCh,
CloseCh: closeCh,
},
GRPCServer: plugin.DefaultGRPCServer,
VersionedPlugins: map[int]plugin.PluginSet{
6: {
"provider": &tfplugin.GRPCProviderPlugin{
GRPCProvider: func() proto.ProviderServer {
return server
},
},
},
},
})
config := <-reattachCh
if config == nil {
t.Fatalf("no reattach config received")
}
reattachStr, err := json.Marshal(map[string]reattachConfig{
provider.String(): {
Protocol: string(config.Protocol),
ProtocolVersion: 6,
Pid: config.Pid,
Test: true,
Addr: reattachConfigAddr{
Network: config.Addr.Network(),
String: config.Addr.String(),
},
},
})
if err != nil {
t.Fatal(err)
}
return string(reattachStr), server
}