Skip to content

Commit d884031

Browse files
authored
PSS: Implement unsetting of a state store (#37875)
* Remove BackendOpts.ProviderFactory abstraction layer * PSS: Implement unsetting of a state store * address PR feedback
1 parent 4420c4d commit d884031

File tree

11 files changed

+508
-248
lines changed

11 files changed

+508
-248
lines changed

internal/command/e2etest/meta_backend_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func TestMetaBackend_GetStateStoreProviderFactory(t *testing.T) {
6767

6868
// Setup the meta and test GetStateStoreProviderFactory
6969
m := command.Meta{}
70-
factory, diags := m.GetStateStoreProviderFactory(config, locks)
70+
factory, diags := m.StateStoreProviderFactoryFromConfig(config, locks)
7171
if diags.HasErrors() {
7272
t.Fatalf("unexpected error : %s", err)
7373
}

internal/command/init_run_experiment.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func (c *InitCommand) initPssBackend(ctx context.Context, root *configs.Module,
378378
return nil, true, diags
379379
case root.StateStore != nil:
380380
// state_store config present
381-
factory, fDiags := c.Meta.GetStateStoreProviderFactory(root.StateStore, configLocks)
381+
factory, fDiags := c.Meta.StateStoreProviderFactoryFromConfig(root.StateStore, configLocks)
382382
diags = diags.Append(fDiags)
383383
if fDiags.HasErrors() {
384384
return nil, true, diags
@@ -439,7 +439,6 @@ func (c *InitCommand) initPssBackend(ctx context.Context, root *configs.Module,
439439
opts = &BackendOpts{
440440
StateStoreConfig: root.StateStore,
441441
Locks: configLocks,
442-
ProviderFactory: factory,
443442
CreateDefaultWorkspace: initArgs.CreateDefaultWorkspace,
444443
ConfigOverride: configOverride,
445444
Init: true,
@@ -492,6 +491,7 @@ func (c *InitCommand) initPssBackend(ctx context.Context, root *configs.Module,
492491

493492
opts = &BackendOpts{
494493
BackendConfig: backendConfig,
494+
Locks: configLocks,
495495
ConfigOverride: configOverride,
496496
Init: true,
497497
ViewType: initArgs.ViewType,
@@ -526,6 +526,7 @@ the backend configuration is present and valid.
526526

527527
opts = &BackendOpts{
528528
Init: true,
529+
Locks: configLocks,
529530
ViewType: initArgs.ViewType,
530531
}
531532
}

internal/command/init_test.go

Lines changed: 202 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8-
"io/ioutil"
98
"log"
109
"os"
1110
"path/filepath"
@@ -524,7 +523,7 @@ func TestInit_backendUnset(t *testing.T) {
524523
log.Printf("[TRACE] TestInit_backendUnset: beginning second init")
525524

526525
// Unset
527-
if err := ioutil.WriteFile("main.tf", []byte(""), 0644); err != nil {
526+
if err := os.WriteFile("main.tf", []byte(""), 0644); err != nil {
528527
t.Fatalf("err: %s", err)
529528
}
530529

@@ -1144,7 +1143,7 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) {
11441143

11451144
// init again but remove the path option from the config
11461145
cfg := "terraform {\n backend \"local\" {}\n}\n"
1147-
if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
1146+
if err := os.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
11481147
t.Fatal(err)
11491148
}
11501149

@@ -2359,7 +2358,7 @@ func TestInit_providerLockFile(t *testing.T) {
23592358
}
23602359

23612360
lockFile := ".terraform.lock.hcl"
2362-
buf, err := ioutil.ReadFile(lockFile)
2361+
buf, err := os.ReadFile(lockFile)
23632362
if err != nil {
23642363
t.Fatalf("failed to read dependency lock file %s: %s", lockFile, err)
23652364
}
@@ -2540,7 +2539,7 @@ provider "registry.terraform.io/hashicorp/test" {
25402539

25412540
// write input lockfile
25422541
lockFile := ".terraform.lock.hcl"
2543-
if err := ioutil.WriteFile(lockFile, []byte(tc.input), 0644); err != nil {
2542+
if err := os.WriteFile(lockFile, []byte(tc.input), 0644); err != nil {
25442543
t.Fatalf("failed to write input lockfile: %s", err)
25452544
}
25462545

@@ -2552,7 +2551,7 @@ provider "registry.terraform.io/hashicorp/test" {
25522551
t.Fatalf("expected error, got output: \n%s", done(t).Stdout())
25532552
}
25542553

2555-
buf, err := ioutil.ReadFile(lockFile)
2554+
buf, err := os.ReadFile(lockFile)
25562555
if err != nil {
25572556
t.Fatalf("failed to read dependency lock file %s: %s", lockFile, err)
25582557
}
@@ -4018,6 +4017,203 @@ func TestInit_stateStore_providerUpgrade(t *testing.T) {
40184017
})
40194018
}
40204019

4020+
func TestInit_stateStore_unset(t *testing.T) {
4021+
// Create a temporary working directory that is empty
4022+
td := t.TempDir()
4023+
testCopyDir(t, testFixturePath("init-state-store"), td)
4024+
t.Chdir(td)
4025+
4026+
mockProvider := mockPluggableStateStorageProvider()
4027+
storeName := "test_store"
4028+
otherStoreName := "test_otherstore"
4029+
// Make the provider report that it contains a 2nd storage implementation with the above name
4030+
mockProvider.GetProviderSchemaResponse.StateStores[otherStoreName] = mockProvider.GetProviderSchemaResponse.StateStores[storeName]
4031+
mockProviderAddress := addrs.NewDefaultProvider("test")
4032+
providerSource, close := newMockProviderSource(t, map[string][]string{
4033+
"hashicorp/test": {"1.2.3"}, // Matches provider version in backend state file fixture
4034+
})
4035+
defer close()
4036+
4037+
{
4038+
log.Printf("[TRACE] TestInit_stateStore_unset: beginning first init")
4039+
4040+
ui := cli.NewMockUi()
4041+
view, done := testView(t)
4042+
c := &InitCommand{
4043+
Meta: Meta{
4044+
testingOverrides: &testingOverrides{
4045+
Providers: map[addrs.Provider]providers.Factory{
4046+
mockProviderAddress: providers.FactoryFixed(mockProvider),
4047+
},
4048+
},
4049+
ProviderSource: providerSource,
4050+
Ui: ui,
4051+
View: view,
4052+
AllowExperimentalFeatures: true,
4053+
},
4054+
}
4055+
4056+
// Init
4057+
args := []string{
4058+
"-enable-pluggable-state-storage-experiment=true",
4059+
}
4060+
code := c.Run(args)
4061+
testOutput := done(t)
4062+
if code != 0 {
4063+
t.Fatalf("bad: \n%s", testOutput.All())
4064+
}
4065+
log.Printf("[TRACE] TestInit_stateStore_unset: first init complete")
4066+
t.Logf("First run output:\n%s", testOutput.Stdout())
4067+
t.Logf("First run errors:\n%s", testOutput.Stderr())
4068+
4069+
if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
4070+
t.Fatalf("err: %s", err)
4071+
}
4072+
}
4073+
4074+
{
4075+
log.Printf("[TRACE] TestInit_stateStore_unset: beginning second init")
4076+
4077+
// Unset
4078+
if err := os.WriteFile("main.tf", []byte(""), 0644); err != nil {
4079+
t.Fatalf("err: %s", err)
4080+
}
4081+
4082+
ui := cli.NewMockUi()
4083+
view, done := testView(t)
4084+
c := &InitCommand{
4085+
Meta: Meta{
4086+
testingOverrides: &testingOverrides{
4087+
Providers: map[addrs.Provider]providers.Factory{
4088+
mockProviderAddress: providers.FactoryFixed(mockProvider),
4089+
},
4090+
},
4091+
ProviderSource: providerSource,
4092+
Ui: ui,
4093+
View: view,
4094+
AllowExperimentalFeatures: true,
4095+
},
4096+
}
4097+
4098+
args := []string{
4099+
"-enable-pluggable-state-storage-experiment=true",
4100+
"-force-copy",
4101+
}
4102+
code := c.Run(args)
4103+
testOutput := done(t)
4104+
if code != 0 {
4105+
t.Fatalf("bad: \n%s", testOutput.All())
4106+
}
4107+
log.Printf("[TRACE] TestInit_stateStore_unset: second init complete")
4108+
t.Logf("Second run output:\n%s", testOutput.Stdout())
4109+
t.Logf("Second run errors:\n%s", testOutput.Stderr())
4110+
4111+
s := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
4112+
if !s.StateStore.Empty() {
4113+
t.Fatal("should not have StateStore config")
4114+
}
4115+
}
4116+
}
4117+
4118+
func TestInit_stateStore_unset_withoutProviderRequirements(t *testing.T) {
4119+
// Create a temporary working directory that is empty
4120+
td := t.TempDir()
4121+
testCopyDir(t, testFixturePath("init-state-store"), td)
4122+
t.Chdir(td)
4123+
4124+
mockProvider := mockPluggableStateStorageProvider()
4125+
storeName := "test_store"
4126+
otherStoreName := "test_otherstore"
4127+
// Make the provider report that it contains a 2nd storage implementation with the above name
4128+
mockProvider.GetProviderSchemaResponse.StateStores[otherStoreName] = mockProvider.GetProviderSchemaResponse.StateStores[storeName]
4129+
mockProviderAddress := addrs.NewDefaultProvider("test")
4130+
providerSource, close := newMockProviderSource(t, map[string][]string{
4131+
"hashicorp/test": {"1.2.3"}, // Matches provider version in backend state file fixture
4132+
})
4133+
defer close()
4134+
4135+
{
4136+
log.Printf("[TRACE] TestInit_stateStore_unset_withoutProviderRequirements: beginning first init")
4137+
4138+
ui := cli.NewMockUi()
4139+
view, done := testView(t)
4140+
c := &InitCommand{
4141+
Meta: Meta{
4142+
testingOverrides: &testingOverrides{
4143+
Providers: map[addrs.Provider]providers.Factory{
4144+
mockProviderAddress: providers.FactoryFixed(mockProvider),
4145+
},
4146+
},
4147+
ProviderSource: providerSource,
4148+
Ui: ui,
4149+
View: view,
4150+
AllowExperimentalFeatures: true,
4151+
},
4152+
}
4153+
4154+
// Init
4155+
args := []string{
4156+
"-enable-pluggable-state-storage-experiment=true",
4157+
}
4158+
code := c.Run(args)
4159+
testOutput := done(t)
4160+
if code != 0 {
4161+
t.Fatalf("bad: \n%s", testOutput.All())
4162+
}
4163+
log.Printf("[TRACE] TestInit_stateStore_unset_withoutProviderRequirements: first init complete")
4164+
t.Logf("First run output:\n%s", testOutput.Stdout())
4165+
t.Logf("First run errors:\n%s", testOutput.Stderr())
4166+
4167+
if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
4168+
t.Fatalf("err: %s", err)
4169+
}
4170+
}
4171+
{
4172+
log.Printf("[TRACE] TestInit_stateStore_unset_withoutProviderRequirements: beginning second init")
4173+
// Unset state store and provider requirements
4174+
if err := os.WriteFile("main.tf", []byte(""), 0644); err != nil {
4175+
t.Fatalf("err: %s", err)
4176+
}
4177+
if err := os.WriteFile("providers.tf", []byte(""), 0644); err != nil {
4178+
t.Fatalf("err: %s", err)
4179+
}
4180+
4181+
ui := cli.NewMockUi()
4182+
view, done := testView(t)
4183+
c := &InitCommand{
4184+
Meta: Meta{
4185+
testingOverrides: &testingOverrides{
4186+
Providers: map[addrs.Provider]providers.Factory{
4187+
mockProviderAddress: providers.FactoryFixed(mockProvider),
4188+
},
4189+
},
4190+
ProviderSource: providerSource,
4191+
Ui: ui,
4192+
View: view,
4193+
AllowExperimentalFeatures: true,
4194+
},
4195+
}
4196+
4197+
args := []string{
4198+
"-enable-pluggable-state-storage-experiment=true",
4199+
"-force-copy",
4200+
}
4201+
code := c.Run(args)
4202+
testOutput := done(t)
4203+
if code != 0 {
4204+
t.Fatalf("bad: \n%s", testOutput.All())
4205+
}
4206+
log.Printf("[TRACE] TestInit_stateStore_unset_withoutProviderRequirements: second init complete")
4207+
t.Logf("Second run output:\n%s", testOutput.Stdout())
4208+
t.Logf("Second run errors:\n%s", testOutput.Stderr())
4209+
4210+
s := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
4211+
if !s.StateStore.Empty() {
4212+
t.Fatal("should not have StateStore config")
4213+
}
4214+
}
4215+
}
4216+
40214217
// newMockProviderSource is a helper to succinctly construct a mock provider
40224218
// source that contains a set of packages matching the given provider versions
40234219
// that are available for installation (from temporary local files).

0 commit comments

Comments
 (0)