Update go-tfe to 26689e

These changes include additions to fulfill the interface for the client
mock, plus moving all that logic (which needn't be duplicated across
both the remote and cloud packages) over to the cloud package under a
dedicated mock client file.
This commit is contained in:
Chris Arcand 2021-09-07 15:35:32 -05:00
parent 34597c237f
commit 471dd479e8
12 changed files with 300 additions and 1602 deletions

6
go.mod
View File

@ -40,7 +40,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-plugin v1.4.3 github.com/hashicorp/go-plugin v1.4.3
github.com/hashicorp/go-retryablehttp v0.5.2 github.com/hashicorp/go-retryablehttp v0.5.2
github.com/hashicorp/go-tfe v0.15.0 github.com/hashicorp/go-tfe v0.18.1-0.20210902165242-26689edbfddf
github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/go-uuid v1.0.1
github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/go-version v1.2.1
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
@ -143,9 +143,9 @@ require (
github.com/hashicorp/go-msgpack v0.5.4 // indirect github.com/hashicorp/go-msgpack v0.5.4 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-slug v0.4.1 // indirect github.com/hashicorp/go-slug v0.7.0 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 // indirect github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
github.com/hashicorp/serf v0.9.5 // indirect github.com/hashicorp/serf v0.9.5 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect

15
go.sum
View File

@ -370,13 +370,13 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZqc= github.com/hashicorp/go-slug v0.7.0 h1:8HIi6oreWPtnhpYd8lIGQBgp4rXzDWQTOhfILZm+nok=
github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= github.com/hashicorp/go-slug v0.7.0/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-tfe v0.15.0 h1:vdnz1NjOhvmap+cj8iPsL8SbS4iFFVuNYFkGpF5SdoA= github.com/hashicorp/go-tfe v0.18.1-0.20210902165242-26689edbfddf h1:Tn5cI9kacNyO40ztxmwfAaHrOGd7dELLSAueV2Xfv38=
github.com/hashicorp/go-tfe v0.15.0/go.mod h1:c8glB5p6XzocEWLNkuy5RxcjqN5X2PpY6NF3f2W6nIo= github.com/hashicorp/go-tfe v0.18.1-0.20210902165242-26689edbfddf/go.mod h1:7lChm1Mjsh0ofrUNkP8MHljUFrnKNZNTw36S6qSbJZU=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@ -393,8 +393,8 @@ 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.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys= github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys=
github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3 h1:mzwkutymYIXR5oQT9YnfbLuuw7LZmksiHKRPUTN5ijo= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0=
github.com/hashicorp/jsonapi v0.0.0-20210518035559-1e50d74c8db3/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
@ -610,8 +610,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232 h1:kwsWbh4rEw42ZDe9/812ebhbwNZxlQyZ2sTmxBOKhN4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232 h1:kwsWbh4rEw42ZDe9/812ebhbwNZxlQyZ2sTmxBOKhN4=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.232/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=

View File

@ -14,6 +14,7 @@ import (
version "github.com/hashicorp/go-version" version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/clistate" "github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/command/views"
@ -308,11 +309,11 @@ func TestRemote_applyWithoutRefresh(t *testing.T) {
// We should find a run inside the mock client that has refresh set // We should find a run inside the mock client that has refresh set
// to false. // to false.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(false, run.Refresh); diff != "" { if diff := cmp.Diff(false, run.Refresh); diff != "" {
t.Errorf("wrong Refresh setting in the created run\n%s", diff) t.Errorf("wrong Refresh setting in the created run\n%s", diff)
} }
@ -377,11 +378,11 @@ func TestRemote_applyWithRefreshOnly(t *testing.T) {
// We should find a run inside the mock client that has refresh-only set // We should find a run inside the mock client that has refresh-only set
// to true. // to true.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { if diff := cmp.Diff(true, run.RefreshOnly); diff != "" {
t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff)
} }
@ -448,11 +449,11 @@ func TestRemote_applyWithTarget(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// target address we requested above. // target address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" {
t.Errorf("wrong TargetAddrs in the created run\n%s", diff) t.Errorf("wrong TargetAddrs in the created run\n%s", diff)
} }
@ -523,11 +524,11 @@ func TestRemote_applyWithReplace(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// refresh address we requested above. // refresh address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" {
t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff)
} }

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ import (
tfe "github.com/hashicorp/go-tfe" tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/clistate" "github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/command/views"
@ -313,11 +314,11 @@ func TestRemote_planWithoutRefresh(t *testing.T) {
// We should find a run inside the mock client that has refresh set // We should find a run inside the mock client that has refresh set
// to false. // to false.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(false, run.Refresh); diff != "" { if diff := cmp.Diff(false, run.Refresh); diff != "" {
t.Errorf("wrong Refresh setting in the created run\n%s", diff) t.Errorf("wrong Refresh setting in the created run\n%s", diff)
} }
@ -382,11 +383,11 @@ func TestRemote_planWithRefreshOnly(t *testing.T) {
// We should find a run inside the mock client that has refresh-only set // We should find a run inside the mock client that has refresh-only set
// to true. // to true.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { if diff := cmp.Diff(true, run.RefreshOnly); diff != "" {
t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff)
} }
@ -432,7 +433,7 @@ func TestRemote_planWithTarget(t *testing.T) {
// When the backend code creates a new run, we'll tweak it so that it // When the backend code creates a new run, we'll tweak it so that it
// has a cost estimation object with the "skipped_due_to_targeting" status, // has a cost estimation object with the "skipped_due_to_targeting" status,
// emulating how a real server is expected to behave in that case. // emulating how a real server is expected to behave in that case.
b.client.Runs.(*mockRuns).modifyNewRun = func(client *mockClient, options tfe.RunCreateOptions, run *tfe.Run) { b.client.Runs.(*cloud.MockRuns).ModifyNewRun = func(client *cloud.MockClient, options tfe.RunCreateOptions, run *tfe.Run) {
const fakeID = "fake" const fakeID = "fake"
// This is the cost estimate object embedded in the run itself which // This is the cost estimate object embedded in the run itself which
// the backend will use to learn the ID to request from the cost // the backend will use to learn the ID to request from the cost
@ -446,7 +447,7 @@ func TestRemote_planWithTarget(t *testing.T) {
// the same ID indicated in the object above, where we'll then return // the same ID indicated in the object above, where we'll then return
// the status "skipped_due_to_targeting" to trigger the special skip // the status "skipped_due_to_targeting" to trigger the special skip
// message in the backend output. // message in the backend output.
client.CostEstimates.estimations[fakeID] = &tfe.CostEstimate{ client.CostEstimates.Estimations[fakeID] = &tfe.CostEstimate{
ID: fakeID, ID: fakeID,
Status: "skipped_due_to_targeting", Status: "skipped_due_to_targeting",
} }
@ -483,11 +484,11 @@ func TestRemote_planWithTarget(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// target address we requested above. // target address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" {
t.Errorf("wrong TargetAddrs in the created run\n%s", diff) t.Errorf("wrong TargetAddrs in the created run\n%s", diff)
} }
@ -558,11 +559,11 @@ func TestRemote_planWithReplace(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// refresh address we requested above. // refresh address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*cloud.MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" {
t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff)
} }

View File

@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/remote"
"github.com/hashicorp/terraform/internal/states/statefile" "github.com/hashicorp/terraform/internal/states/statefile"
@ -39,7 +40,7 @@ func TestRemoteClient_stateLock(t *testing.T) {
func TestRemoteClient_withRunID(t *testing.T) { func TestRemoteClient_withRunID(t *testing.T) {
// Set the TFE_RUN_ID environment variable before creating the client! // Set the TFE_RUN_ID environment variable before creating the client!
if err := os.Setenv("TFE_RUN_ID", generateID("run-")); err != nil { if err := os.Setenv("TFE_RUN_ID", cloud.GenerateID("run-")); err != nil {
t.Fatalf("error setting env var TFE_RUN_ID: %v", err) t.Fatalf("error setting env var TFE_RUN_ID: %v", err)
} }

View File

@ -8,12 +8,14 @@ import (
"net/http/httptest" "net/http/httptest"
"path" "path"
"testing" "testing"
"time"
tfe "github.com/hashicorp/go-tfe" tfe "github.com/hashicorp/go-tfe"
svchost "github.com/hashicorp/terraform-svchost" svchost "github.com/hashicorp/terraform-svchost"
"github.com/hashicorp/terraform-svchost/auth" "github.com/hashicorp/terraform-svchost/auth"
"github.com/hashicorp/terraform-svchost/disco" "github.com/hashicorp/terraform-svchost/disco"
"github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/httpclient" "github.com/hashicorp/terraform/internal/httpclient"
@ -39,6 +41,26 @@ var (
}) })
) )
// mockInput is a mock implementation of terraform.UIInput.
type mockInput struct {
answers map[string]string
}
func (m *mockInput) Input(ctx context.Context, opts *terraform.InputOpts) (string, error) {
v, ok := m.answers[opts.Id]
if !ok {
return "", fmt.Errorf("unexpected input request in test: %s", opts.Id)
}
if v == "wait-for-external-update" {
select {
case <-ctx.Done():
case <-time.After(time.Minute):
}
}
delete(m.answers, opts.Id)
return v, nil
}
func testInput(t *testing.T, answers map[string]string) *mockInput { func testInput(t *testing.T, answers map[string]string) *mockInput {
return &mockInput{answers: answers} return &mockInput{answers: answers}
} }
@ -111,7 +133,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) {
} }
// Get a new mock client. // Get a new mock client.
mc := newMockClient() mc := cloud.NewMockClient()
// Replace the services we use with our mock services. // Replace the services we use with our mock services.
b.CLI = cli.NewMockUi() b.CLI = cli.NewMockUi()

View File

@ -299,11 +299,11 @@ func TestCloud_applyWithoutRefresh(t *testing.T) {
// We should find a run inside the mock client that has refresh set // We should find a run inside the mock client that has refresh set
// to false. // to false.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(false, run.Refresh); diff != "" { if diff := cmp.Diff(false, run.Refresh); diff != "" {
t.Errorf("wrong Refresh setting in the created run\n%s", diff) t.Errorf("wrong Refresh setting in the created run\n%s", diff)
} }
@ -368,11 +368,11 @@ func TestCloud_applyWithRefreshOnly(t *testing.T) {
// We should find a run inside the mock client that has refresh-only set // We should find a run inside the mock client that has refresh-only set
// to true. // to true.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { if diff := cmp.Diff(true, run.RefreshOnly); diff != "" {
t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff)
} }
@ -439,11 +439,11 @@ func TestCloud_applyWithTarget(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// target address we requested above. // target address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" {
t.Errorf("wrong TargetAddrs in the created run\n%s", diff) t.Errorf("wrong TargetAddrs in the created run\n%s", diff)
} }
@ -514,11 +514,11 @@ func TestCloud_applyWithReplace(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// refresh address we requested above. // refresh address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" {
t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff)
} }

View File

@ -304,11 +304,11 @@ func TestCloud_planWithoutRefresh(t *testing.T) {
// We should find a run inside the mock client that has refresh set // We should find a run inside the mock client that has refresh set
// to false. // to false.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(false, run.Refresh); diff != "" { if diff := cmp.Diff(false, run.Refresh); diff != "" {
t.Errorf("wrong Refresh setting in the created run\n%s", diff) t.Errorf("wrong Refresh setting in the created run\n%s", diff)
} }
@ -373,11 +373,11 @@ func TestCloud_planWithRefreshOnly(t *testing.T) {
// We should find a run inside the mock client that has refresh-only set // We should find a run inside the mock client that has refresh-only set
// to true. // to true.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff(true, run.RefreshOnly); diff != "" { if diff := cmp.Diff(true, run.RefreshOnly); diff != "" {
t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff) t.Errorf("wrong RefreshOnly setting in the created run\n%s", diff)
} }
@ -423,7 +423,7 @@ func TestCloud_planWithTarget(t *testing.T) {
// When the backend code creates a new run, we'll tweak it so that it // When the backend code creates a new run, we'll tweak it so that it
// has a cost estimation object with the "skipped_due_to_targeting" status, // has a cost estimation object with the "skipped_due_to_targeting" status,
// emulating how a real server is expected to behave in that case. // emulating how a real server is expected to behave in that case.
b.client.Runs.(*mockRuns).modifyNewRun = func(client *mockClient, options tfe.RunCreateOptions, run *tfe.Run) { b.client.Runs.(*MockRuns).ModifyNewRun = func(client *MockClient, options tfe.RunCreateOptions, run *tfe.Run) {
const fakeID = "fake" const fakeID = "fake"
// This is the cost estimate object embedded in the run itself which // This is the cost estimate object embedded in the run itself which
// the backend will use to learn the ID to request from the cost // the backend will use to learn the ID to request from the cost
@ -437,7 +437,7 @@ func TestCloud_planWithTarget(t *testing.T) {
// the same ID indicated in the object above, where we'll then return // the same ID indicated in the object above, where we'll then return
// the status "skipped_due_to_targeting" to trigger the special skip // the status "skipped_due_to_targeting" to trigger the special skip
// message in the backend output. // message in the backend output.
client.CostEstimates.estimations[fakeID] = &tfe.CostEstimate{ client.CostEstimates.Estimations[fakeID] = &tfe.CostEstimate{
ID: fakeID, ID: fakeID,
Status: "skipped_due_to_targeting", Status: "skipped_due_to_targeting",
} }
@ -474,11 +474,11 @@ func TestCloud_planWithTarget(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// target address we requested above. // target address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.TargetAddrs); diff != "" {
t.Errorf("wrong TargetAddrs in the created run\n%s", diff) t.Errorf("wrong TargetAddrs in the created run\n%s", diff)
} }
@ -549,11 +549,11 @@ func TestCloud_planWithReplace(t *testing.T) {
// We should find a run inside the mock client that has the same // We should find a run inside the mock client that has the same
// refresh address we requested above. // refresh address we requested above.
runsAPI := b.client.Runs.(*mockRuns) runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.runs), 1; got != want { if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want) t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
} }
for _, run := range runsAPI.runs { for _, run := range runsAPI.Runs {
if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" { if diff := cmp.Diff([]string{"null_resource.foo"}, run.ReplaceAddrs); diff != "" {
t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff) t.Errorf("wrong ReplaceAddrs in the created run\n%s", diff)
} }

View File

@ -39,7 +39,7 @@ func TestRemoteClient_stateLock(t *testing.T) {
func TestRemoteClient_withRunID(t *testing.T) { func TestRemoteClient_withRunID(t *testing.T) {
// Set the TFE_RUN_ID environment variable before creating the client! // Set the TFE_RUN_ID environment variable before creating the client!
if err := os.Setenv("TFE_RUN_ID", generateID("run-")); err != nil { if err := os.Setenv("TFE_RUN_ID", GenerateID("run-")); err != nil {
t.Fatalf("error setting env var TFE_RUN_ID: %v", err) t.Fatalf("error setting env var TFE_RUN_ID: %v", err)
} }

View File

@ -8,6 +8,7 @@ import (
"net/http/httptest" "net/http/httptest"
"path" "path"
"testing" "testing"
"time"
tfe "github.com/hashicorp/go-tfe" tfe "github.com/hashicorp/go-tfe"
svchost "github.com/hashicorp/terraform-svchost" svchost "github.com/hashicorp/terraform-svchost"
@ -39,6 +40,26 @@ var (
}) })
) )
// mockInput is a mock implementation of terraform.UIInput.
type mockInput struct {
answers map[string]string
}
func (m *mockInput) Input(ctx context.Context, opts *terraform.InputOpts) (string, error) {
v, ok := m.answers[opts.Id]
if !ok {
return "", fmt.Errorf("unexpected input request in test: %s", opts.Id)
}
if v == "wait-for-external-update" {
select {
case <-ctx.Done():
case <-time.After(time.Minute):
}
}
delete(m.answers, opts.Id)
return v, nil
}
func testInput(t *testing.T, answers map[string]string) *mockInput { func testInput(t *testing.T, answers map[string]string) *mockInput {
return &mockInput{answers: answers} return &mockInput{answers: answers}
} }
@ -111,7 +132,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Cloud, func()) {
} }
// Get a new mock client. // Get a new mock client.
mc := newMockClient() mc := NewMockClient()
// Replace the services we use with our mock services. // Replace the services we use with our mock services.
b.CLI = cli.NewMockUi() b.CLI = cli.NewMockUi()

View File

@ -16,26 +16,25 @@ import (
"time" "time"
tfe "github.com/hashicorp/go-tfe" tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform/internal/terraform"
tfversion "github.com/hashicorp/terraform/version" tfversion "github.com/hashicorp/terraform/version"
"github.com/mitchellh/copystructure" "github.com/mitchellh/copystructure"
) )
type mockClient struct { type MockClient struct {
Applies *mockApplies Applies *MockApplies
ConfigurationVersions *mockConfigurationVersions ConfigurationVersions *MockConfigurationVersions
CostEstimates *mockCostEstimates CostEstimates *MockCostEstimates
Organizations *mockOrganizations Organizations *MockOrganizations
Plans *mockPlans Plans *MockPlans
PolicyChecks *mockPolicyChecks PolicyChecks *MockPolicyChecks
Runs *mockRuns Runs *MockRuns
StateVersions *mockStateVersions StateVersions *MockStateVersions
Variables *mockVariables Variables *MockVariables
Workspaces *mockWorkspaces Workspaces *MockWorkspaces
} }
func newMockClient() *mockClient { func NewMockClient() *MockClient {
c := &mockClient{} c := &MockClient{}
c.Applies = newMockApplies(c) c.Applies = newMockApplies(c)
c.ConfigurationVersions = newMockConfigurationVersions(c) c.ConfigurationVersions = newMockConfigurationVersions(c)
c.CostEstimates = newMockCostEstimates(c) c.CostEstimates = newMockCostEstimates(c)
@ -49,14 +48,14 @@ func newMockClient() *mockClient {
return c return c
} }
type mockApplies struct { type MockApplies struct {
client *mockClient client *MockClient
applies map[string]*tfe.Apply applies map[string]*tfe.Apply
logs map[string]string logs map[string]string
} }
func newMockApplies(client *mockClient) *mockApplies { func newMockApplies(client *MockClient) *MockApplies {
return &mockApplies{ return &MockApplies{
client: client, client: client,
applies: make(map[string]*tfe.Apply), applies: make(map[string]*tfe.Apply),
logs: make(map[string]string), logs: make(map[string]string),
@ -65,7 +64,7 @@ func newMockApplies(client *mockClient) *mockApplies {
// create is a helper function to create a mock apply that uses the configured // create is a helper function to create a mock apply that uses the configured
// working directory to find the logfile. // working directory to find the logfile.
func (m *mockApplies) create(cvID, workspaceID string) (*tfe.Apply, error) { func (m *MockApplies) create(cvID, workspaceID string) (*tfe.Apply, error) {
c, ok := m.client.ConfigurationVersions.configVersions[cvID] c, ok := m.client.ConfigurationVersions.configVersions[cvID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -75,7 +74,7 @@ func (m *mockApplies) create(cvID, workspaceID string) (*tfe.Apply, error) {
return nil, nil return nil, nil
} }
id := generateID("apply-") id := GenerateID("apply-")
url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id) url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id)
a := &tfe.Apply{ a := &tfe.Apply{
@ -103,7 +102,7 @@ func (m *mockApplies) create(cvID, workspaceID string) (*tfe.Apply, error) {
return a, nil return a, nil
} }
func (m *mockApplies) Read(ctx context.Context, applyID string) (*tfe.Apply, error) { func (m *MockApplies) Read(ctx context.Context, applyID string) (*tfe.Apply, error) {
a, ok := m.applies[applyID] a, ok := m.applies[applyID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -115,7 +114,7 @@ func (m *mockApplies) Read(ctx context.Context, applyID string) (*tfe.Apply, err
return a, nil return a, nil
} }
func (m *mockApplies) Logs(ctx context.Context, applyID string) (io.Reader, error) { func (m *MockApplies) Logs(ctx context.Context, applyID string) (io.Reader, error) {
a, err := m.Read(ctx, applyID) a, err := m.Read(ctx, applyID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -152,15 +151,15 @@ func (m *mockApplies) Logs(ctx context.Context, applyID string) (io.Reader, erro
}, nil }, nil
} }
type mockConfigurationVersions struct { type MockConfigurationVersions struct {
client *mockClient client *MockClient
configVersions map[string]*tfe.ConfigurationVersion configVersions map[string]*tfe.ConfigurationVersion
uploadPaths map[string]string uploadPaths map[string]string
uploadURLs map[string]*tfe.ConfigurationVersion uploadURLs map[string]*tfe.ConfigurationVersion
} }
func newMockConfigurationVersions(client *mockClient) *mockConfigurationVersions { func newMockConfigurationVersions(client *MockClient) *MockConfigurationVersions {
return &mockConfigurationVersions{ return &MockConfigurationVersions{
client: client, client: client,
configVersions: make(map[string]*tfe.ConfigurationVersion), configVersions: make(map[string]*tfe.ConfigurationVersion),
uploadPaths: make(map[string]string), uploadPaths: make(map[string]string),
@ -168,7 +167,7 @@ func newMockConfigurationVersions(client *mockClient) *mockConfigurationVersions
} }
} }
func (m *mockConfigurationVersions) List(ctx context.Context, workspaceID string, options tfe.ConfigurationVersionListOptions) (*tfe.ConfigurationVersionList, error) { func (m *MockConfigurationVersions) List(ctx context.Context, workspaceID string, options tfe.ConfigurationVersionListOptions) (*tfe.ConfigurationVersionList, error) {
cvl := &tfe.ConfigurationVersionList{} cvl := &tfe.ConfigurationVersionList{}
for _, cv := range m.configVersions { for _, cv := range m.configVersions {
cvl.Items = append(cvl.Items, cv) cvl.Items = append(cvl.Items, cv)
@ -185,8 +184,8 @@ func (m *mockConfigurationVersions) List(ctx context.Context, workspaceID string
return cvl, nil return cvl, nil
} }
func (m *mockConfigurationVersions) Create(ctx context.Context, workspaceID string, options tfe.ConfigurationVersionCreateOptions) (*tfe.ConfigurationVersion, error) { func (m *MockConfigurationVersions) Create(ctx context.Context, workspaceID string, options tfe.ConfigurationVersionCreateOptions) (*tfe.ConfigurationVersion, error) {
id := generateID("cv-") id := GenerateID("cv-")
url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id) url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id)
cv := &tfe.ConfigurationVersion{ cv := &tfe.ConfigurationVersion{
@ -201,7 +200,7 @@ func (m *mockConfigurationVersions) Create(ctx context.Context, workspaceID stri
return cv, nil return cv, nil
} }
func (m *mockConfigurationVersions) Read(ctx context.Context, cvID string) (*tfe.ConfigurationVersion, error) { func (m *MockConfigurationVersions) Read(ctx context.Context, cvID string) (*tfe.ConfigurationVersion, error) {
cv, ok := m.configVersions[cvID] cv, ok := m.configVersions[cvID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -209,7 +208,15 @@ func (m *mockConfigurationVersions) Read(ctx context.Context, cvID string) (*tfe
return cv, nil return cv, nil
} }
func (m *mockConfigurationVersions) Upload(ctx context.Context, url, path string) error { func (m *MockConfigurationVersions) ReadWithOptions(ctx context.Context, cvID string, options *tfe.ConfigurationVersionReadOptions) (*tfe.ConfigurationVersion, error) {
cv, ok := m.configVersions[cvID]
if !ok {
return nil, tfe.ErrResourceNotFound
}
return cv, nil
}
func (m *MockConfigurationVersions) Upload(ctx context.Context, url, path string) error {
cv, ok := m.uploadURLs[url] cv, ok := m.uploadURLs[url]
if !ok { if !ok {
return errors.New("404 not found") return errors.New("404 not found")
@ -219,24 +226,24 @@ func (m *mockConfigurationVersions) Upload(ctx context.Context, url, path string
return nil return nil
} }
type mockCostEstimates struct { type MockCostEstimates struct {
client *mockClient client *MockClient
estimations map[string]*tfe.CostEstimate Estimations map[string]*tfe.CostEstimate
logs map[string]string logs map[string]string
} }
func newMockCostEstimates(client *mockClient) *mockCostEstimates { func newMockCostEstimates(client *MockClient) *MockCostEstimates {
return &mockCostEstimates{ return &MockCostEstimates{
client: client, client: client,
estimations: make(map[string]*tfe.CostEstimate), Estimations: make(map[string]*tfe.CostEstimate),
logs: make(map[string]string), logs: make(map[string]string),
} }
} }
// create is a helper function to create a mock cost estimation that uses the // create is a helper function to create a mock cost estimation that uses the
// configured working directory to find the logfile. // configured working directory to find the logfile.
func (m *mockCostEstimates) create(cvID, workspaceID string) (*tfe.CostEstimate, error) { func (m *MockCostEstimates) create(cvID, workspaceID string) (*tfe.CostEstimate, error) {
id := generateID("ce-") id := GenerateID("ce-")
ce := &tfe.CostEstimate{ ce := &tfe.CostEstimate{
ID: id, ID: id,
@ -263,21 +270,21 @@ func (m *mockCostEstimates) create(cvID, workspaceID string) (*tfe.CostEstimate,
} }
m.logs[ce.ID] = logfile m.logs[ce.ID] = logfile
m.estimations[ce.ID] = ce m.Estimations[ce.ID] = ce
return ce, nil return ce, nil
} }
func (m *mockCostEstimates) Read(ctx context.Context, costEstimateID string) (*tfe.CostEstimate, error) { func (m *MockCostEstimates) Read(ctx context.Context, costEstimateID string) (*tfe.CostEstimate, error) {
ce, ok := m.estimations[costEstimateID] ce, ok := m.Estimations[costEstimateID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
} }
return ce, nil return ce, nil
} }
func (m *mockCostEstimates) Logs(ctx context.Context, costEstimateID string) (io.Reader, error) { func (m *MockCostEstimates) Logs(ctx context.Context, costEstimateID string) (io.Reader, error) {
ce, ok := m.estimations[costEstimateID] ce, ok := m.Estimations[costEstimateID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
} }
@ -301,39 +308,19 @@ func (m *mockCostEstimates) Logs(ctx context.Context, costEstimateID string) (io
return bytes.NewBuffer(logs), nil return bytes.NewBuffer(logs), nil
} }
// mockInput is a mock implementation of terraform.UIInput. type MockOrganizations struct {
type mockInput struct { client *MockClient
answers map[string]string
}
func (m *mockInput) Input(ctx context.Context, opts *terraform.InputOpts) (string, error) {
v, ok := m.answers[opts.Id]
if !ok {
return "", fmt.Errorf("unexpected input request in test: %s", opts.Id)
}
if v == "wait-for-external-update" {
select {
case <-ctx.Done():
case <-time.After(time.Minute):
}
}
delete(m.answers, opts.Id)
return v, nil
}
type mockOrganizations struct {
client *mockClient
organizations map[string]*tfe.Organization organizations map[string]*tfe.Organization
} }
func newMockOrganizations(client *mockClient) *mockOrganizations { func newMockOrganizations(client *MockClient) *MockOrganizations {
return &mockOrganizations{ return &MockOrganizations{
client: client, client: client,
organizations: make(map[string]*tfe.Organization), organizations: make(map[string]*tfe.Organization),
} }
} }
func (m *mockOrganizations) List(ctx context.Context, options tfe.OrganizationListOptions) (*tfe.OrganizationList, error) { func (m *MockOrganizations) List(ctx context.Context, options tfe.OrganizationListOptions) (*tfe.OrganizationList, error) {
orgl := &tfe.OrganizationList{} orgl := &tfe.OrganizationList{}
for _, org := range m.organizations { for _, org := range m.organizations {
orgl.Items = append(orgl.Items, org) orgl.Items = append(orgl.Items, org)
@ -376,13 +363,13 @@ func (m *mockLogReader) read(l []byte) (int, error) {
return m.logs.Read(l) return m.logs.Read(l)
} }
func (m *mockOrganizations) Create(ctx context.Context, options tfe.OrganizationCreateOptions) (*tfe.Organization, error) { func (m *MockOrganizations) Create(ctx context.Context, options tfe.OrganizationCreateOptions) (*tfe.Organization, error) {
org := &tfe.Organization{Name: *options.Name} org := &tfe.Organization{Name: *options.Name}
m.organizations[org.Name] = org m.organizations[org.Name] = org
return org, nil return org, nil
} }
func (m *mockOrganizations) Read(ctx context.Context, name string) (*tfe.Organization, error) { func (m *MockOrganizations) Read(ctx context.Context, name string) (*tfe.Organization, error) {
org, ok := m.organizations[name] org, ok := m.organizations[name]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -390,7 +377,7 @@ func (m *mockOrganizations) Read(ctx context.Context, name string) (*tfe.Organiz
return org, nil return org, nil
} }
func (m *mockOrganizations) Update(ctx context.Context, name string, options tfe.OrganizationUpdateOptions) (*tfe.Organization, error) { func (m *MockOrganizations) Update(ctx context.Context, name string, options tfe.OrganizationUpdateOptions) (*tfe.Organization, error) {
org, ok := m.organizations[name] org, ok := m.organizations[name]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -400,14 +387,14 @@ func (m *mockOrganizations) Update(ctx context.Context, name string, options tfe
} }
func (m *mockOrganizations) Delete(ctx context.Context, name string) error { func (m *MockOrganizations) Delete(ctx context.Context, name string) error {
delete(m.organizations, name) delete(m.organizations, name)
return nil return nil
} }
func (m *mockOrganizations) Capacity(ctx context.Context, name string) (*tfe.Capacity, error) { func (m *MockOrganizations) Capacity(ctx context.Context, name string) (*tfe.Capacity, error) {
var pending, running int var pending, running int
for _, r := range m.client.Runs.runs { for _, r := range m.client.Runs.Runs {
if r.Status == tfe.RunPending { if r.Status == tfe.RunPending {
pending++ pending++
continue continue
@ -417,7 +404,7 @@ func (m *mockOrganizations) Capacity(ctx context.Context, name string) (*tfe.Cap
return &tfe.Capacity{Pending: pending, Running: running}, nil return &tfe.Capacity{Pending: pending, Running: running}, nil
} }
func (m *mockOrganizations) Entitlements(ctx context.Context, name string) (*tfe.Entitlements, error) { func (m *MockOrganizations) Entitlements(ctx context.Context, name string) (*tfe.Entitlements, error) {
return &tfe.Entitlements{ return &tfe.Entitlements{
Operations: true, Operations: true,
PrivateModuleRegistry: true, PrivateModuleRegistry: true,
@ -428,10 +415,10 @@ func (m *mockOrganizations) Entitlements(ctx context.Context, name string) (*tfe
}, nil }, nil
} }
func (m *mockOrganizations) RunQueue(ctx context.Context, name string, options tfe.RunQueueOptions) (*tfe.RunQueue, error) { func (m *MockOrganizations) RunQueue(ctx context.Context, name string, options tfe.RunQueueOptions) (*tfe.RunQueue, error) {
rq := &tfe.RunQueue{} rq := &tfe.RunQueue{}
for _, r := range m.client.Runs.runs { for _, r := range m.client.Runs.Runs {
rq.Items = append(rq.Items, r) rq.Items = append(rq.Items, r)
} }
@ -446,15 +433,15 @@ func (m *mockOrganizations) RunQueue(ctx context.Context, name string, options t
return rq, nil return rq, nil
} }
type mockPlans struct { type MockPlans struct {
client *mockClient client *MockClient
logs map[string]string logs map[string]string
planOutputs map[string]string planOutputs map[string]string
plans map[string]*tfe.Plan plans map[string]*tfe.Plan
} }
func newMockPlans(client *mockClient) *mockPlans { func newMockPlans(client *MockClient) *MockPlans {
return &mockPlans{ return &MockPlans{
client: client, client: client,
logs: make(map[string]string), logs: make(map[string]string),
planOutputs: make(map[string]string), planOutputs: make(map[string]string),
@ -464,8 +451,8 @@ func newMockPlans(client *mockClient) *mockPlans {
// create is a helper function to create a mock plan that uses the configured // create is a helper function to create a mock plan that uses the configured
// working directory to find the logfile. // working directory to find the logfile.
func (m *mockPlans) create(cvID, workspaceID string) (*tfe.Plan, error) { func (m *MockPlans) create(cvID, workspaceID string) (*tfe.Plan, error) {
id := generateID("plan-") id := GenerateID("plan-")
url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id) url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id)
p := &tfe.Plan{ p := &tfe.Plan{
@ -489,7 +476,7 @@ func (m *mockPlans) create(cvID, workspaceID string) (*tfe.Plan, error) {
return p, nil return p, nil
} }
func (m *mockPlans) Read(ctx context.Context, planID string) (*tfe.Plan, error) { func (m *MockPlans) Read(ctx context.Context, planID string) (*tfe.Plan, error) {
p, ok := m.plans[planID] p, ok := m.plans[planID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -501,7 +488,7 @@ func (m *mockPlans) Read(ctx context.Context, planID string) (*tfe.Plan, error)
return p, nil return p, nil
} }
func (m *mockPlans) Logs(ctx context.Context, planID string) (io.Reader, error) { func (m *MockPlans) Logs(ctx context.Context, planID string) (io.Reader, error) {
p, err := m.Read(ctx, planID) p, err := m.Read(ctx, planID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -538,7 +525,7 @@ func (m *mockPlans) Logs(ctx context.Context, planID string) (io.Reader, error)
}, nil }, nil
} }
func (m *mockPlans) JSONOutput(ctx context.Context, planID string) ([]byte, error) { func (m *MockPlans) JSONOutput(ctx context.Context, planID string) ([]byte, error) {
planOutput, ok := m.planOutputs[planID] planOutput, ok := m.planOutputs[planID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -547,14 +534,14 @@ func (m *mockPlans) JSONOutput(ctx context.Context, planID string) ([]byte, erro
return []byte(planOutput), nil return []byte(planOutput), nil
} }
type mockPolicyChecks struct { type MockPolicyChecks struct {
client *mockClient client *MockClient
checks map[string]*tfe.PolicyCheck checks map[string]*tfe.PolicyCheck
logs map[string]string logs map[string]string
} }
func newMockPolicyChecks(client *mockClient) *mockPolicyChecks { func newMockPolicyChecks(client *MockClient) *MockPolicyChecks {
return &mockPolicyChecks{ return &MockPolicyChecks{
client: client, client: client,
checks: make(map[string]*tfe.PolicyCheck), checks: make(map[string]*tfe.PolicyCheck),
logs: make(map[string]string), logs: make(map[string]string),
@ -563,8 +550,8 @@ func newMockPolicyChecks(client *mockClient) *mockPolicyChecks {
// create is a helper function to create a mock policy check that uses the // create is a helper function to create a mock policy check that uses the
// configured working directory to find the logfile. // configured working directory to find the logfile.
func (m *mockPolicyChecks) create(cvID, workspaceID string) (*tfe.PolicyCheck, error) { func (m *MockPolicyChecks) create(cvID, workspaceID string) (*tfe.PolicyCheck, error) {
id := generateID("pc-") id := GenerateID("pc-")
pc := &tfe.PolicyCheck{ pc := &tfe.PolicyCheck{
ID: id, ID: id,
@ -595,8 +582,8 @@ func (m *mockPolicyChecks) create(cvID, workspaceID string) (*tfe.PolicyCheck, e
return pc, nil return pc, nil
} }
func (m *mockPolicyChecks) List(ctx context.Context, runID string, options tfe.PolicyCheckListOptions) (*tfe.PolicyCheckList, error) { func (m *MockPolicyChecks) List(ctx context.Context, runID string, options tfe.PolicyCheckListOptions) (*tfe.PolicyCheckList, error) {
_, ok := m.client.Runs.runs[runID] _, ok := m.client.Runs.Runs[runID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
} }
@ -617,7 +604,7 @@ func (m *mockPolicyChecks) List(ctx context.Context, runID string, options tfe.P
return pcl, nil return pcl, nil
} }
func (m *mockPolicyChecks) Read(ctx context.Context, policyCheckID string) (*tfe.PolicyCheck, error) { func (m *MockPolicyChecks) Read(ctx context.Context, policyCheckID string) (*tfe.PolicyCheck, error) {
pc, ok := m.checks[policyCheckID] pc, ok := m.checks[policyCheckID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -657,7 +644,7 @@ func (m *mockPolicyChecks) Read(ctx context.Context, policyCheckID string) (*tfe
return pc, nil return pc, nil
} }
func (m *mockPolicyChecks) Override(ctx context.Context, policyCheckID string) (*tfe.PolicyCheck, error) { func (m *MockPolicyChecks) Override(ctx context.Context, policyCheckID string) (*tfe.PolicyCheck, error) {
pc, ok := m.checks[policyCheckID] pc, ok := m.checks[policyCheckID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -666,7 +653,7 @@ func (m *mockPolicyChecks) Override(ctx context.Context, policyCheckID string) (
return pc, nil return pc, nil
} }
func (m *mockPolicyChecks) Logs(ctx context.Context, policyCheckID string) (io.Reader, error) { func (m *MockPolicyChecks) Logs(ctx context.Context, policyCheckID string) (io.Reader, error) {
pc, ok := m.checks[policyCheckID] pc, ok := m.checks[policyCheckID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -706,28 +693,28 @@ func (m *mockPolicyChecks) Logs(ctx context.Context, policyCheckID string) (io.R
return bytes.NewBuffer(logs), nil return bytes.NewBuffer(logs), nil
} }
type mockRuns struct { type MockRuns struct {
sync.Mutex sync.Mutex
client *mockClient client *MockClient
runs map[string]*tfe.Run Runs map[string]*tfe.Run
workspaces map[string][]*tfe.Run workspaces map[string][]*tfe.Run
// If modifyNewRun is non-nil, the create method will call it just before // If ModifyNewRun is non-nil, the create method will call it just before
// saving a new run in the runs map, so that a calling test can mimic // saving a new run in the runs map, so that a calling test can mimic
// side-effects that a real server might apply in certain situations. // side-effects that a real server might apply in certain situations.
modifyNewRun func(client *mockClient, options tfe.RunCreateOptions, run *tfe.Run) ModifyNewRun func(client *MockClient, options tfe.RunCreateOptions, run *tfe.Run)
} }
func newMockRuns(client *mockClient) *mockRuns { func newMockRuns(client *MockClient) *MockRuns {
return &mockRuns{ return &MockRuns{
client: client, client: client,
runs: make(map[string]*tfe.Run), Runs: make(map[string]*tfe.Run),
workspaces: make(map[string][]*tfe.Run), workspaces: make(map[string][]*tfe.Run),
} }
} }
func (m *mockRuns) List(ctx context.Context, workspaceID string, options tfe.RunListOptions) (*tfe.RunList, error) { func (m *MockRuns) List(ctx context.Context, workspaceID string, options tfe.RunListOptions) (*tfe.RunList, error) {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
@ -756,7 +743,7 @@ func (m *mockRuns) List(ctx context.Context, workspaceID string, options tfe.Run
return rl, nil return rl, nil
} }
func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*tfe.Run, error) { func (m *MockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*tfe.Run, error) {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
@ -781,7 +768,7 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t
} }
r := &tfe.Run{ r := &tfe.Run{
ID: generateID("run-"), ID: GenerateID("run-"),
Actions: &tfe.RunActions{IsCancelable: true}, Actions: &tfe.RunActions{IsCancelable: true},
Apply: a, Apply: a,
CostEstimate: ce, CostEstimate: ce,
@ -821,33 +808,33 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t
w.CurrentRun = r w.CurrentRun = r
} }
if m.modifyNewRun != nil { if m.ModifyNewRun != nil {
// caller-provided callback may modify the run in-place to mimic // caller-provided callback may modify the run in-place to mimic
// side-effects that a real server might take in some situations. // side-effects that a real server might take in some situations.
m.modifyNewRun(m.client, options, r) m.ModifyNewRun(m.client, options, r)
} }
m.runs[r.ID] = r m.Runs[r.ID] = r
m.workspaces[options.Workspace.ID] = append(m.workspaces[options.Workspace.ID], r) m.workspaces[options.Workspace.ID] = append(m.workspaces[options.Workspace.ID], r)
return r, nil return r, nil
} }
func (m *mockRuns) Read(ctx context.Context, runID string) (*tfe.Run, error) { func (m *MockRuns) Read(ctx context.Context, runID string) (*tfe.Run, error) {
return m.ReadWithOptions(ctx, runID, nil) return m.ReadWithOptions(ctx, runID, nil)
} }
func (m *mockRuns) ReadWithOptions(ctx context.Context, runID string, _ *tfe.RunReadOptions) (*tfe.Run, error) { func (m *MockRuns) ReadWithOptions(ctx context.Context, runID string, _ *tfe.RunReadOptions) (*tfe.Run, error) {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
r, ok := m.runs[runID] r, ok := m.Runs[runID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
} }
pending := false pending := false
for _, r := range m.runs { for _, r := range m.Runs {
if r.ID != runID && r.Status == tfe.RunPending { if r.ID != runID && r.Status == tfe.RunPending {
pending = true pending = true
break break
@ -885,11 +872,11 @@ func (m *mockRuns) ReadWithOptions(ctx context.Context, runID string, _ *tfe.Run
return rc.(*tfe.Run), nil return rc.(*tfe.Run), nil
} }
func (m *mockRuns) Apply(ctx context.Context, runID string, options tfe.RunApplyOptions) error { func (m *MockRuns) Apply(ctx context.Context, runID string, options tfe.RunApplyOptions) error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
r, ok := m.runs[runID] r, ok := m.Runs[runID]
if !ok { if !ok {
return tfe.ErrResourceNotFound return tfe.ErrResourceNotFound
} }
@ -902,19 +889,19 @@ func (m *mockRuns) Apply(ctx context.Context, runID string, options tfe.RunApply
return nil return nil
} }
func (m *mockRuns) Cancel(ctx context.Context, runID string, options tfe.RunCancelOptions) error { func (m *MockRuns) Cancel(ctx context.Context, runID string, options tfe.RunCancelOptions) error {
panic("not implemented") panic("not implemented")
} }
func (m *mockRuns) ForceCancel(ctx context.Context, runID string, options tfe.RunForceCancelOptions) error { func (m *MockRuns) ForceCancel(ctx context.Context, runID string, options tfe.RunForceCancelOptions) error {
panic("not implemented") panic("not implemented")
} }
func (m *mockRuns) Discard(ctx context.Context, runID string, options tfe.RunDiscardOptions) error { func (m *MockRuns) Discard(ctx context.Context, runID string, options tfe.RunDiscardOptions) error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
r, ok := m.runs[runID] r, ok := m.Runs[runID]
if !ok { if !ok {
return tfe.ErrResourceNotFound return tfe.ErrResourceNotFound
} }
@ -923,15 +910,15 @@ func (m *mockRuns) Discard(ctx context.Context, runID string, options tfe.RunDis
return nil return nil
} }
type mockStateVersions struct { type MockStateVersions struct {
client *mockClient client *MockClient
states map[string][]byte states map[string][]byte
stateVersions map[string]*tfe.StateVersion stateVersions map[string]*tfe.StateVersion
workspaces map[string][]string workspaces map[string][]string
} }
func newMockStateVersions(client *mockClient) *mockStateVersions { func newMockStateVersions(client *MockClient) *MockStateVersions {
return &mockStateVersions{ return &MockStateVersions{
client: client, client: client,
states: make(map[string][]byte), states: make(map[string][]byte),
stateVersions: make(map[string]*tfe.StateVersion), stateVersions: make(map[string]*tfe.StateVersion),
@ -939,7 +926,7 @@ func newMockStateVersions(client *mockClient) *mockStateVersions {
} }
} }
func (m *mockStateVersions) List(ctx context.Context, options tfe.StateVersionListOptions) (*tfe.StateVersionList, error) { func (m *MockStateVersions) List(ctx context.Context, options tfe.StateVersionListOptions) (*tfe.StateVersionList, error) {
svl := &tfe.StateVersionList{} svl := &tfe.StateVersionList{}
for _, sv := range m.stateVersions { for _, sv := range m.stateVersions {
svl.Items = append(svl.Items, sv) svl.Items = append(svl.Items, sv)
@ -956,8 +943,8 @@ func (m *mockStateVersions) List(ctx context.Context, options tfe.StateVersionLi
return svl, nil return svl, nil
} }
func (m *mockStateVersions) Create(ctx context.Context, workspaceID string, options tfe.StateVersionCreateOptions) (*tfe.StateVersion, error) { func (m *MockStateVersions) Create(ctx context.Context, workspaceID string, options tfe.StateVersionCreateOptions) (*tfe.StateVersion, error) {
id := generateID("sv-") id := GenerateID("sv-")
runID := os.Getenv("TFE_RUN_ID") runID := os.Getenv("TFE_RUN_ID")
url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id) url := fmt.Sprintf("https://app.terraform.io/_archivist/%s", id)
@ -983,11 +970,11 @@ func (m *mockStateVersions) Create(ctx context.Context, workspaceID string, opti
return sv, nil return sv, nil
} }
func (m *mockStateVersions) Read(ctx context.Context, svID string) (*tfe.StateVersion, error) { func (m *MockStateVersions) Read(ctx context.Context, svID string) (*tfe.StateVersion, error) {
return m.ReadWithOptions(ctx, svID, nil) return m.ReadWithOptions(ctx, svID, nil)
} }
func (m *mockStateVersions) ReadWithOptions(ctx context.Context, svID string, options *tfe.StateVersionReadOptions) (*tfe.StateVersion, error) { func (m *MockStateVersions) ReadWithOptions(ctx context.Context, svID string, options *tfe.StateVersionReadOptions) (*tfe.StateVersion, error) {
sv, ok := m.stateVersions[svID] sv, ok := m.stateVersions[svID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -995,11 +982,11 @@ func (m *mockStateVersions) ReadWithOptions(ctx context.Context, svID string, op
return sv, nil return sv, nil
} }
func (m *mockStateVersions) Current(ctx context.Context, workspaceID string) (*tfe.StateVersion, error) { func (m *MockStateVersions) Current(ctx context.Context, workspaceID string) (*tfe.StateVersion, error) {
return m.CurrentWithOptions(ctx, workspaceID, nil) return m.CurrentWithOptions(ctx, workspaceID, nil)
} }
func (m *mockStateVersions) CurrentWithOptions(ctx context.Context, workspaceID string, options *tfe.StateVersionCurrentOptions) (*tfe.StateVersion, error) { func (m *MockStateVersions) CurrentWithOptions(ctx context.Context, workspaceID string, options *tfe.StateVersionCurrentOptions) (*tfe.StateVersion, error) {
w, ok := m.client.Workspaces.workspaceIDs[workspaceID] w, ok := m.client.Workspaces.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1018,7 +1005,7 @@ func (m *mockStateVersions) CurrentWithOptions(ctx context.Context, workspaceID
return sv, nil return sv, nil
} }
func (m *mockStateVersions) Download(ctx context.Context, url string) ([]byte, error) { func (m *MockStateVersions) Download(ctx context.Context, url string) ([]byte, error) {
state, ok := m.states[url] state, ok := m.states[url]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1026,28 +1013,32 @@ func (m *mockStateVersions) Download(ctx context.Context, url string) ([]byte, e
return state, nil return state, nil
} }
type mockVariables struct { func (m *MockStateVersions) Outputs(ctx context.Context, svID string, options tfe.StateVersionOutputsListOptions) ([]*tfe.StateVersionOutput, error) {
client *mockClient panic("not implemented")
}
type MockVariables struct {
client *MockClient
workspaces map[string]*tfe.VariableList workspaces map[string]*tfe.VariableList
} }
var _ tfe.Variables = (*mockVariables)(nil) var _ tfe.Variables = (*MockVariables)(nil)
func newMockVariables(client *mockClient) *mockVariables { func newMockVariables(client *MockClient) *MockVariables {
return &mockVariables{ return &MockVariables{
client: client, client: client,
workspaces: make(map[string]*tfe.VariableList), workspaces: make(map[string]*tfe.VariableList),
} }
} }
func (m *mockVariables) List(ctx context.Context, workspaceID string, options tfe.VariableListOptions) (*tfe.VariableList, error) { func (m *MockVariables) List(ctx context.Context, workspaceID string, options tfe.VariableListOptions) (*tfe.VariableList, error) {
vl := m.workspaces[workspaceID] vl := m.workspaces[workspaceID]
return vl, nil return vl, nil
} }
func (m *mockVariables) Create(ctx context.Context, workspaceID string, options tfe.VariableCreateOptions) (*tfe.Variable, error) { func (m *MockVariables) Create(ctx context.Context, workspaceID string, options tfe.VariableCreateOptions) (*tfe.Variable, error) {
v := &tfe.Variable{ v := &tfe.Variable{
ID: generateID("var-"), ID: GenerateID("var-"),
Key: *options.Key, Key: *options.Key,
Category: *options.Category, Category: *options.Category,
} }
@ -1073,33 +1064,33 @@ func (m *mockVariables) Create(ctx context.Context, workspaceID string, options
return v, nil return v, nil
} }
func (m *mockVariables) Read(ctx context.Context, workspaceID string, variableID string) (*tfe.Variable, error) { func (m *MockVariables) Read(ctx context.Context, workspaceID string, variableID string) (*tfe.Variable, error) {
panic("not implemented") panic("not implemented")
} }
func (m *mockVariables) Update(ctx context.Context, workspaceID string, variableID string, options tfe.VariableUpdateOptions) (*tfe.Variable, error) { func (m *MockVariables) Update(ctx context.Context, workspaceID string, variableID string, options tfe.VariableUpdateOptions) (*tfe.Variable, error) {
panic("not implemented") panic("not implemented")
} }
func (m *mockVariables) Delete(ctx context.Context, workspaceID string, variableID string) error { func (m *MockVariables) Delete(ctx context.Context, workspaceID string, variableID string) error {
panic("not implemented") panic("not implemented")
} }
type mockWorkspaces struct { type MockWorkspaces struct {
client *mockClient client *MockClient
workspaceIDs map[string]*tfe.Workspace workspaceIDs map[string]*tfe.Workspace
workspaceNames map[string]*tfe.Workspace workspaceNames map[string]*tfe.Workspace
} }
func newMockWorkspaces(client *mockClient) *mockWorkspaces { func newMockWorkspaces(client *MockClient) *MockWorkspaces {
return &mockWorkspaces{ return &MockWorkspaces{
client: client, client: client,
workspaceIDs: make(map[string]*tfe.Workspace), workspaceIDs: make(map[string]*tfe.Workspace),
workspaceNames: make(map[string]*tfe.Workspace), workspaceNames: make(map[string]*tfe.Workspace),
} }
} }
func (m *mockWorkspaces) List(ctx context.Context, organization string, options tfe.WorkspaceListOptions) (*tfe.WorkspaceList, error) { func (m *MockWorkspaces) List(ctx context.Context, organization string, options tfe.WorkspaceListOptions) (*tfe.WorkspaceList, error) {
dummyWorkspaces := 10 dummyWorkspaces := 10
wl := &tfe.WorkspaceList{} wl := &tfe.WorkspaceList{}
@ -1129,7 +1120,7 @@ func (m *mockWorkspaces) List(ctx context.Context, organization string, options
if options.PageNumber <= 1 { if options.PageNumber <= 1 {
for i := 0; i < dummyWorkspaces; i++ { for i := 0; i < dummyWorkspaces; i++ {
wl.Items = append(wl.Items, &tfe.Workspace{ wl.Items = append(wl.Items, &tfe.Workspace{
ID: generateID("ws-"), ID: GenerateID("ws-"),
Name: fmt.Sprintf("dummy-workspace-%d", i), Name: fmt.Sprintf("dummy-workspace-%d", i),
}) })
} }
@ -1156,14 +1147,14 @@ func (m *mockWorkspaces) List(ctx context.Context, organization string, options
return wl, nil return wl, nil
} }
func (m *mockWorkspaces) Create(ctx context.Context, organization string, options tfe.WorkspaceCreateOptions) (*tfe.Workspace, error) { func (m *MockWorkspaces) Create(ctx context.Context, organization string, options tfe.WorkspaceCreateOptions) (*tfe.Workspace, error) {
if strings.HasSuffix(*options.Name, "no-operations") { if strings.HasSuffix(*options.Name, "no-operations") {
options.Operations = tfe.Bool(false) options.Operations = tfe.Bool(false)
} else if options.Operations == nil { } else if options.Operations == nil {
options.Operations = tfe.Bool(true) options.Operations = tfe.Bool(true)
} }
w := &tfe.Workspace{ w := &tfe.Workspace{
ID: generateID("ws-"), ID: GenerateID("ws-"),
Name: *options.Name, Name: *options.Name,
Operations: *options.Operations, Operations: *options.Operations,
Permissions: &tfe.WorkspacePermissions{ Permissions: &tfe.WorkspacePermissions{
@ -1187,7 +1178,7 @@ func (m *mockWorkspaces) Create(ctx context.Context, organization string, option
return w, nil return w, nil
} }
func (m *mockWorkspaces) Read(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) { func (m *MockWorkspaces) Read(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) {
// custom error for TestCloud_plan500 in backend_plan_test.go // custom error for TestCloud_plan500 in backend_plan_test.go
if workspace == "network-error" { if workspace == "network-error" {
return nil, errors.New("I'm a little teacup") return nil, errors.New("I'm a little teacup")
@ -1200,7 +1191,7 @@ func (m *mockWorkspaces) Read(ctx context.Context, organization, workspace strin
return w, nil return w, nil
} }
func (m *mockWorkspaces) ReadByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) { func (m *MockWorkspaces) ReadByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1208,7 +1199,19 @@ func (m *mockWorkspaces) ReadByID(ctx context.Context, workspaceID string) (*tfe
return w, nil return w, nil
} }
func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) { func (m *MockWorkspaces) ReadWithOptions(ctx context.Context, organization string, workspace string, options *tfe.WorkspaceReadOptions) (*tfe.Workspace, error) {
panic("not implemented")
}
func (m *MockWorkspaces) ReadByIDWithOptions(ctx context.Context, workspaceID string, options *tfe.WorkspaceReadOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID]
if !ok {
return nil, tfe.ErrResourceNotFound
}
return w, nil
}
func (m *MockWorkspaces) Update(ctx context.Context, organization, workspace string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceNames[workspace] w, ok := m.workspaceNames[workspace]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1233,7 +1236,7 @@ func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace str
return w, nil return w, nil
} }
func (m *mockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) { func (m *MockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1255,7 +1258,7 @@ func (m *mockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, opt
return w, nil return w, nil
} }
func (m *mockWorkspaces) Delete(ctx context.Context, organization, workspace string) error { func (m *MockWorkspaces) Delete(ctx context.Context, organization, workspace string) error {
if w, ok := m.workspaceNames[workspace]; ok { if w, ok := m.workspaceNames[workspace]; ok {
delete(m.workspaceIDs, w.ID) delete(m.workspaceIDs, w.ID)
} }
@ -1263,7 +1266,7 @@ func (m *mockWorkspaces) Delete(ctx context.Context, organization, workspace str
return nil return nil
} }
func (m *mockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) error { func (m *MockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) error {
if w, ok := m.workspaceIDs[workspaceID]; ok { if w, ok := m.workspaceIDs[workspaceID]; ok {
delete(m.workspaceIDs, w.Name) delete(m.workspaceIDs, w.Name)
} }
@ -1271,7 +1274,7 @@ func (m *mockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) err
return nil return nil
} }
func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) { func (m *MockWorkspaces) RemoveVCSConnection(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) {
w, ok := m.workspaceNames[workspace] w, ok := m.workspaceNames[workspace]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1280,7 +1283,7 @@ func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organization,
return w, nil return w, nil
} }
func (m *mockWorkspaces) RemoveVCSConnectionByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) { func (m *MockWorkspaces) RemoveVCSConnectionByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1289,7 +1292,7 @@ func (m *mockWorkspaces) RemoveVCSConnectionByID(ctx context.Context, workspaceI
return w, nil return w, nil
} }
func (m *mockWorkspaces) Lock(ctx context.Context, workspaceID string, options tfe.WorkspaceLockOptions) (*tfe.Workspace, error) { func (m *MockWorkspaces) Lock(ctx context.Context, workspaceID string, options tfe.WorkspaceLockOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1301,7 +1304,7 @@ func (m *mockWorkspaces) Lock(ctx context.Context, workspaceID string, options t
return w, nil return w, nil
} }
func (m *mockWorkspaces) Unlock(ctx context.Context, workspaceID string) (*tfe.Workspace, error) { func (m *MockWorkspaces) Unlock(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1313,7 +1316,7 @@ func (m *mockWorkspaces) Unlock(ctx context.Context, workspaceID string) (*tfe.W
return w, nil return w, nil
} }
func (m *mockWorkspaces) ForceUnlock(ctx context.Context, workspaceID string) (*tfe.Workspace, error) { func (m *MockWorkspaces) ForceUnlock(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID] w, ok := m.workspaceIDs[workspaceID]
if !ok { if !ok {
return nil, tfe.ErrResourceNotFound return nil, tfe.ErrResourceNotFound
@ -1325,37 +1328,49 @@ func (m *mockWorkspaces) ForceUnlock(ctx context.Context, workspaceID string) (*
return w, nil return w, nil
} }
func (m *mockWorkspaces) AssignSSHKey(ctx context.Context, workspaceID string, options tfe.WorkspaceAssignSSHKeyOptions) (*tfe.Workspace, error) { func (m *MockWorkspaces) AssignSSHKey(ctx context.Context, workspaceID string, options tfe.WorkspaceAssignSSHKeyOptions) (*tfe.Workspace, error) {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) UnassignSSHKey(ctx context.Context, workspaceID string) (*tfe.Workspace, error) { func (m *MockWorkspaces) UnassignSSHKey(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) RemoteStateConsumers(ctx context.Context, workspaceID string) (*tfe.WorkspaceList, error) { func (m *MockWorkspaces) RemoteStateConsumers(ctx context.Context, workspaceID string) (*tfe.WorkspaceList, error) {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) AddRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceAddRemoteStateConsumersOptions) error { func (m *MockWorkspaces) AddRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceAddRemoteStateConsumersOptions) error {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) RemoveRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveRemoteStateConsumersOptions) error { func (m *MockWorkspaces) RemoveRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveRemoteStateConsumersOptions) error {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) UpdateRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateRemoteStateConsumersOptions) error { func (m *MockWorkspaces) UpdateRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateRemoteStateConsumersOptions) error {
panic("not implemented") panic("not implemented")
} }
func (m *mockWorkspaces) Readme(ctx context.Context, workspaceID string) (io.Reader, error) { func (m *MockWorkspaces) Readme(ctx context.Context, workspaceID string) (io.Reader, error) {
panic("not implemented")
}
func (m *MockWorkspaces) Tags(ctx context.Context, workspaceID string, options tfe.WorkspaceTagListOptions) (*tfe.TagList, error) {
panic("not implemented")
}
func (m *MockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error {
panic("not implemented")
}
func (m *MockWorkspaces) RemoveTags(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveTagsOptions) error {
panic("not implemented") panic("not implemented")
} }
const alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" const alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func generateID(s string) string { func GenerateID(s string) string {
b := make([]byte, 16) b := make([]byte, 16)
for i := range b { for i := range b {
b[i] = alphanumeric[rand.Intn(len(alphanumeric))] b[i] = alphanumeric[rand.Intn(len(alphanumeric))]