From 12e3560d21e1aeefc429b5bff74ded7cc551faf4 Mon Sep 17 00:00:00 2001 From: Megan Bang Date: Fri, 26 Aug 2022 15:32:18 -0500 Subject: [PATCH] check for cloud integration mode --- internal/command/command_test.go | 56 ++++++++++++++++++++++ internal/command/helper.go | 8 ++++ internal/command/import.go | 2 +- internal/command/state_mv.go | 13 +++-- internal/command/state_push.go | 6 +-- internal/command/state_replace_provider.go | 13 +++-- internal/command/state_rm.go | 13 +++-- internal/command/taint.go | 6 +-- internal/command/untaint.go | 6 +-- 9 files changed, 104 insertions(+), 19 deletions(-) diff --git a/internal/command/command_test.go b/internal/command/command_test.go index 43c4640822..df02a9166c 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -766,6 +766,62 @@ 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/helper.go b/internal/command/helper.go index ef13934cc0..46f3863354 100644 --- a/internal/command/helper.go +++ b/internal/command/helper.go @@ -1,6 +1,8 @@ package command import ( + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/cloud" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/terraform" @@ -17,6 +19,12 @@ however, historic state information may be missing if the affected integration r %s ` +func isCloudMode(b backend.Enhanced) bool { + _, ok := b.(*cloud.Cloud) + + return ok +} + func getSchemas(c *Meta, state *states.State, config *configs.Config) (*terraform.Schemas, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics diff --git a/internal/command/import.go b/internal/command/import.go index 8d98fb74c8..a7e829882d 100644 --- a/internal/command/import.go +++ b/internal/command/import.go @@ -250,7 +250,7 @@ func (c *ImportCommand) Run(args []string) int { // Get schemas, if possible, before writing state schemas, diags := getSchemas(&c.Meta, newState, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } diff --git a/internal/command/state_mv.go b/internal/command/state_mv.go index 231f964996..d057834b59 100644 --- a/internal/command/state_mv.go +++ b/internal/command/state_mv.go @@ -386,18 +386,25 @@ 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{}) + diags = diags.Append(backendDiags) + if backendDiags.HasErrors() { + c.showDiagnostics(diags) + return 1 + } + path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, stateTo, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) return 1 } diff --git a/internal/command/state_push.go b/internal/command/state_push.go index fd3f55a248..1d9dfcc52b 100644 --- a/internal/command/state_push.go +++ b/internal/command/state_push.go @@ -129,17 +129,17 @@ func (c *StatePushCommand) Run(args []string) int { // Get schemas, if possible, before writing state path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, srcStateFile.State, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } diff --git a/internal/command/state_replace_provider.go b/internal/command/state_replace_provider.go index 4b08439e23..19a1f8df77 100644 --- a/internal/command/state_replace_provider.go +++ b/internal/command/state_replace_provider.go @@ -161,19 +161,26 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { resource.ProviderConfig.Provider = to } + b, backendDiags := c.Backend(&BackendOpts{}) + diags = diags.Append(backendDiags) + if backendDiags.HasErrors() { + c.showDiagnostics(diags) + return 1 + } + // Get schemas, if possible, before writing state path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, state, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } diff --git a/internal/command/state_rm.go b/internal/command/state_rm.go index 1273d2cca8..8dfc9f7eb5 100644 --- a/internal/command/state_rm.go +++ b/internal/command/state_rm.go @@ -111,19 +111,26 @@ 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{}) + diags = diags.Append(backendDiags) + if backendDiags.HasErrors() { + c.showDiagnostics(diags) + return 1 + } + // Get schemas, if possible, before writing state path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, state, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } diff --git a/internal/command/taint.go b/internal/command/taint.go index 7546360782..03ddbbddd7 100644 --- a/internal/command/taint.go +++ b/internal/command/taint.go @@ -128,17 +128,17 @@ func (c *TaintCommand) Run(args []string) int { // Get schemas, if possible, before writing state path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, state, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } diff --git a/internal/command/untaint.go b/internal/command/untaint.go index a25d5de470..68d29d99be 100644 --- a/internal/command/untaint.go +++ b/internal/command/untaint.go @@ -167,17 +167,17 @@ func (c *UntaintCommand) Run(args []string) int { // Get schemas, if possible, before writing state path, err := os.Getwd() - if err != nil { + if err != nil && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } config, diags := c.loadConfig(path) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } schemas, diags := getSchemas(&c.Meta, state, config) - if diags.HasErrors() { + if diags.HasErrors() && isCloudMode(b) { c.Ui.Warn(fmt.Sprintf(failedToLoadSchemasMessage, err)) } obj.Status = states.ObjectReady