address PR feedback

This commit is contained in:
Radek Simko 2026-02-03 17:53:33 +00:00
parent f0fbe0f656
commit 8e1d04a388
No known key found for this signature in database
GPG key ID: 1F1C84FE689A88D7
2 changed files with 181 additions and 7 deletions

View file

@ -4788,7 +4788,6 @@ func TestInit_backend_to_stateStore_singleWorkspace(t *testing.T) {
args := []string{
"-enable-pluggable-state-storage-experiment=true",
"-force-copy",
"-migrate-state",
}
code := c.Run(args)
testOutput := done(t)
@ -4835,6 +4834,131 @@ func TestInit_backend_to_stateStore_singleWorkspace(t *testing.T) {
}
}
func TestInit_backend_to_stateStore_noState(t *testing.T) {
// Create a temporary working directory that is empty
td := t.TempDir()
testBackend := new(httpBackend.TestHTTPBackend)
ts := httptest.NewServer(http.HandlerFunc(testBackend.Handle))
t.Cleanup(ts.Close)
cfg := fmt.Sprintf(`terraform {
backend "http" {
address = %q
}
}
`, ts.URL)
if err := os.WriteFile(filepath.Join(td, "main.tf"), []byte(cfg), 0644); err != nil {
t.Fatalf("err: %s", err)
}
t.Chdir(td)
mockProvider := mockPluggableStateStorageProvider()
mockProviderAddress := addrs.NewDefaultProvider("test")
providerSource, close := newMockProviderSource(t, map[string][]string{
"hashicorp/test": {"1.2.3"},
})
defer close()
tOverrides := &testingOverrides{
Providers: map[addrs.Provider]providers.Factory{
mockProviderAddress: providers.FactoryFixed(mockProvider),
},
}
{
log.Printf("[TRACE] %s: beginning first init with backend", t.Name())
// Init
ui := cli.NewMockUi()
view, done := testView(t)
c := &InitCommand{
Meta: Meta{
Ui: ui,
View: view,
AllowExperimentalFeatures: true,
},
}
args := []string{
"-enable-pluggable-state-storage-experiment=true",
}
code := c.Run(args)
testOutput := done(t)
if code != 0 {
t.Fatalf("first init exited with non-zero code %d:\n%s", code, testOutput.Stderr())
}
log.Printf("[TRACE] %s: first init complete", t.Name())
t.Logf("First run output:\n%s", testOutput.Stdout())
t.Logf("First run errors:\n%s", testOutput.Stderr())
if testBackend.CallCount("POST") != 0 {
t.Fatalf("expected 0 POST calls after init, got %d", testBackend.CallCount("POST"))
}
if testBackend.CallCount("GET") != 2 {
t.Fatalf("expected 2 GET calls after init, got %d", testBackend.CallCount("GET"))
}
}
{
log.Printf("[TRACE] %s: beginning second init with state store", t.Name())
ssCfg := `terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
state_store "test_store" {
provider "test" {}
value = "foobar"
}
}
`
if err := os.WriteFile(filepath.Join(td, "main.tf"), []byte(ssCfg), 0644); err != nil {
t.Fatalf("err: %s", err)
}
ui := cli.NewMockUi()
view, done := testView(t)
c := &InitCommand{
Meta: Meta{
testingOverrides: tOverrides,
ProviderSource: providerSource,
Ui: ui,
View: view,
AllowExperimentalFeatures: true,
},
}
args := []string{
"-enable-pluggable-state-storage-experiment=true",
"-force-copy",
// assume default -create-default-workspace=true
}
code := c.Run(args)
testOutput := done(t)
if code != 0 {
t.Fatalf("second init exited with non-zero code %d:\n%s", code, testOutput.Stderr())
}
log.Printf("[TRACE] %s: second init with state store complete", t.Name())
t.Logf("Second run output:\n%s", testOutput.Stdout())
t.Logf("Second run errors:\n%s", testOutput.Stderr())
s := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
if s.StateStore.Empty() {
t.Fatal("should have StateStore config")
}
if !s.Backend.Empty() {
t.Fatalf("expected backend to be empty")
}
_, ok := mockProvider.MockStates[backend.DefaultStateName].([]byte)
if !ok {
t.Fatalf("expected %q state to exist in %s: %#v",
backend.DefaultStateName,
mockProviderAddress,
mockProvider.MockStates)
}
}
}
func TestInit_backend_to_stateStore_multipleWorkspaces(t *testing.T) {
// Create a temporary working directory that is empty
td := t.TempDir()

View file

@ -1107,8 +1107,9 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
)
if !opts.Init {
initReason := fmt.Sprintf("Migrating from backend %q to state store %q",
s.Backend.Type, stateStoreConfig.Type)
initReason := fmt.Sprintf("Migrating from backend %q to state store %q in provider %s (%q)",
s.Backend.Type, stateStoreConfig.Type,
stateStoreConfig.Provider.Name, stateStoreConfig.ProviderAddr)
diags = diags.Append(errBackendInitDiag(initReason))
return nil, diags
}
@ -1959,9 +1960,6 @@ func (m *Meta) backend_to_stateStore(bcs *workdir.BackendConfigState, sMgr *clis
return nil, diags
}
// Update the stored metadata
s.Backend = nil
if m.stateLock {
view := views.NewStateLocker(vt, m.View)
stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
@ -1975,7 +1973,7 @@ func (m *Meta) backend_to_stateStore(bcs *workdir.BackendConfigState, sMgr *clis
// Store the state_store metadata in our saved state location
var pVersion *version.Version // This will remain nil for builtin providers or unmanaged providers.
if c.ProviderAddr.Hostname == addrs.BuiltInProviderHost {
if c.ProviderAddr.IsBuiltIn() {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "State storage is using a builtin provider",
@ -2005,6 +2003,7 @@ func (m *Meta) backend_to_stateStore(bcs *workdir.BackendConfigState, sMgr *clis
}
}
// Update the stored metadata
s.Backend = nil
s.StateStore = &workdir.StateStoreConfigState{
Type: c.Type,
@ -2030,6 +2029,57 @@ func (m *Meta) backend_to_stateStore(bcs *workdir.BackendConfigState, sMgr *clis
return nil, diags
}
// Verify that selected workspace exists in the state store.
if opts.Init {
err := m.selectWorkspace(ssBackend)
if err != nil {
if errors.Is(err, &errBackendNoExistingWorkspaces{}) {
// If there are no workspaces, Terraform either needs to create the default workspace here
// or instruct the user to run a `terraform workspace new` command.
ws, err := m.Workspace()
if err != nil {
diags = diags.Append(fmt.Errorf("Failed to check current workspace: %w", err))
return nil, diags
}
if ws == backend.DefaultStateName {
// Users control if the default workspace is created through the -create-default-workspace flag (defaults to true)
if opts.CreateDefaultWorkspace {
diags = diags.Append(m.createDefaultWorkspace(c, ssBackend))
if !diags.HasErrors() {
// Report workspace creation to the view
view := views.NewInit(vt, m.View)
view.Output(views.DefaultWorkspaceCreatedMessage)
}
} else {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "The default workspace does not exist",
Detail: "Terraform has been configured to skip creation of the default workspace in the state store. To create it, either remove the `-create-default-workspace=false` flag and re-run the 'init' command, or create it using a 'workspace new' command",
})
}
} else {
// User needs to run a `terraform workspace new` command to create the missing custom workspace.
diags = append(diags, tfdiags.Sourceless(
tfdiags.Error,
fmt.Sprintf("Workspace %q has not been created yet", ws),
fmt.Sprintf("State store %q in provider %s (%q) reports that no workspaces currently exist. To create the custom workspace %q use the command `terraform workspace new %s`.",
c.Type,
c.Provider.Name,
c.ProviderAddr,
ws,
ws,
),
))
return nil, diags
}
} else {
// For all other errors, report via diagnostics
diags = diags.Append(fmt.Errorf("Failed to select a workspace: %w", err))
}
}
}
// Update backend state file
if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(errBackendWriteSavedDiag(err))