From 00cc1ea26dee6f2b5d9ac44de1b4aca178489773 Mon Sep 17 00:00:00 2001 From: Megan Bang Date: Mon, 29 Aug 2022 11:10:03 -0500 Subject: [PATCH] refactor getSchemas --- internal/command/command_test.go | 56 ---------------------- internal/command/import.go | 9 ++-- internal/command/meta.go | 42 ++++++++++++++++ internal/command/meta_backend_migrate.go | 20 ++------ internal/command/state_mv.go | 23 +++------ internal/command/state_push.go | 22 +++------ internal/command/state_replace_provider.go | 23 +++------ internal/command/state_rm.go | 23 +++------ internal/command/taint.go | 21 ++------ internal/command/untaint.go | 21 ++------ 10 files changed, 87 insertions(+), 173 deletions(-) diff --git a/internal/command/command_test.go b/internal/command/command_test.go index df02a9166c..43c4640822 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -766,62 +766,6 @@ func testBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *htt return state, srv } -// testCloudBackendState is used to make a cloud configured -// backend. -// -// When using this function, the configuration fixture for the test must -// include an empty configuration block for the HTTP backend, like this: -// -// terraform { -// cloud { -// } -// } -// -// If such a block isn't present, then an error will -// be returned about the backend configuration having changed and that -// "terraform init" must be run, since the test backend config cache created -// by this function contains the hash for an empty configuration. -func testCloudBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *httptest.Server) { - t.Helper() - - var b64md5 string - buf := bytes.NewBuffer(nil) - - cb := func(resp http.ResponseWriter, req *http.Request) { - if req.Method == "PUT" { - resp.WriteHeader(c) - return - } - if s == nil { - resp.WriteHeader(404) - return - } - - resp.Header().Set("Content-MD5", b64md5) - resp.Write(buf.Bytes()) - } - - // If a state was given, make sure we calculate the proper b64md5 - if s != nil { - err := statefile.Write(&statefile.File{State: s}, buf) - if err != nil { - t.Fatalf("err: %v", err) - } - md5 := md5.Sum(buf.Bytes()) - b64md5 = base64.StdEncoding.EncodeToString(md5[:16]) - } - - srv := httptest.NewServer(http.HandlerFunc(cb)) - - state := legacy.NewState() - state.Backend = &legacy.BackendState{ - Type: "cloud", - ConfigRaw: json.RawMessage(fmt.Sprintf(`{"address":%q}`, srv.URL)), - } - - return state, srv -} - // testRemoteState is used to make a test HTTP server to return a given // state file that can be used for testing legacy remote state. // diff --git a/internal/command/import.go b/internal/command/import.go index a7e829882d..08aa7f6ff0 100644 --- a/internal/command/import.go +++ b/internal/command/import.go @@ -249,9 +249,12 @@ func (c *ImportCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas, diags := getSchemas(&c.Meta, newState, config) - if diags.HasErrors() && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(newState) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) + } } // Persist the final state diff --git a/internal/command/meta.go b/internal/command/meta.go index 594292f1b9..8139a63262 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -6,6 +6,8 @@ import ( "errors" "flag" "fmt" + "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/states" "io/ioutil" "log" "os" @@ -779,3 +781,43 @@ func (m *Meta) checkRequiredVersion() tfdiags.Diagnostics { return nil } + +// GetSchemas loads and returns the schemas +func (c *Meta) GetSchemas(state *states.State) (*terraform.Schemas, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + var config *configs.Config + + path, err := os.Getwd() + if err != nil { + diags.Append(err) + return nil, diags + } + + config, diags = c.loadConfig(path) + if diags.HasErrors() { + diags.Append(diags) + return nil, diags + } + + if config != nil || state != nil { + opts, err := c.contextOpts() + if err != nil { + diags = diags.Append(err) + return nil, diags + } + tfCtx, ctxDiags := terraform.NewContext(opts) + diags = diags.Append(ctxDiags) + if ctxDiags.HasErrors() { + return nil, diags + } + var schemaDiags tfdiags.Diagnostics + schemas, schemaDiags := tfCtx.Schemas(config, state) + diags = diags.Append(schemaDiags) + if schemaDiags.HasErrors() { + return nil, diags + } + return schemas, diags + + } + return nil, diags +} diff --git a/internal/command/meta_backend_migrate.go b/internal/command/meta_backend_migrate.go index e0a81ba390..ba0fbb5525 100644 --- a/internal/command/meta_backend_migrate.go +++ b/internal/command/meta_backend_migrate.go @@ -435,26 +435,14 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error { // both managers support such metadata. log.Print("[TRACE] backendMigrateState: migration confirmed, so migrating") - path, err := os.Getwd() - if err != nil { - return fmt.Errorf("could not get working directory") - } - - config, diags := m.loadConfig(path) - if diags.HasErrors() { - return diags.Err() - } - - schemas, diags := getSchemas(m, destination, config) - if diags.HasErrors() { - return diags.Err() - } - if err := statemgr.Migrate(destinationState, sourceState); err != nil { return fmt.Errorf(strings.TrimSpace(errBackendStateCopy), opts.SourceType, opts.DestinationType, err) } - if err := destinationState.PersistState(schemas); err != nil { + // Fetching schemas during init might be more of a hassle than we want to attempt + // in the case that we're migrating to TFC backend, the initial JSON state won't + // be generated and stored. + if err := destinationState.PersistState(nil); err != nil { return fmt.Errorf(strings.TrimSpace(errBackendStateCopy), opts.SourceType, opts.DestinationType, err) } diff --git a/internal/command/state_mv.go b/internal/command/state_mv.go index 93b8117ca3..b00eb2d8ba 100644 --- a/internal/command/state_mv.go +++ b/internal/command/state_mv.go @@ -3,7 +3,6 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" - "os" "strings" "github.com/hashicorp/terraform/internal/addrs" @@ -387,7 +386,7 @@ func (c *StateMvCommand) Run(args []string) int { return 0 // This is as far as we go in dry-run mode } - b, backendDiags := c.Backend(&BackendOpts{}) + b, backendDiags := c.Backend(nil) diags = diags.Append(backendDiags) if backendDiags.HasErrors() { c.showDiagnostics(diags) @@ -395,24 +394,14 @@ func (c *StateMvCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, stateTo, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(stateTo) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - // Write the new state if err := stateToMgr.WriteState(stateTo); err != nil { c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) diff --git a/internal/command/state_push.go b/internal/command/state_push.go index 95dc0fb810..8adf612f40 100644 --- a/internal/command/state_push.go +++ b/internal/command/state_push.go @@ -3,6 +3,7 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" + "github.com/hashicorp/terraform/internal/tfdiags" "io" "os" "strings" @@ -129,24 +130,15 @@ func (c *StatePushCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, srcStateFile.State, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + var diags tfdiags.Diagnostics + schemas, diags = c.GetSchemas(srcStateFile.State) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - if err := stateMgr.WriteState(srcStateFile.State); err != nil { c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err)) return 1 diff --git a/internal/command/state_replace_provider.go b/internal/command/state_replace_provider.go index 684e8d3409..ec7062af8c 100644 --- a/internal/command/state_replace_provider.go +++ b/internal/command/state_replace_provider.go @@ -3,7 +3,6 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" - "os" "strings" "github.com/hashicorp/terraform/internal/addrs" @@ -162,7 +161,7 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { resource.ProviderConfig.Provider = to } - b, backendDiags := c.Backend(&BackendOpts{}) + b, backendDiags := c.Backend(nil) diags = diags.Append(backendDiags) if backendDiags.HasErrors() { c.showDiagnostics(diags) @@ -170,24 +169,14 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, state, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(state) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - // Write the updated state if err := stateMgr.WriteState(state); err != nil { c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) diff --git a/internal/command/state_rm.go b/internal/command/state_rm.go index 0344d51ce5..f7c9a937db 100644 --- a/internal/command/state_rm.go +++ b/internal/command/state_rm.go @@ -3,7 +3,6 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" - "os" "strings" "github.com/hashicorp/terraform/internal/addrs" @@ -112,7 +111,7 @@ func (c *StateRmCommand) Run(args []string) int { return 0 // This is as far as we go in dry-run mode } - b, backendDiags := c.Backend(&BackendOpts{}) + b, backendDiags := c.Backend(nil) diags = diags.Append(backendDiags) if backendDiags.HasErrors() { c.showDiagnostics(diags) @@ -120,24 +119,14 @@ func (c *StateRmCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, state, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(state) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - if err := stateMgr.WriteState(state); err != nil { c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) return 1 diff --git a/internal/command/taint.go b/internal/command/taint.go index d2710d6b7f..547a0038e1 100644 --- a/internal/command/taint.go +++ b/internal/command/taint.go @@ -3,7 +3,6 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" - "os" "strings" "github.com/hashicorp/terraform/internal/addrs" @@ -128,24 +127,14 @@ func (c *TaintCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, state, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(state) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - ss := state.SyncWrapper() // Get the resource and instance we're going to taint diff --git a/internal/command/untaint.go b/internal/command/untaint.go index d66b6d1bce..ebfa26d69c 100644 --- a/internal/command/untaint.go +++ b/internal/command/untaint.go @@ -3,7 +3,6 @@ package command import ( "fmt" "github.com/hashicorp/terraform/internal/terraform" - "os" "strings" "github.com/hashicorp/terraform/internal/addrs" @@ -167,24 +166,14 @@ func (c *UntaintCommand) Run(args []string) int { } // Get schemas, if possible, before writing state - schemas := &terraform.Schemas{} - path, err := os.Getwd() - schemaErr := err != nil - - if !schemaErr { - config, diags := c.loadConfig(path) - schemaErr = diags.HasErrors() - - if !schemaErr { - schemas, diags = getSchemas(&c.Meta, state, config) - schemaErr = diags.HasErrors() + var schemas *terraform.Schemas + if isCloudMode(b) { + schemas, diags = c.GetSchemas(state) + if diags.HasErrors() { + c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } } - if schemaErr && isCloudMode(b) { - c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) - } - obj.Status = states.ObjectReady ss.SetResourceInstanceCurrent(addr, obj, rs.ProviderConfig)