From cb49a4c8d69980c723875d50033f54acb92fd633 Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Mon, 3 May 2021 21:16:38 -0500 Subject: [PATCH 1/7] backend/remote: Remove the -refresh=false restriction --- internal/backend/remote/backend_apply.go | 34 +++++++++------- internal/backend/remote/backend_plan.go | 49 ++++++++++-------------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/internal/backend/remote/backend_apply.go b/internal/backend/remote/backend_apply.go index 1cf8be8a0..14be147ba 100644 --- a/internal/backend/remote/backend_apply.go +++ b/internal/backend/remote/backend_apply.go @@ -60,15 +60,6 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati )) } - if !op.PlanRefresh { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Applying without refresh is currently not supported", - `Currently the "remote" backend will always do an in-memory refresh of `+ - `the Terraform state prior to generating the plan.`, - )) - } - if b.hasExplicitVariableValues(op) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -96,11 +87,28 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati )) } + // For API versions prior to 2.3, RemoteAPIVersion will return an empty string, + // so if there's an error when parsing the RemoteAPIVersion, it's handled as + // equivalent to an API version < 2.3. + currentAPIVersion, parseErr := version.NewVersion(b.client.RemoteAPIVersion()) + + if !op.PlanRefresh { + desiredAPIVersion, _ := version.NewVersion("2.4") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Planning without refresh is not supported", + fmt.Sprintf( + `The host %s does not support the -refresh=false option for `+ + `remote plans.`, + b.hostname, + ), + )) + } + } + if len(op.Targets) != 0 { - // For API versions prior to 2.3, RemoteAPIVersion will return an empty string, - // so if there's an error when parsing the RemoteAPIVersion, it's handled as - // equivalent to an API version < 2.3. - currentAPIVersion, parseErr := version.NewVersion(b.client.RemoteAPIVersion()) desiredAPIVersion, _ := version.NewVersion("2.3") if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { diff --git a/internal/backend/remote/backend_plan.go b/internal/backend/remote/backend_plan.go index da9b0064d..d73328bcd 100644 --- a/internal/backend/remote/backend_plan.go +++ b/internal/backend/remote/backend_plan.go @@ -65,23 +65,6 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio )) } - if !op.PlanRefresh { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Planning without refresh is currently not supported", - `Currently the "remote" backend will always do an in-memory refresh of `+ - `the Terraform state prior to generating the plan.`, - )) - } - - if op.PlanMode == plans.RefreshOnlyMode { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Refresh-only mode is currently not supported", - `The "remote" backend does not currently support the refresh-only planning mode.`, - )) - } - if b.hasExplicitVariableValues(op) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -110,19 +93,28 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio )) } - if len(op.ForceReplace) != 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Forced replacement is currently not supported", - `The "remote" backend does not currently support the -replace=... planning option.`, - )) + // For API versions prior to 2.3, RemoteAPIVersion will return an empty string, + // so if there's an error when parsing the RemoteAPIVersion, it's handled as + // equivalent to an API version < 2.3. + currentAPIVersion, parseErr := version.NewVersion(b.client.RemoteAPIVersion()) + + if !op.PlanRefresh { + desiredAPIVersion, _ := version.NewVersion("2.4") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Planning without refresh is not supported", + fmt.Sprintf( + `The host %s does not support the -refresh=false option for `+ + `remote plans.`, + b.hostname, + ), + )) + } } if len(op.Targets) != 0 { - // For API versions prior to 2.3, RemoteAPIVersion will return an empty string, - // so if there's an error when parsing the RemoteAPIVersion, it's handled as - // equivalent to an API version < 2.3. - currentAPIVersion, parseErr := version.NewVersion(b.client.RemoteAPIVersion()) desiredAPIVersion, _ := version.NewVersion("2.3") if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { @@ -255,8 +247,9 @@ in order to capture the filesystem context the remote workspace expects: } runOptions := tfe.RunCreateOptions{ - Message: tfe.String(queueMessage), ConfigurationVersion: cv, + Message: tfe.String(queueMessage), + Refresh: tfe.Bool(op.PlanRefresh), Workspace: w, } From a436c7fa2f0da2f76791699ce9cdbca22df5845b Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Mon, 3 May 2021 22:58:40 -0500 Subject: [PATCH 2/7] backend/remote: Support refresh-only plans --- internal/backend/remote/backend_plan.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/backend/remote/backend_plan.go b/internal/backend/remote/backend_plan.go index d73328bcd..6f6588871 100644 --- a/internal/backend/remote/backend_plan.go +++ b/internal/backend/remote/backend_plan.go @@ -256,6 +256,8 @@ in order to capture the filesystem context the remote workspace expects: switch op.PlanMode { case plans.NormalMode: // okay, but we don't need to do anything special for this + case plans.RefreshOnlyMode: + runOptions.RefreshOnly = tfe.Bool(true) case plans.DestroyMode: runOptions.IsDestroy = tfe.Bool(true) default: From 2171f8a1a679b269f305b7fcbd5e95140209650a Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Tue, 4 May 2021 09:35:11 -0500 Subject: [PATCH 3/7] backend/remote: Support replacements in plans --- internal/backend/remote/backend_plan.go | 35 +++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/internal/backend/remote/backend_plan.go b/internal/backend/remote/backend_plan.go index 6f6588871..9f208a7fb 100644 --- a/internal/backend/remote/backend_plan.go +++ b/internal/backend/remote/backend_plan.go @@ -98,6 +98,22 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio // equivalent to an API version < 2.3. currentAPIVersion, parseErr := version.NewVersion(b.client.RemoteAPIVersion()) + if len(op.Targets) != 0 { + desiredAPIVersion, _ := version.NewVersion("2.3") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Resource targeting is not supported", + fmt.Sprintf( + `The host %s does not support the -target option for `+ + `remote plans.`, + b.hostname, + ), + )) + } + } + if !op.PlanRefresh { desiredAPIVersion, _ := version.NewVersion("2.4") @@ -114,15 +130,15 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio } } - if len(op.Targets) != 0 { - desiredAPIVersion, _ := version.NewVersion("2.3") + if len(op.ForceReplace) != 0 { + desiredAPIVersion, _ := version.NewVersion("2.4") if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, - "Resource targeting is not supported", + "Planning resource replacements is not supported", fmt.Sprintf( - `The host %s does not support the -target option for `+ + `The host %s does not support the -replace option for `+ `remote plans.`, b.hostname, ), @@ -273,14 +289,17 @@ in order to capture the filesystem context the remote workspace expects: if len(op.Targets) != 0 { runOptions.TargetAddrs = make([]string, 0, len(op.Targets)) for _, addr := range op.Targets { - // The API client wants the normal string representation of a - // target address, which will ultimately get inserted into a - // -target option when Terraform CLI is launched in the - // Cloud/Enterprise execution environment. runOptions.TargetAddrs = append(runOptions.TargetAddrs, addr.String()) } } + if len(op.ForceReplace) != 0 { + runOptions.ReplaceAddrs = make([]string, 0, len(op.ForceReplace)) + for _, addr := range op.ForceReplace { + runOptions.ReplaceAddrs = append(runOptions.ReplaceAddrs, addr.String()) + } + } + r, err := b.client.Runs.Create(stopCtx, runOptions) if err != nil { return r, generalError("Failed to create run", err) From 16bc8f6442b5c473f257b29261c9f4fa833b60e0 Mon Sep 17 00:00:00 2001 From: CJ Horton Date: Thu, 13 May 2021 18:29:18 -0700 Subject: [PATCH 4/7] go get go-tfe@v0.15.0 --- go.mod | 3 ++- go.sum | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6b3c4b001..219d72e86 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.5.2 github.com/hashicorp/go-rootcerts v1.0.0 // indirect github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect - github.com/hashicorp/go-tfe v0.14.0 + github.com/hashicorp/go-tfe v0.15.0 github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f @@ -139,6 +139,7 @@ require ( ) replace google.golang.org/grpc v1.36.0 => google.golang.org/grpc v1.27.1 + replace github.com/golang/mock v1.5.0 => github.com/golang/mock v1.4.4 replace k8s.io/client-go => k8s.io/client-go v0.0.0-20190620085101-78d2af792bab diff --git a/go.sum b/go.sum index a87cff4f6..d816a0e8d 100644 --- a/go.sum +++ b/go.sum @@ -364,6 +364,10 @@ github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2Y github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-tfe v0.14.0 h1:TJi3tQ3B0qlZN1KqBYlxQ33LgdAbsmPR921ml4H2lDs= github.com/hashicorp/go-tfe v0.14.0/go.mod h1:B71izbwmCZdhEo/GzHopCXN3P74cYv2tsff1mxY4J6c= +github.com/hashicorp/go-tfe v0.14.1-0.20210514003416-95781d84e7d3 h1:r+LZZdpnZh05l+GXgD8vs2NQeIKFlvmkKdsh2CEdBmI= +github.com/hashicorp/go-tfe v0.14.1-0.20210514003416-95781d84e7d3/go.mod h1:NmddH88o7I8jHF7gi4L76rhOKmtIsMlzIW847lPsbYg= +github.com/hashicorp/go-tfe v0.15.0 h1:vdnz1NjOhvmap+cj8iPsL8SbS4iFFVuNYFkGpF5SdoA= +github.com/hashicorp/go-tfe v0.15.0/go.mod h1:c8glB5p6XzocEWLNkuy5RxcjqN5X2PpY6NF3f2W6nIo= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -382,6 +386,8 @@ github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCB github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf h1:EsVVE/vPelkJ83dk/Y3CeMbH/yPR2S8bLzMtxUoMFGI= github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= +github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 h1:mzwkutymYIXR5oQT9YnfbLuuw7LZmksiHKRPUTN5ijo= +github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU1hzH8= github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE= github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb h1:ZbgmOQt8DOg796figP87/EFCVx2v2h9yRvwHF/zceX4= From eb1f1136934d0cadbca3d481be08a9aba17558d7 Mon Sep 17 00:00:00 2001 From: CJ Horton Date: Thu, 13 May 2021 21:14:15 -0700 Subject: [PATCH 5/7] backend/remote: validate API version for refresh and replace --- internal/backend/remote/backend_apply.go | 32 ++++++++++++++++++++++++ internal/backend/remote/backend_plan.go | 16 ++++++++++++ 2 files changed, 48 insertions(+) diff --git a/internal/backend/remote/backend_apply.go b/internal/backend/remote/backend_apply.go index 14be147ba..2ec123d30 100644 --- a/internal/backend/remote/backend_apply.go +++ b/internal/backend/remote/backend_apply.go @@ -108,6 +108,38 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati } } + if op.PlanMode == plans.RefreshOnlyMode { + desiredAPIVersion, _ := version.NewVersion("2.4") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Refresh-only mode is not supported", + fmt.Sprintf( + `The host %s does not support -refresh-only mode for `+ + `remote plans.`, + b.hostname, + ), + )) + } + } + + if len(op.ForceReplace) != 0 { + desiredAPIVersion, _ := version.NewVersion("2.4") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Planning resource replacements is not supported", + fmt.Sprintf( + `The host %s does not support the -replace option for `+ + `remote plans.`, + b.hostname, + ), + )) + } + } + if len(op.Targets) != 0 { desiredAPIVersion, _ := version.NewVersion("2.3") diff --git a/internal/backend/remote/backend_plan.go b/internal/backend/remote/backend_plan.go index 9f208a7fb..430131050 100644 --- a/internal/backend/remote/backend_plan.go +++ b/internal/backend/remote/backend_plan.go @@ -146,6 +146,22 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio } } + if op.PlanMode == plans.RefreshOnlyMode { + desiredAPIVersion, _ := version.NewVersion("2.4") + + if parseErr != nil || currentAPIVersion.LessThan(desiredAPIVersion) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Refresh-only mode is not supported", + fmt.Sprintf( + `The host %s does not support -refresh-only mode for `+ + `remote plans.`, + b.hostname, + ), + )) + } + } + // Return if there are any errors. if diags.HasErrors() { return nil, diags.Err() From ab5fec9aecaf40f900fcf61d46ed5b35ccec560f Mon Sep 17 00:00:00 2001 From: CJ Horton Date: Thu, 13 May 2021 21:28:16 -0700 Subject: [PATCH 6/7] backend/remote: test new options and modes Tests for -refresh=false, -refresh-only, and -replace, as well as tests to ensure we error appropriately on mismatched API versions. --- internal/backend/remote/backend_apply_test.go | 188 +++++++++++++++++- internal/backend/remote/backend_mock.go | 9 + internal/backend/remote/backend_plan_test.go | 188 +++++++++++++++++- internal/backend/remote/testing.go | 2 +- 4 files changed, 382 insertions(+), 5 deletions(-) diff --git a/internal/backend/remote/backend_apply_test.go b/internal/backend/remote/backend_apply_test.go index 5cbd4c4d6..8ba2aa271 100644 --- a/internal/backend/remote/backend_apply_test.go +++ b/internal/backend/remote/backend_apply_test.go @@ -279,6 +279,45 @@ func TestRemote_applyWithoutRefresh(t *testing.T) { op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) + + op.PlanRefresh = false + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + if run.PlanEmpty { + t.Fatalf("expected plan to be non-empty") + } + + // We should find a run inside the mock client that has refresh set + // to false. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff(false, run.Refresh); diff != "" { + t.Errorf("wrong Refresh setting in the created run\n%s", diff) + } + } +} + +func TestRemote_applyWithoutRefreshIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") op.PlanRefresh = false op.Workspace = backend.DefaultStateName @@ -293,10 +332,82 @@ func TestRemote_applyWithoutRefresh(t *testing.T) { if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } errOutput := output.Stderr() - if !strings.Contains(errOutput, "refresh is currently not supported") { - t.Fatalf("expected a refresh error, got: %v", errOutput) + if !strings.Contains(errOutput, "Planning without refresh is not supported") { + t.Fatalf("expected a not supported error, got: %v", errOutput) + } +} + +func TestRemote_applyWithRefreshOnly(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + defer done(t) + + op.PlanMode = plans.RefreshOnlyMode + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + if run.PlanEmpty { + t.Fatalf("expected plan to be non-empty") + } + + // We should find a run inside the mock client that has refresh-only set + // to true. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { + t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) + } + } +} + +func TestRemote_applyWithRefreshOnlyIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") + + op.PlanMode = plans.RefreshOnlyMode + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + output := done(t) + if run.Result == backend.OperationSuccess { + t.Fatal("expected apply operation to fail") + } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } + + errOutput := output.Stderr() + if !strings.Contains(errOutput, "Refresh-only mode is not supported") { + t.Fatalf("expected a not supported error, got: %v", errOutput) } } @@ -375,6 +486,79 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) { } } +func TestRemote_applyWithReplace(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + defer done(t) + + addr, _ := addrs.ParseAbsResourceInstanceStr("null_resource.foo") + + op.ForceReplace = []addrs.AbsResourceInstance{addr} + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatal("expected plan operation to succeed") + } + if run.PlanEmpty { + t.Fatalf("expected plan to be non-empty") + } + + // We should find a run inside the mock client that has the same + // refresh address we requested above. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { + t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) + } + } +} + +func TestRemote_applyWithReplaceIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") + + addr, _ := addrs.ParseAbsResourceInstanceStr("null_resource.foo") + + op.ForceReplace = []addrs.AbsResourceInstance{addr} + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + output := done(t) + if run.Result == backend.OperationSuccess { + t.Fatal("expected apply operation to fail") + } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } + + errOutput := output.Stderr() + if !strings.Contains(errOutput, "Planning resource replacements is not supported") { + t.Fatalf("expected a not supported error, got: %v", errOutput) + } +} + func TestRemote_applyWithVariables(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() diff --git a/internal/backend/remote/backend_mock.go b/internal/backend/remote/backend_mock.go index 5ade80e81..abf150f7c 100644 --- a/internal/backend/remote/backend_mock.go +++ b/internal/backend/remote/backend_mock.go @@ -788,6 +788,7 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t HasChanges: false, Permissions: &tfe.RunPermissions{}, Plan: p, + ReplaceAddrs: options.ReplaceAddrs, Status: tfe.RunPending, TargetAddrs: options.TargetAddrs, } @@ -804,6 +805,14 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t r.IsDestroy = *options.IsDestroy } + if options.Refresh != nil { + r.Refresh = *options.Refresh + } + + if options.RefreshOnly != nil { + r.RefreshOnly = *options.RefreshOnly + } + w, ok := m.client.Workspaces.workspaceIDs[options.Workspace.ID] if !ok { return nil, tfe.ErrResourceNotFound diff --git a/internal/backend/remote/backend_plan_test.go b/internal/backend/remote/backend_plan_test.go index b274963f4..4d5676516 100644 --- a/internal/backend/remote/backend_plan_test.go +++ b/internal/backend/remote/backend_plan_test.go @@ -284,6 +284,45 @@ func TestRemote_planWithoutRefresh(t *testing.T) { op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) + + op.PlanRefresh = false + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + if run.PlanEmpty { + t.Fatal("expected a non-empty plan") + } + + // We should find a run inside the mock client that has refresh set + // to false. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff(false, run.Refresh); diff != "" { + t.Errorf("wrong Refresh setting in the created run\n%s", diff) + } + } +} + +func TestRemote_planWithoutRefreshIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") op.PlanRefresh = false op.Workspace = backend.DefaultStateName @@ -298,10 +337,82 @@ func TestRemote_planWithoutRefresh(t *testing.T) { if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } errOutput := output.Stderr() - if !strings.Contains(errOutput, "refresh is currently not supported") { - t.Fatalf("expected a refresh error, got: %v", errOutput) + if !strings.Contains(errOutput, "Planning without refresh is not supported") { + t.Fatalf("expected not supported error, got: %v", errOutput) + } +} + +func TestRemote_planWithRefreshOnly(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + defer done(t) + + op.PlanMode = plans.RefreshOnlyMode + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + if run.PlanEmpty { + t.Fatal("expected a non-empty plan") + } + + // We should find a run inside the mock client that has refresh-only set + // to true. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { + t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) + } + } +} + +func TestRemote_planWithRefreshOnlyIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") + + op.PlanMode = plans.RefreshOnlyMode + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + output := done(t) + if run.Result == backend.OperationSuccess { + t.Fatal("expected plan operation to fail") + } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } + + errOutput := output.Stderr() + if !strings.Contains(errOutput, "Refresh-only mode is not supported") { + t.Fatalf("expected not supported error, got: %v", errOutput) } } @@ -414,6 +525,79 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) { } } +func TestRemote_planWithReplace(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + defer done(t) + + addr, _ := addrs.ParseAbsResourceInstanceStr("null_resource.foo") + + op.ForceReplace = []addrs.AbsResourceInstance{addr} + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatal("expected plan operation to succeed") + } + if run.PlanEmpty { + t.Fatalf("expected plan to be non-empty") + } + + // We should find a run inside the mock client that has the same + // refresh address we requested above. + runsAPI := b.client.Runs.(*mockRuns) + if got, want := len(runsAPI.runs), 1; got != want { + t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) + } + for _, run := range runsAPI.runs { + if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { + t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) + } + } +} + +func TestRemote_planWithReplaceIncompatibleAPIVersion(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + + b.client.SetFakeRemoteAPIVersion("2.3") + + addr, _ := addrs.ParseAbsResourceInstanceStr("null_resource.foo") + + op.ForceReplace = []addrs.AbsResourceInstance{addr} + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + output := done(t) + if run.Result == backend.OperationSuccess { + t.Fatal("expected plan operation to fail") + } + if !run.PlanEmpty { + t.Fatalf("expected plan to be empty") + } + + errOutput := output.Stderr() + if !strings.Contains(errOutput, "Planning resource replacements is not supported") { + t.Fatalf("expected not supported error, got: %v", errOutput) + } +} + func TestRemote_planWithVariables(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() diff --git a/internal/backend/remote/testing.go b/internal/backend/remote/testing.go index 24a768c67..f3c66941a 100644 --- a/internal/backend/remote/testing.go +++ b/internal/backend/remote/testing.go @@ -200,7 +200,7 @@ func testServer(t *testing.T) *httptest.Server { // Respond to pings to get the API version header. mux.HandleFunc("/api/v2/ping", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Header().Set("TFP-API-Version", "2.3") + w.Header().Set("TFP-API-Version", "2.4") }) // Respond to the initial query to read the hashicorp org entitlements. From 5b49e37747a3f4237cba5796261daf2772f2b8fc Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Wed, 19 May 2021 11:11:04 -0500 Subject: [PATCH 7/7] go mod tidy --- go.sum | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/go.sum b/go.sum index d816a0e8d..3a714b69c 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,6 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= @@ -237,8 +236,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -267,7 +264,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -281,8 +277,8 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -362,10 +358,6 @@ github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZ github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-tfe v0.14.0 h1:TJi3tQ3B0qlZN1KqBYlxQ33LgdAbsmPR921ml4H2lDs= -github.com/hashicorp/go-tfe v0.14.0/go.mod h1:B71izbwmCZdhEo/GzHopCXN3P74cYv2tsff1mxY4J6c= -github.com/hashicorp/go-tfe v0.14.1-0.20210514003416-95781d84e7d3 h1:r+LZZdpnZh05l+GXgD8vs2NQeIKFlvmkKdsh2CEdBmI= -github.com/hashicorp/go-tfe v0.14.1-0.20210514003416-95781d84e7d3/go.mod h1:NmddH88o7I8jHF7gi4L76rhOKmtIsMlzIW847lPsbYg= github.com/hashicorp/go-tfe v0.15.0 h1:vdnz1NjOhvmap+cj8iPsL8SbS4iFFVuNYFkGpF5SdoA= github.com/hashicorp/go-tfe v0.15.0/go.mod h1:c8glB5p6XzocEWLNkuy5RxcjqN5X2PpY6NF3f2W6nIo= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -384,8 +376,6 @@ github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= -github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf h1:EsVVE/vPelkJ83dk/Y3CeMbH/yPR2S8bLzMtxUoMFGI= -github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 h1:mzwkutymYIXR5oQT9YnfbLuuw7LZmksiHKRPUTN5ijo= github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU1hzH8= @@ -632,7 +622,6 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= @@ -681,7 +670,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -692,10 +680,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -749,7 +735,6 @@ golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -809,14 +794,12 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -883,8 +866,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb h1:KVWk3RW1AZlxWum4tYqegLgwJHb5oouozcGM8HfNQaw= -golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -912,8 +893,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A= -google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= @@ -925,8 +904,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -958,7 +937,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -987,8 +965,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=