From e479bd5dc30ca109af490777f9643b68e61653b4 Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Mon, 15 Apr 2019 17:46:49 -0700 Subject: [PATCH 1/9] update to latest go-tfe same version number but pointing to a new sha Signed-off-by: Paul Thrasher --- backend/remote/backend_common.go | 54 ++++++++++++++++++++++++++++++++ backend/remote/backend_plan.go | 8 +++++ 2 files changed, 62 insertions(+) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 4e64830f2..623403a22 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -227,6 +227,60 @@ func (b *Remote) parseVariableValues(op *backend.Operation) (terraform.InputValu return result, diags } +func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { + if b.CLI != nil { + b.CLI.Output("\n------------------------------------------------------------------------\n") + } + var ce = r.CostEstimation + + logs, err := b.client.CostEstimation.Logs(stopCtx, ce.ID) + if err != nil { + return generalError("Failed to retrieve cost estimation logs", err) + } + scanner := bufio.NewScanner(logs) + + // Retrieve the cost estimation to get its current status. + ce, err := b.client.CostEstimation.Read(stopCtx, ce.ID) + if err != nil { + return generalError("Failed to retrieve cost estimation", err) + } + + var msgPrefix = "Cost estimation" + + if b.CLI != nil { + b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) + } + + for scanner.Scan() { + if b.CLI != nil { + b.CLI.Output(b.Colorize().Color(scanner.Text())) + } + } + if err := scanner.Err(); err != nil { + return generalError("Failed to read logs", err) + } + + switch ce.Status { + case tfe.CostEstimationFinished: + if r.HasChanges && op.Type == backend.OperationTypeApply || b.CLI != nil { + b.CLI.Output("\n------------------------------------------------------------------------") + } + return nil + case tfe.CostEstimationErrored: + return fmt.Errorf(msgPrefix + " errored.") + case tfe.CostEstimationCanceled: + return fmt.Errorf(msgPrefix + " canceled.") + default: + return fmt.Errorf("Unknown or unexpected cost estimation state: %s", ce.Status) + } + + if b.CLI != nil { + b.CLI.Output("------------------------------------------------------------------------") + } + + return nil +} + func (b *Remote) checkPolicy(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { if b.CLI != nil { b.CLI.Output("\n------------------------------------------------------------------------\n") diff --git a/backend/remote/backend_plan.go b/backend/remote/backend_plan.go index 200f7fd8d..07e7422d0 100644 --- a/backend/remote/backend_plan.go +++ b/backend/remote/backend_plan.go @@ -290,6 +290,14 @@ func (b *Remote) plan(stopCtx, cancelCtx context.Context, op *backend.Operation, return r, nil } + // Show Cost Estimation + if r.CostEstimations != nil { + err = b.costEstimation(stopCtx, cancelCtx, op, r) + if err != nil { + generalError("Cost Estimation error", err) + } + } + // Check any configured sentinel policies. if len(r.PolicyChecks) > 0 { err = b.checkPolicy(stopCtx, cancelCtx, op, r) From 53f977bee2044dbabef31931c0c0a9301344286e Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Tue, 23 Apr 2019 16:37:23 -0700 Subject: [PATCH 2/9] TFCE api correct pluralization Signed-off-by: Paul Thrasher --- backend/remote/backend_common.go | 4 ++-- backend/remote/backend_plan.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 623403a22..91a13b763 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -233,14 +233,14 @@ func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend. } var ce = r.CostEstimation - logs, err := b.client.CostEstimation.Logs(stopCtx, ce.ID) + logs, err := b.client.CostEstimations.Logs(stopCtx, ce.ID) if err != nil { return generalError("Failed to retrieve cost estimation logs", err) } scanner := bufio.NewScanner(logs) // Retrieve the cost estimation to get its current status. - ce, err := b.client.CostEstimation.Read(stopCtx, ce.ID) + ce, err = b.client.CostEstimations.Read(stopCtx, ce.ID) if err != nil { return generalError("Failed to retrieve cost estimation", err) } diff --git a/backend/remote/backend_plan.go b/backend/remote/backend_plan.go index 07e7422d0..662e28ef3 100644 --- a/backend/remote/backend_plan.go +++ b/backend/remote/backend_plan.go @@ -291,7 +291,7 @@ func (b *Remote) plan(stopCtx, cancelCtx context.Context, op *backend.Operation, } // Show Cost Estimation - if r.CostEstimations != nil { + if r.CostEstimation != nil { err = b.costEstimation(stopCtx, cancelCtx, op, r) if err != nil { generalError("Cost Estimation error", err) From aece05320b46acee87453efa9d2d4ab36b286108 Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Wed, 24 Apr 2019 16:13:59 -0700 Subject: [PATCH 3/9] update go-tfe to v0.3.16 Signed-off-by: Paul Thrasher --- go.mod | 2 +- go.sum | 8 +- .../github.com/hashicorp/go-tfe/apply_test.go | 65 ++ .../go-tfe/configuration_version_test.go | 195 +++++ .../hashicorp/go-tfe/cost_estimation.go | 121 ++++ .../hashicorp/go-tfe/cost_estimation_test.go | 92 +++ .../go-tfe/examples/organizations/main.go | 39 + .../go-tfe/examples/workspaces/main.go | 40 ++ .../hashicorp/go-tfe/helper_test.go | 666 ++++++++++++++++++ .../hashicorp/go-tfe/logreader_test.go | 253 +++++++ .../go-tfe/notification_configuration_test.go | 218 ++++++ .../hashicorp/go-tfe/oauth_client_test.go | 226 ++++++ .../hashicorp/go-tfe/oauth_token_test.go | 189 +++++ .../hashicorp/go-tfe/organization.go | 7 + .../hashicorp/go-tfe/organization_test.go | 367 ++++++++++ .../go-tfe/organization_token_test.go | 93 +++ .../github.com/hashicorp/go-tfe/plan_test.go | 66 ++ .../hashicorp/go-tfe/policy_check_test.go | 200 ++++++ .../hashicorp/go-tfe/policy_set_test.go | 454 ++++++++++++ .../hashicorp/go-tfe/policy_test.go | 393 +++++++++++ vendor/github.com/hashicorp/go-tfe/run.go | 7 +- .../github.com/hashicorp/go-tfe/run_test.go | 277 ++++++++ .../hashicorp/go-tfe/ssh_key_test.go | 219 ++++++ .../hashicorp/go-tfe/state_version_test.go | 350 +++++++++ .../hashicorp/go-tfe/team_access_test.go | 214 ++++++ .../hashicorp/go-tfe/team_member_test.go | 136 ++++ .../github.com/hashicorp/go-tfe/team_test.go | 216 ++++++ .../hashicorp/go-tfe/team_token_test.go | 92 +++ .../go-tfe/test-fixtures/archive-dir/bar.txt | 1 + .../go-tfe/test-fixtures/archive-dir/exe | 0 .../go-tfe/test-fixtures/archive-dir/foo.txt | 1 + .../test-fixtures/archive-dir/sub/foo.txt | 1 + .../test-fixtures/archive-dir/sub/zip.txt | 1 + .../test-fixtures/config-version/main.tf | 1 + vendor/github.com/hashicorp/go-tfe/tfe.go | 4 +- .../github.com/hashicorp/go-tfe/tfe_test.go | 398 +++++++++++ .../github.com/hashicorp/go-tfe/user_test.go | 79 +++ .../hashicorp/go-tfe/variable_test.go | 295 ++++++++ .../hashicorp/go-tfe/workspace_test.go | 496 +++++++++++++ 39 files changed, 6477 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/hashicorp/go-tfe/apply_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/configuration_version_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/cost_estimation.go create mode 100644 vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go create mode 100644 vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go create mode 100644 vendor/github.com/hashicorp/go-tfe/helper_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/logreader_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/oauth_client_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/oauth_token_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/organization_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/organization_token_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/plan_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/policy_check_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/policy_set_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/policy_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/run_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/ssh_key_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/state_version_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/team_access_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/team_member_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/team_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/team_token_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt create mode 100755 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe create mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt create mode 120000 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt create mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt create mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf create mode 100644 vendor/github.com/hashicorp/go-tfe/tfe_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/user_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/variable_test.go create mode 100644 vendor/github.com/hashicorp/go-tfe/workspace_test.go diff --git a/go.mod b/go.mod index aa4164674..a0010ee9b 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.5.2 github.com/hashicorp/go-rootcerts v1.0.0 github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect - github.com/hashicorp/go-tfe v0.3.14 + github.com/hashicorp/go-tfe v0.3.16 github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/go-version v1.1.0 github.com/hashicorp/golang-lru v0.5.0 // indirect diff --git a/go.sum b/go.sum index 97548c7e3..3c46c122f 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,12 @@ github.com/hashicorp/go-slug v0.3.0 h1:L0c+AvH/J64iMNF4VqRaRku2DMTEuHioPVS7kMjWI github.com/hashicorp/go-slug v0.3.0/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.3.14 h1:1eWmq4RAICGufydNUWu7ahb0gtq24pN9jatD2FkdxdE= -github.com/hashicorp/go-tfe v0.3.14/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM= +github.com/hashicorp/go-tfe v0.3.4 h1:A9pKjZMDTSGozXf2wQlWhBI7QoxCoas14Xg/TSiEAV8= +github.com/hashicorp/go-tfe v0.3.4/go.mod h1:Vssg8/lwVz+PyJ/nAK97zYmXxxLe28MCIMhKo+rva1o= +github.com/hashicorp/go-tfe v0.3.15-0.20190415182703-eb3b9edefdda h1:qWY67Uh98jh4BMYSnLVIXsz2AF1hRiHG4k43wzn1NoI= +github.com/hashicorp/go-tfe v0.3.15-0.20190415182703-eb3b9edefdda/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM= +github.com/hashicorp/go-tfe v0.3.16 h1:GS2yv580p0co4j3FBVaC6Zahd9mxdCGehhJ0qqzFMH0= +github.com/hashicorp/go-tfe v0.3.16/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= 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= diff --git a/vendor/github.com/hashicorp/go-tfe/apply_test.go b/vendor/github.com/hashicorp/go-tfe/apply_test.go new file mode 100644 index 000000000..bdc4b5d4a --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/apply_test.go @@ -0,0 +1,65 @@ +package tfe + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAppliesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createAppliedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the plan exists", func(t *testing.T) { + a, err := client.Applies.Read(ctx, rTest.Apply.ID) + require.NoError(t, err) + assert.NotEmpty(t, a.LogReadURL) + assert.Equal(t, a.Status, ApplyFinished) + assert.NotEmpty(t, a.StatusTimestamps) + }) + + t.Run("when the apply does not exist", func(t *testing.T) { + a, err := client.Applies.Read(ctx, "nonexisting") + assert.Nil(t, a) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid apply ID", func(t *testing.T) { + a, err := client.Applies.Read(ctx, badIdentifier) + assert.Nil(t, a) + assert.EqualError(t, err, "invalid value for apply ID") + }) +} + +func TestAppliesLogs(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createAppliedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the log exists", func(t *testing.T) { + a, err := client.Applies.Read(ctx, rTest.Apply.ID) + require.NoError(t, err) + + logReader, err := client.Applies.Logs(ctx, a.ID) + require.NoError(t, err) + + logs, err := ioutil.ReadAll(logReader) + require.NoError(t, err) + + assert.Contains(t, string(logs), "1 added, 0 changed, 0 destroyed") + }) + + t.Run("when the log does not exist", func(t *testing.T) { + logs, err := client.Applies.Logs(ctx, "nonexisting") + assert.Nil(t, logs) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go b/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go new file mode 100644 index 000000000..e2766d516 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go @@ -0,0 +1,195 @@ +package tfe + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfigurationVersionsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + cvTest1, cvTest1Cleanup := createConfigurationVersion(t, client, wTest) + defer cvTest1Cleanup() + cvTest2, cvTest2Cleanup := createConfigurationVersion(t, client, wTest) + defer cvTest2Cleanup() + + t.Run("without list options", func(t *testing.T) { + options := ConfigurationVersionListOptions{} + + cvl, err := client.ConfigurationVersions.List(ctx, wTest.ID, options) + require.NoError(t, err) + + // We need to strip the upload URL as that is a dynamic link. + cvTest1.UploadURL = "" + cvTest2.UploadURL = "" + + // And for the retrieved configuration versions as well. + for _, cv := range cvl.Items { + cv.UploadURL = "" + } + + assert.Contains(t, cvl.Items, cvTest1) + assert.Contains(t, cvl.Items, cvTest2) + assert.Equal(t, 1, cvl.CurrentPage) + assert.Equal(t, 2, cvl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + options := ConfigurationVersionListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + } + + cvl, err := client.ConfigurationVersions.List(ctx, wTest.ID, options) + require.NoError(t, err) + assert.Empty(t, cvl.Items) + assert.Equal(t, 999, cvl.CurrentPage) + assert.Equal(t, 2, cvl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + options := ConfigurationVersionListOptions{} + + cvl, err := client.ConfigurationVersions.List(ctx, badIdentifier, options) + assert.Nil(t, cvl) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestConfigurationVersionsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + cv, err := client.ConfigurationVersions.Create(ctx, + wTest.ID, + ConfigurationVersionCreateOptions{}, + ) + require.NoError(t, err) + + // Get a refreshed view of the configuration version. + refreshed, err := client.ConfigurationVersions.Read(ctx, cv.ID) + require.NoError(t, err) + + for _, item := range []*ConfigurationVersion{ + cv, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Empty(t, item.Error) + assert.Equal(t, item.Source, ConfigurationSourceAPI) + assert.Equal(t, item.Status, ConfigurationPending) + assert.NotEmpty(t, item.UploadURL) + } + }) + + t.Run("with invalid workspace id", func(t *testing.T) { + cv, err := client.ConfigurationVersions.Create( + ctx, + badIdentifier, + ConfigurationVersionCreateOptions{}, + ) + assert.Nil(t, cv) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestConfigurationVersionsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + cvTest, cvTestCleanup := createConfigurationVersion(t, client, nil) + defer cvTestCleanup() + + t.Run("when the configuration version exists", func(t *testing.T) { + cv, err := client.ConfigurationVersions.Read(ctx, cvTest.ID) + require.NoError(t, err) + + // Don't compare the UploadURL because it will be generated twice in + // this test - once at creation of the configuration version, and + // again during the GET. + cvTest.UploadURL, cv.UploadURL = "", "" + + assert.Equal(t, cvTest, cv) + }) + + t.Run("when the configuration version does not exist", func(t *testing.T) { + cv, err := client.ConfigurationVersions.Read(ctx, "nonexisting") + assert.Nil(t, cv) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid configuration version id", func(t *testing.T) { + cv, err := client.ConfigurationVersions.Read(ctx, badIdentifier) + assert.Nil(t, cv) + assert.EqualError(t, err, "invalid value for configuration version ID") + }) +} + +func TestConfigurationVersionsUpload(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + cv, cvCleanup := createConfigurationVersion(t, client, nil) + defer cvCleanup() + + t.Run("with valid options", func(t *testing.T) { + err := client.ConfigurationVersions.Upload( + ctx, + cv.UploadURL, + "test-fixtures/config-version", + ) + require.NoError(t, err) + + // We do this is a small loop, because it can take a second + // before the upload is finished. + for i := 0; ; i++ { + refreshed, err := client.ConfigurationVersions.Read(ctx, cv.ID) + require.NoError(t, err) + + if refreshed.Status == ConfigurationUploaded { + break + } + + if i > 10 { + t.Fatal("Timeout waiting for the configuration version to be uploaded") + } + + time.Sleep(1 * time.Second) + } + }) + + t.Run("without a valid upload URL", func(t *testing.T) { + err := client.ConfigurationVersions.Upload( + ctx, + cv.UploadURL[:len(cv.UploadURL)-10]+"nonexisting", + "test-fixtures/config-version", + ) + assert.Error(t, err) + }) + + t.Run("without a valid path", func(t *testing.T) { + err := client.ConfigurationVersions.Upload( + ctx, + cv.UploadURL, + "nonexisting", + ) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/cost_estimation.go b/vendor/github.com/hashicorp/go-tfe/cost_estimation.go new file mode 100644 index 000000000..acb038554 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/cost_estimation.go @@ -0,0 +1,121 @@ +package tfe + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/url" + "time" +) + +// Compile-time proof of interface implementation. +var _ CostEstimations = (*costEstimations)(nil) + +// CostEstimations describes all the costEstimation related methods that +// the Terraform Enterprise API supports. +// +// TFE API docs: https://www.terraform.io/docs/enterprise/api/ (TBD) +type CostEstimations interface { + // Read a costEstimation by its ID. + Read(ctx context.Context, costEstimationID string) (*CostEstimation, error) + + // Logs retrieves the logs of a costEstimation. + Logs(ctx context.Context, costEstimationID string) (io.Reader, error) +} + +// costEstimations implements CostEstimations. +type costEstimations struct { + client *Client +} + +// CostEstimationStatus represents a costEstimation state. +type CostEstimationStatus string + +//List all available costEstimation statuses. +const ( + CostEstimationCanceled CostEstimationStatus = "canceled" + CostEstimationErrored CostEstimationStatus = "errored" + CostEstimationFinished CostEstimationStatus = "finished" + CostEstimationQueued CostEstimationStatus = "queued" +) + +// CostEstimation represents a Terraform Enterprise costEstimation. +type CostEstimation struct { + ID string `jsonapi:"primary,cost-estimations"` + ErrorMessage string `jsonapi:"attr,error-message"` + Status CostEstimationStatus `jsonapi:"attr,status"` + StatusTimestamps *CostEstimationStatusTimestamps `jsonapi:"attr,status-timestamps"` +} + +// CostEstimationStatusTimestamps holds the timestamps for individual costEstimation statuses. +type CostEstimationStatusTimestamps struct { + CanceledAt time.Time `json:"canceled-at"` + ErroredAt time.Time `json:"errored-at"` + FinishedAt time.Time `json:"finished-at"` + QueuedAt time.Time `json:"queued-at"` +} + +// Read a costEstimation by its ID. +func (s *costEstimations) Read(ctx context.Context, costEstimationID string) (*CostEstimation, error) { + if !validStringID(&costEstimationID) { + return nil, errors.New("invalid value for cost estimation ID") + } + + u := fmt.Sprintf("cost-estimations/%s", url.QueryEscape(costEstimationID)) + req, err := s.client.newRequest("GET", u, nil) + if err != nil { + return nil, err + } + + ce := &CostEstimation{} + err = s.client.do(ctx, req, ce) + if err != nil { + return nil, err + } + + return ce, nil +} + +// Logs retrieves the logs of a costEstimation. +func (s *costEstimations) Logs(ctx context.Context, costEstimationID string) (io.Reader, error) { + if !validStringID(&costEstimationID) { + return nil, errors.New("invalid value for cost estimation ID") + } + + // Loop until the context is canceled or the cost estimation is finished + // running. The cost estimation logs are not streamed and so only available + // once the estimation is finished. + for { + // Get the costEstimation to make sure it exists. + ce, err := s.Read(ctx, costEstimationID) + if err != nil { + return nil, err + } + + switch ce.Status { + case CostEstimationQueued: + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(500 * time.Millisecond): + continue + } + } + + u := fmt.Sprintf("cost-estimations/%s/output", url.QueryEscape(costEstimationID)) + req, err := s.client.newRequest("GET", u, nil) + if err != nil { + return nil, err + } + + logs := bytes.NewBuffer(nil) + err = s.client.do(ctx, req, logs) + if err != nil { + return nil, err + } + + return logs, nil + } +} diff --git a/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go b/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go new file mode 100644 index 000000000..1c89aa7c3 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go @@ -0,0 +1,92 @@ +package tfe + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCostEstimationsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + // Enable cost estimation for the test organization. + orgTest, err := client.Organizations.Update( + ctx, + orgTest.Name, + OrganizationUpdateOptions{ + CostEstimationEnabled: Bool(true), + }, + ) + require.NoError(t, err) + + wTest, _ := createWorkspace(t, client, orgTest) + rTest, _ := createPlannedRun(t, client, wTest) + + t.Run("when the costEstimation exists", func(t *testing.T) { + ce, err := client.CostEstimations.Read(ctx, rTest.CostEstimation.ID) + require.NoError(t, err) + assert.Equal(t, ce.Status, CostEstimationFinished) + assert.NotEmpty(t, ce.StatusTimestamps) + }) + + t.Run("when the costEstimation does not exist", func(t *testing.T) { + ce, err := client.CostEstimations.Read(ctx, "nonexisting") + assert.Nil(t, ce) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("with invalid costEstimation ID", func(t *testing.T) { + ce, err := client.CostEstimations.Read(ctx, badIdentifier) + assert.Nil(t, ce) + assert.EqualError(t, err, "invalid value for cost estimation ID") + }) +} + +func TestCostEstimationsLogs(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + // Enable cost estimation for the test organization. + orgTest, err := client.Organizations.Update( + ctx, + orgTest.Name, + OrganizationUpdateOptions{ + CostEstimationEnabled: Bool(true), + }, + ) + require.NoError(t, err) + + wTest, _ := createWorkspace(t, client, orgTest) + rTest, _ := createPlannedRun(t, client, wTest) + + t.Run("when the log exists", func(t *testing.T) { + ce, err := client.CostEstimations.Read(ctx, rTest.CostEstimation.ID) + require.NoError(t, err) + + logReader, err := client.CostEstimations.Logs(ctx, ce.ID) + require.NotNil(t, logReader) + require.NoError(t, err) + + logs, err := ioutil.ReadAll(logReader) + require.NoError(t, err) + + t.Skip("log output is likely to change") + assert.Contains(t, string(logs), "SKU") + }) + + t.Run("when the log does not exist", func(t *testing.T) { + logs, err := client.CostEstimations.Logs(ctx, "nonexisting") + assert.Nil(t, logs) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go b/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go new file mode 100644 index 000000000..96fc95b31 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "log" + + tfe "github.com/hashicorp/go-tfe" +) + +func main() { + config := &tfe.Config{ + Token: "insert-your-token-here", + } + + client, err := tfe.NewClient(config) + if err != nil { + log.Fatal(err) + } + + // Create a context + ctx := context.Background() + + // Create a new organization + options := tfe.OrganizationCreateOptions{ + Name: tfe.String("example"), + Email: tfe.String("info@example.com"), + } + + org, err := client.Organizations.Create(ctx, options) + if err != nil { + log.Fatal(err) + } + + // Delete an organization + err = client.Organizations.Delete(ctx, org.Name) + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go b/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go new file mode 100644 index 000000000..a809c0bc0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "log" + + tfe "github.com/hashicorp/go-tfe" +) + +func main() { + config := &tfe.Config{ + Token: "insert-your-token-here", + } + + client, err := tfe.NewClient(config) + if err != nil { + log.Fatal(err) + } + + // Create a context + ctx := context.Background() + + // Create a new workspace + w, err := client.Workspaces.Create(ctx, "org-name", tfe.WorkspaceCreateOptions{ + Name: tfe.String("my-app-tst"), + }) + if err != nil { + log.Fatal(err) + } + + // Update the workspace + w, err = client.Workspaces.Update(ctx, "org-name", w.Name, tfe.WorkspaceUpdateOptions{ + AutoApply: tfe.Bool(false), + TerraformVersion: tfe.String("0.11.1"), + WorkingDirectory: tfe.String("my-app/infra"), + }) + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/hashicorp/go-tfe/helper_test.go b/vendor/github.com/hashicorp/go-tfe/helper_test.go new file mode 100644 index 000000000..83a8a2483 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/helper_test.go @@ -0,0 +1,666 @@ +package tfe + +import ( + "context" + "crypto/md5" + "encoding/base64" + "fmt" + "io/ioutil" + "os" + "testing" + "time" + + "github.com/hashicorp/go-uuid" +) + +const badIdentifier = "! / nope" + +func testClient(t *testing.T) *Client { + client, err := NewClient(nil) + if err != nil { + t.Fatal(err) + } + + return client +} + +func createConfigurationVersion(t *testing.T, client *Client, w *Workspace) (*ConfigurationVersion, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + ctx := context.Background() + cv, err := client.ConfigurationVersions.Create( + ctx, + w.ID, + ConfigurationVersionCreateOptions{AutoQueueRuns: Bool(false)}, + ) + if err != nil { + t.Fatal(err) + } + + return cv, func() { + if wCleanup != nil { + wCleanup() + } + } +} + +func createUploadedConfigurationVersion(t *testing.T, client *Client, w *Workspace) (*ConfigurationVersion, func()) { + cv, cvCleanup := createConfigurationVersion(t, client, w) + + ctx := context.Background() + err := client.ConfigurationVersions.Upload(ctx, cv.UploadURL, "test-fixtures/config-version") + if err != nil { + cvCleanup() + t.Fatal(err) + } + + for i := 0; ; i++ { + cv, err = client.ConfigurationVersions.Read(ctx, cv.ID) + if err != nil { + cvCleanup() + t.Fatal(err) + } + + if cv.Status == ConfigurationUploaded { + break + } + + if i > 10 { + cvCleanup() + t.Fatal("Timeout waiting for the configuration version to be uploaded") + } + + time.Sleep(1 * time.Second) + } + + return cv, cvCleanup +} + +func createNotificationConfiguration(t *testing.T, client *Client, w *Workspace) (*NotificationConfiguration, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + ctx := context.Background() + nc, err := client.NotificationConfigurations.Create( + ctx, + w.ID, + NotificationConfigurationCreateOptions{ + DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), + Enabled: Bool(false), + Name: String(randomString(t)), + Token: String(randomString(t)), + URL: String("http://example.com"), + Triggers: []string{NotificationTriggerCreated}, + }, + ) + if err != nil { + t.Fatal(err) + } + + return nc, func() { + if err := client.NotificationConfigurations.Delete(ctx, nc.ID); err != nil { + t.Errorf("Error destroying notification configuration! WARNING: Dangling\n"+ + "resources may exist! The full error is shown below.\n\n"+ + "NotificationConfiguration: %s\nError: %s", nc.ID, err) + } + + if wCleanup != nil { + wCleanup() + } + } +} + +func createPolicySet(t *testing.T, client *Client, org *Organization, policies []*Policy, workspaces []*Workspace) (*PolicySet, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + ps, err := client.PolicySets.Create(ctx, org.Name, PolicySetCreateOptions{ + Name: String(randomString(t)), + Policies: policies, + Workspaces: workspaces, + }) + if err != nil { + t.Fatal(err) + } + + return ps, func() { + if err := client.PolicySets.Delete(ctx, ps.ID); err != nil { + t.Errorf("Error destroying policy set! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "PolicySet: %s\nError: %s", ps.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createPolicy(t *testing.T, client *Client, org *Organization) (*Policy, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + name := randomString(t) + options := PolicyCreateOptions{ + Name: String(name), + Enforce: []*EnforcementOptions{ + { + Path: String(name + ".sentinel"), + Mode: EnforcementMode(EnforcementSoft), + }, + }, + } + + ctx := context.Background() + p, err := client.Policies.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + return p, func() { + if err := client.Policies.Delete(ctx, p.ID); err != nil { + t.Errorf("Error destroying policy! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Policy: %s\nError: %s", p.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createUploadedPolicy(t *testing.T, client *Client, pass bool, org *Organization) (*Policy, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + p, pCleanup := createPolicy(t, client, org) + + ctx := context.Background() + err := client.Policies.Upload(ctx, p.ID, []byte(fmt.Sprintf("main = rule { %t }", pass))) + if err != nil { + t.Fatal(err) + } + + p, err = client.Policies.Read(ctx, p.ID) + if err != nil { + t.Fatal(err) + } + + return p, func() { + pCleanup() + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createOAuthClient(t *testing.T, client *Client, org *Organization) (*OAuthClient, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + githubToken := os.Getenv("GITHUB_TOKEN") + if githubToken == "" { + t.Fatal("Export a valid GITHUB_TOKEN before running this test!") + } + + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + HTTPURL: String("https://github.com"), + OAuthToken: String(githubToken), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + ctx := context.Background() + oc, err := client.OAuthClients.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + // This currently panics as the token will not be there when the client is + // created. To get a token, the client needs to be connected through the UI + // first. So the test using this (TestOAuthTokensList) is currently disabled. + return oc, func() { + if err := client.OAuthClients.Delete(ctx, oc.ID); err != nil { + t.Errorf("Error destroying OAuth client! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "OAuthClient: %s\nError: %s", oc.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createOAuthToken(t *testing.T, client *Client, org *Organization) (*OAuthToken, func()) { + ocTest, ocTestCleanup := createOAuthClient(t, client, org) + return ocTest.OAuthTokens[0], ocTestCleanup +} + +func createOrganization(t *testing.T, client *Client) (*Organization, func()) { + ctx := context.Background() + org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ + Name: String("tst-" + randomString(t)), + Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), + }) + if err != nil { + t.Fatal(err) + } + + return org, func() { + if err := client.Organizations.Delete(ctx, org.Name); err != nil { + t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Organization: %s\nError: %s", org.Name, err) + } + } +} + +func createOrganizationToken(t *testing.T, client *Client, org *Organization) (*OrganizationToken, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + tk, err := client.OrganizationTokens.Generate(ctx, org.Name) + if err != nil { + t.Fatal(err) + } + + return tk, func() { + if err := client.OrganizationTokens.Delete(ctx, org.Name); err != nil { + t.Errorf("Error destroying organization token! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "OrganizationToken: %s\nError: %s", tk.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + cv, cvCleanup := createUploadedConfigurationVersion(t, client, w) + + ctx := context.Background() + r, err := client.Runs.Create(ctx, RunCreateOptions{ + ConfigurationVersion: cv, + Workspace: w, + }) + if err != nil { + t.Fatal(err) + } + + return r, func() { + if wCleanup != nil { + wCleanup() + } else { + cvCleanup() + } + } +} + +func createPlannedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + r, rCleanup := createRun(t, client, w) + + var err error + ctx := context.Background() + for i := 0; ; i++ { + r, err = client.Runs.Read(ctx, r.ID) + if err != nil { + t.Fatal(err) + } + + switch r.Status { + case RunPlanned, RunCostEstimated, RunPolicyChecked, RunPolicyOverride: + return r, rCleanup + } + + if i > 45 { + rCleanup() + t.Fatal("Timeout waiting for run to be applied") + } + + time.Sleep(1 * time.Second) + } +} + +func createAppliedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + r, rCleanup := createPlannedRun(t, client, w) + ctx := context.Background() + + err := client.Runs.Apply(ctx, r.ID, RunApplyOptions{}) + if err != nil { + t.Fatal(err) + } + + for i := 0; ; i++ { + r, err = client.Runs.Read(ctx, r.ID) + if err != nil { + t.Fatal(err) + } + + if r.Status == RunApplied { + return r, rCleanup + } + + if i > 45 { + rCleanup() + t.Fatal("Timeout waiting for run to be applied") + } + + time.Sleep(1 * time.Second) + } +} + +func createSSHKey(t *testing.T, client *Client, org *Organization) (*SSHKey, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + key, err := client.SSHKeys.Create(ctx, org.Name, SSHKeyCreateOptions{ + Name: String(randomString(t)), + Value: String(randomString(t)), + }) + if err != nil { + t.Fatal(err) + } + + return key, func() { + if err := client.SSHKeys.Delete(ctx, key.ID); err != nil { + t.Errorf("Error destroying SSH key! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "SSHKey: %s\nError: %s", key.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createStateVersion(t *testing.T, client *Client, serial int64, w *Workspace) (*StateVersion, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + state, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + _, err = client.Workspaces.Lock(ctx, w.ID, WorkspaceLockOptions{}) + if err != nil { + t.Fatal(err) + } + defer func() { + _, err := client.Workspaces.Unlock(ctx, w.ID) + if err != nil { + t.Fatal(err) + } + }() + + sv, err := client.StateVersions.Create(ctx, w.ID, StateVersionCreateOptions{ + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(serial), + State: String(base64.StdEncoding.EncodeToString(state)), + }) + if err != nil { + t.Fatal(err) + } + + return sv, func() { + // There currently isn't a way to delete a state, so we + // can only cleanup by deleting the workspace. + if wCleanup != nil { + wCleanup() + } + } +} + +func createTeam(t *testing.T, client *Client, org *Organization) (*Team, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + tm, err := client.Teams.Create(ctx, org.Name, TeamCreateOptions{ + Name: String(randomString(t)), + OrganizationAccess: &OrganizationAccessOptions{ManagePolicies: Bool(true)}, + }) + if err != nil { + t.Fatal(err) + } + + return tm, func() { + if err := client.Teams.Delete(ctx, tm.ID); err != nil { + t.Errorf("Error destroying team! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Team: %s\nError: %s", tm.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createTeamAccess(t *testing.T, client *Client, tm *Team, w *Workspace, org *Organization) (*TeamAccess, func()) { + var orgCleanup, tmCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + if tm == nil { + tm, tmCleanup = createTeam(t, client, org) + } + + if w == nil { + w, _ = createWorkspace(t, client, org) + } + + ctx := context.Background() + ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ + Access: Access(AccessAdmin), + Team: tm, + Workspace: w, + }) + if err != nil { + t.Fatal(err) + } + + return ta, func() { + if err := client.TeamAccess.Remove(ctx, ta.ID); err != nil { + t.Errorf("Error removing team access! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "TeamAccess: %s\nError: %s", ta.ID, err) + } + + if tmCleanup != nil { + tmCleanup() + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createTeamToken(t *testing.T, client *Client, tm *Team) (*TeamToken, func()) { + var tmCleanup func() + + if tm == nil { + tm, tmCleanup = createTeam(t, client, nil) + } + + ctx := context.Background() + tt, err := client.TeamTokens.Generate(ctx, tm.ID) + if err != nil { + t.Fatal(err) + } + + return tt, func() { + if err := client.TeamTokens.Delete(ctx, tm.ID); err != nil { + t.Errorf("Error destroying team token! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "TeamToken: %s\nError: %s", tm.ID, err) + } + + if tmCleanup != nil { + tmCleanup() + } + } +} + +func createVariable(t *testing.T, client *Client, w *Workspace) (*Variable, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + ctx := context.Background() + v, err := client.Variables.Create(ctx, VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Workspace: w, + }) + if err != nil { + t.Fatal(err) + } + + return v, func() { + if err := client.Variables.Delete(ctx, v.ID); err != nil { + t.Errorf("Error destroying variable! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Variable: %s\nError: %s", v.Key, err) + } + + if wCleanup != nil { + wCleanup() + } + } +} + +func createWorkspace(t *testing.T, client *Client, org *Organization) (*Workspace, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + w, err := client.Workspaces.Create(ctx, org.Name, WorkspaceCreateOptions{ + Name: String(randomString(t)), + }) + if err != nil { + t.Fatal(err) + } + + return w, func() { + if err := client.Workspaces.Delete(ctx, org.Name, w.Name); err != nil { + t.Errorf("Error destroying workspace! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Workspace: %s\nError: %s", w.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization) (*Workspace, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + oc, ocCleanup := createOAuthToken(t, client, org) + + githubIdentifier := os.Getenv("GITHUB_IDENTIFIER") + if githubIdentifier == "" { + t.Fatal("Export a valid GITHUB_IDENTIFIER before running this test!") + } + + options := WorkspaceCreateOptions{ + Name: String(randomString(t)), + VCSRepo: &VCSRepoOptions{ + Identifier: String(githubIdentifier), + OAuthTokenID: String(oc.ID), + }, + } + + ctx := context.Background() + w, err := client.Workspaces.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + return w, func() { + if err := client.Workspaces.Delete(ctx, org.Name, w.Name); err != nil { + t.Errorf("Error destroying workspace! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Workspace: %s\nError: %s", w.Name, err) + } + + if ocCleanup != nil { + ocCleanup() + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func randomString(t *testing.T) string { + v, err := uuid.GenerateUUID() + if err != nil { + t.Fatal(err) + } + return v +} diff --git a/vendor/github.com/hashicorp/go-tfe/logreader_test.go b/vendor/github.com/hashicorp/go-tfe/logreader_test.go new file mode 100644 index 000000000..e240937b6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/logreader_test.go @@ -0,0 +1,253 @@ +package tfe + +import ( + "context" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func testLogReader(t *testing.T, h http.HandlerFunc) (*httptest.Server, *LogReader) { + ts := httptest.NewServer(h) + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + HTTPClient: ts.Client(), + } + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + logURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + + lr := &LogReader{ + client: client, + ctx: context.Background(), + logURL: logURL, + } + + return ts, lr +} + +func TestLogReader_withMarkersSingle(t *testing.T) { + t.Parallel() + + logReads := 0 + ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logReads++ + switch { + case logReads == 2: + w.Write([]byte("\x02Terraform run started - logs - Terraform run finished\x03")) + } + })) + defer ts.Close() + + doneReads := 0 + lr.done = func() (bool, error) { + doneReads++ + if logReads >= 2 { + return true, nil + } + return false, nil + } + + logs, err := ioutil.ReadAll(lr) + if err != nil { + t.Fatal(err) + } + + expected := "Terraform run started - logs - Terraform run finished" + if string(logs) != expected { + t.Fatalf("expected %s, got: %s", expected, string(logs)) + } + if doneReads != 1 { + t.Fatalf("expected 1 done reads, got %d reads", doneReads) + } + if logReads != 3 { + t.Fatalf("expected 3 log reads, got %d reads", logReads) + } +} + +func TestLogReader_withMarkersDouble(t *testing.T) { + t.Parallel() + + logReads := 0 + ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logReads++ + switch { + case logReads == 2: + w.Write([]byte("\x02Terraform run started")) + case logReads == 3: + w.Write([]byte(" - logs - Terraform run finished\x03")) + } + })) + defer ts.Close() + + doneReads := 0 + lr.done = func() (bool, error) { + doneReads++ + if logReads >= 3 { + return true, nil + } + return false, nil + } + + logs, err := ioutil.ReadAll(lr) + if err != nil { + t.Fatal(err) + } + + expected := "Terraform run started - logs - Terraform run finished" + if string(logs) != expected { + t.Fatalf("expected %s, got: %s", expected, string(logs)) + } + if doneReads != 1 { + t.Fatalf("expected 1 done reads, got %d reads", doneReads) + } + if logReads != 4 { + t.Fatalf("expected 4 log reads, got %d reads", logReads) + } +} + +func TestLogReader_withMarkersMulti(t *testing.T) { + t.Parallel() + + logReads := 0 + ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logReads++ + switch { + case logReads == 2: + w.Write([]byte("\x02")) + case logReads == 3: + w.Write([]byte("Terraform run started")) + case logReads == 16: + w.Write([]byte(" - logs - ")) + case logReads == 30: + w.Write([]byte("Terraform run finished")) + case logReads == 31: + w.Write([]byte("\x03")) + } + })) + defer ts.Close() + + doneReads := 0 + lr.done = func() (bool, error) { + doneReads++ + if logReads >= 31 { + return true, nil + } + return false, nil + } + + logs, err := ioutil.ReadAll(lr) + if err != nil { + t.Fatal(err) + } + + expected := "Terraform run started - logs - Terraform run finished" + if string(logs) != expected { + t.Fatalf("expected %s, got: %s", expected, string(logs)) + } + if doneReads != 3 { + t.Fatalf("expected 3 done reads, got %d reads", doneReads) + } + if logReads != 31 { + t.Fatalf("expected 31 log reads, got %d reads", logReads) + } +} + +func TestLogReader_withoutMarkers(t *testing.T) { + t.Parallel() + + logReads := 0 + ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logReads++ + switch { + case logReads == 2: + w.Write([]byte("Terraform run started")) + case logReads == 16: + w.Write([]byte(" - logs - ")) + case logReads == 31: + w.Write([]byte("Terraform run finished")) + } + })) + defer ts.Close() + + doneReads := 0 + lr.done = func() (bool, error) { + doneReads++ + if logReads >= 31 { + return true, nil + } + return false, nil + } + + logs, err := ioutil.ReadAll(lr) + if err != nil { + t.Fatal(err) + } + + expected := "Terraform run started - logs - Terraform run finished" + if string(logs) != expected { + t.Fatalf("expected %s, got: %s", expected, string(logs)) + } + if doneReads != 25 { + t.Fatalf("expected 14 done reads, got %d reads", doneReads) + } + if logReads != 32 { + t.Fatalf("expected 32 log reads, got %d reads", logReads) + } +} + +func TestLogReader_withoutEndOfTextMarker(t *testing.T) { + t.Parallel() + + logReads := 0 + ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logReads++ + switch { + case logReads == 2: + w.Write([]byte("\x02")) + case logReads == 3: + w.Write([]byte("Terraform run started")) + case logReads == 16: + w.Write([]byte(" - logs - ")) + case logReads == 31: + w.Write([]byte("Terraform run finished")) + } + })) + defer ts.Close() + + doneReads := 0 + lr.done = func() (bool, error) { + doneReads++ + if logReads >= 31 { + return true, nil + } + return false, nil + } + + logs, err := ioutil.ReadAll(lr) + if err != nil { + t.Fatal(err) + } + + expected := "Terraform run started - logs - Terraform run finished" + if string(logs) != expected { + t.Fatalf("expected %s, got: %s", expected, string(logs)) + } + if doneReads != 3 { + t.Fatalf("expected 3 done reads, got %d reads", doneReads) + } + if logReads != 42 { + t.Fatalf("expected 42 log reads, got %d reads", logReads) + } +} diff --git a/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go b/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go new file mode 100644 index 000000000..b0ecf51e8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go @@ -0,0 +1,218 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNotificationConfigurationList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + ncTest1, _ := createNotificationConfiguration(t, client, wTest) + ncTest2, _ := createNotificationConfiguration(t, client, wTest) + + t.Run("with a valid workspace", func(t *testing.T) { + ncl, err := client.NotificationConfigurations.List( + ctx, + wTest.ID, + NotificationConfigurationListOptions{}, + ) + require.NoError(t, err) + assert.Contains(t, ncl.Items, ncTest1) + assert.Contains(t, ncl.Items, ncTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, ncl.CurrentPage) + assert.Equal(t, 2, ncl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + ncl, err := client.NotificationConfigurations.List( + ctx, + wTest.ID, + NotificationConfigurationListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }, + ) + require.NoError(t, err) + assert.Empty(t, ncl.Items) + assert.Equal(t, 999, ncl.CurrentPage) + assert.Equal(t, 2, ncl.TotalCount) + }) + + t.Run("without a valid workspace", func(t *testing.T) { + ncl, err := client.NotificationConfigurations.List( + ctx, + badIdentifier, + NotificationConfigurationListOptions{}, + ) + assert.Nil(t, ncl) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestNotificationConfigurationCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + t.Run("with all required values", func(t *testing.T) { + options := NotificationConfigurationCreateOptions{ + DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), + Enabled: Bool(false), + Name: String(randomString(t)), + Token: String(randomString(t)), + URL: String("http://example.com"), + Triggers: []string{NotificationTriggerCreated}, + } + + _, err := client.NotificationConfigurations.Create(ctx, wTest.ID, options) + require.NoError(t, err) + }) + + t.Run("without a required value", func(t *testing.T) { + options := NotificationConfigurationCreateOptions{ + DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), + Enabled: Bool(false), + Token: String(randomString(t)), + URL: String("http://example.com"), + Triggers: []string{NotificationTriggerCreated}, + } + + nc, err := client.NotificationConfigurations.Create(ctx, wTest.ID, options) + assert.Nil(t, nc) + assert.EqualError(t, err, "name is required") + }) + + t.Run("without a valid workspace", func(t *testing.T) { + nc, err := client.NotificationConfigurations.Create(ctx, badIdentifier, NotificationConfigurationCreateOptions{}) + assert.Nil(t, nc) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestNotificationConfigurationRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) + defer ncTestCleanup() + + t.Run("with a valid ID", func(t *testing.T) { + nc, err := client.NotificationConfigurations.Read(ctx, ncTest.ID) + require.NoError(t, err) + assert.Equal(t, ncTest.ID, nc.ID) + }) + + t.Run("when the notification configuration does not exist", func(t *testing.T) { + _, err := client.NotificationConfigurations.Read(ctx, "nonexisting") + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the notification configuration ID is invalid", func(t *testing.T) { + _, err := client.NotificationConfigurations.Read(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for notification configuration ID") + }) +} + +func TestNotificationConfigurationUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) + defer ncTestCleanup() + + t.Run("with options", func(t *testing.T) { + options := NotificationConfigurationUpdateOptions{ + Enabled: Bool(true), + Name: String("newName"), + } + + nc, err := client.NotificationConfigurations.Update(ctx, ncTest.ID, options) + require.NoError(t, err) + assert.Equal(t, nc.Enabled, true) + assert.Equal(t, nc.Name, "newName") + }) + + t.Run("without options", func(t *testing.T) { + _, err := client.NotificationConfigurations.Update(ctx, ncTest.ID, NotificationConfigurationUpdateOptions{}) + require.NoError(t, err) + }) + + t.Run("when the notification configuration does not exist", func(t *testing.T) { + _, err := client.NotificationConfigurations.Update(ctx, "nonexisting", NotificationConfigurationUpdateOptions{}) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the notification configuration ID is invalid", func(t *testing.T) { + _, err := client.NotificationConfigurations.Update(ctx, badIdentifier, NotificationConfigurationUpdateOptions{}) + assert.EqualError(t, err, "invalid value for notification configuration ID") + }) +} + +func TestNotificationConfigurationDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + ncTest, _ := createNotificationConfiguration(t, client, wTest) + + t.Run("with a valid ID", func(t *testing.T) { + err := client.NotificationConfigurations.Delete(ctx, ncTest.ID) + require.NoError(t, err) + + _, err = client.NotificationConfigurations.Read(ctx, ncTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the notification configuration does not exist", func(t *testing.T) { + err := client.NotificationConfigurations.Delete(ctx, "nonexisting") + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the notification configuration ID is invalid", func(t *testing.T) { + err := client.NotificationConfigurations.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for notification configuration ID") + }) +} + +func TestNotificationConfigurationVerify(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) + defer ncTestCleanup() + + t.Run("with a valid ID", func(t *testing.T) { + _, err := client.NotificationConfigurations.Verify(ctx, ncTest.ID) + require.NoError(t, err) + }) + + t.Run("when the notification configuration does not exists", func(t *testing.T) { + _, err := client.NotificationConfigurations.Verify(ctx, "nonexisting") + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the notification configuration ID is invalid", func(t *testing.T) { + _, err := client.NotificationConfigurations.Verify(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for notification configuration ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go b/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go new file mode 100644 index 000000000..9478194b0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go @@ -0,0 +1,226 @@ +package tfe + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOAuthClientsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + ocTest1, _ := createOAuthClient(t, client, orgTest) + ocTest2, _ := createOAuthClient(t, client, orgTest) + + t.Run("without list options", func(t *testing.T) { + options := OAuthClientListOptions{} + + ocl, err := client.OAuthClients.List(ctx, orgTest.Name, options) + require.NoError(t, err) + + t.Run("the OAuth tokens relationship is decoded correcly", func(t *testing.T) { + for _, oc := range ocl.Items { + assert.Equal(t, 1, len(oc.OAuthTokens)) + } + }) + + // We need to strip some fields before the next test. + for _, oc := range append(ocl.Items, ocTest1, ocTest2) { + oc.OAuthTokens = nil + oc.Organization = nil + } + + assert.Contains(t, ocl.Items, ocTest1) + assert.Contains(t, ocl.Items, ocTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, ocl.CurrentPage) + assert.Equal(t, 2, ocl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + options := OAuthClientListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + } + + ocl, err := client.OAuthClients.List(ctx, orgTest.Name, options) + require.NoError(t, err) + assert.Empty(t, ocl.Items) + assert.Equal(t, 999, ocl.CurrentPage) + assert.Equal(t, 2, ocl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + options := OAuthClientListOptions{} + + ocl, err := client.OAuthClients.List(ctx, badIdentifier, options) + assert.Nil(t, ocl) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestOAuthClientsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + githubToken := os.Getenv("GITHUB_TOKEN") + if githubToken == "" { + t.Fatal("Export a valid GITHUB_TOKEN before running this test!") + } + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + HTTPURL: String("https://github.com"), + OAuthToken: String(githubToken), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + oc, err := client.OAuthClients.Create(ctx, orgTest.Name, options) + assert.NoError(t, err) + assert.NotEmpty(t, oc.ID) + assert.Equal(t, "https://api.github.com", oc.APIURL) + assert.Equal(t, "https://github.com", oc.HTTPURL) + assert.Equal(t, 1, len(oc.OAuthTokens)) + assert.Equal(t, ServiceProviderGithub, oc.ServiceProvider) + + t.Run("the organization relationship is decoded correcly", func(t *testing.T) { + assert.NotEmpty(t, oc.Organization) + }) + }) + + t.Run("without an valid organization", func(t *testing.T) { + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + HTTPURL: String("https://github.com"), + OAuthToken: String(githubToken), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + _, err := client.OAuthClients.Create(ctx, badIdentifier, options) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("without an API URL", func(t *testing.T) { + options := OAuthClientCreateOptions{ + HTTPURL: String("https://github.com"), + OAuthToken: String(githubToken), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) + assert.EqualError(t, err, "API URL is required") + }) + + t.Run("without a HTTP URL", func(t *testing.T) { + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + OAuthToken: String(githubToken), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) + assert.EqualError(t, err, "HTTP URL is required") + }) + + t.Run("without an OAuth token", func(t *testing.T) { + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + HTTPURL: String("https://github.com"), + ServiceProvider: ServiceProvider(ServiceProviderGithub), + } + + _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) + assert.EqualError(t, err, "OAuth token is required") + }) + + t.Run("without a service provider", func(t *testing.T) { + options := OAuthClientCreateOptions{ + APIURL: String("https://api.github.com"), + HTTPURL: String("https://github.com"), + OAuthToken: String(githubToken), + } + + _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) + assert.EqualError(t, err, "service provider is required") + }) +} + +func TestOAuthClientsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + ocTest, ocTestCleanup := createOAuthClient(t, client, nil) + defer ocTestCleanup() + + t.Run("when the OAuth client exists", func(t *testing.T) { + oc, err := client.OAuthClients.Read(ctx, ocTest.ID) + require.NoError(t, err) + assert.Equal(t, ocTest.ID, oc.ID) + assert.Equal(t, ocTest.APIURL, oc.APIURL) + assert.Equal(t, ocTest.CallbackURL, oc.CallbackURL) + assert.Equal(t, ocTest.ConnectPath, oc.ConnectPath) + assert.Equal(t, ocTest.HTTPURL, oc.HTTPURL) + assert.Equal(t, ocTest.ServiceProvider, oc.ServiceProvider) + assert.Equal(t, ocTest.ServiceProviderName, oc.ServiceProviderName) + assert.Equal(t, ocTest.OAuthTokens, oc.OAuthTokens) + }) + + t.Run("when the OAuth client does not exist", func(t *testing.T) { + oc, err := client.OAuthClients.Read(ctx, "nonexisting") + assert.Nil(t, oc) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid OAuth client ID", func(t *testing.T) { + oc, err := client.OAuthClients.Read(ctx, badIdentifier) + assert.Nil(t, oc) + assert.EqualError(t, err, "invalid value for OAuth client ID") + }) +} + +func TestOAuthClientsDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + ocTest, _ := createOAuthClient(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.OAuthClients.Delete(ctx, ocTest.ID) + require.NoError(t, err) + + // Try loading the OAuth client - it should fail. + _, err = client.OAuthClients.Read(ctx, ocTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the OAuth client does not exist", func(t *testing.T) { + err := client.OAuthClients.Delete(ctx, ocTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the OAuth client ID is invalid", func(t *testing.T) { + err := client.OAuthClients.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for OAuth client ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go b/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go new file mode 100644 index 000000000..1ba20e44b --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go @@ -0,0 +1,189 @@ +package tfe + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOAuthTokensList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + otTest1, _ := createOAuthToken(t, client, orgTest) + otTest2, _ := createOAuthToken(t, client, orgTest) + + t.Run("without list options", func(t *testing.T) { + options := OAuthTokenListOptions{} + + otl, err := client.OAuthTokens.List(ctx, orgTest.Name, options) + require.NoError(t, err) + + t.Run("the OAuth client relationship is decoded correcly", func(t *testing.T) { + for _, ot := range otl.Items { + assert.NotEmpty(t, ot.OAuthClient) + } + }) + + // We need to strip some fields before the next test. + for _, ot := range otl.Items { + ot.CreatedAt = time.Time{} + ot.ServiceProviderUser = "" + ot.OAuthClient = nil + } + + assert.Contains(t, otl.Items, otTest1) + assert.Contains(t, otl.Items, otTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, otl.CurrentPage) + assert.Equal(t, 2, otl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + options := OAuthTokenListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + } + + otl, err := client.OAuthTokens.List(ctx, orgTest.Name, options) + require.NoError(t, err) + assert.Empty(t, otl.Items) + assert.Equal(t, 999, otl.CurrentPage) + assert.Equal(t, 2, otl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + options := OAuthTokenListOptions{} + + otl, err := client.OAuthTokens.List(ctx, badIdentifier, options) + assert.Nil(t, otl) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestOAuthTokensRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + otTest, otTestCleanup := createOAuthToken(t, client, nil) + defer otTestCleanup() + + t.Run("when the OAuth token exists", func(t *testing.T) { + ot, err := client.OAuthTokens.Read(ctx, otTest.ID) + require.NoError(t, err) + assert.Equal(t, otTest.ID, ot.ID) + assert.NotEmpty(t, ot.OAuthClient) + }) + + t.Run("when the OAuth token does not exist", func(t *testing.T) { + ot, err := client.OAuthTokens.Read(ctx, "nonexisting") + assert.Nil(t, ot) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid OAuth token ID", func(t *testing.T) { + ot, err := client.OAuthTokens.Read(ctx, badIdentifier) + assert.Nil(t, ot) + assert.EqualError(t, err, "invalid value for OAuth token ID") + }) +} + +func TestOAuthTokensUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + otTest, otTestCleanup := createOAuthToken(t, client, nil) + defer otTestCleanup() + + t.Run("before updating with an SSH key", func(t *testing.T) { + assert.False(t, otTest.HasSSHKey) + }) + + t.Run("without options", func(t *testing.T) { + ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{}) + require.NoError(t, err) + assert.False(t, ot.HasSSHKey) + }) + + t.Run("when updating with a valid SSH key", func(t *testing.T) { + dummyPrivateSSHKey := `-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDIF0s2yX7dSQQL1grdTbai1Mb7sEco6RIOz8iqrHTGqmESpu5n +d8imMkV5KadgVBJ/UvHsWpg446O3DAMYn0Y6f8dDlK7pmCEtiGVKTR1PaVRMpF8R +5Guvrmlru8Kex5ozh0pPMB15aGsIzSezCKgSs74Od9YL4smdgKyYwqsu3wIDAQAB +AoGBAKCs6+4j4icqYgBrMjBCHp4lRWCJTqtQdfrE6jv73o5F9Uu4FwupScwD5HwG +cezNtkjeP3zvxvsv+aCdGcNk60vSz4n9Nt6gEJveWFSpePYXKZ9cz/IjFLI7nSzc +1msLyE3DfUqB91s/A/aT5p0LiVDc8i4mCGDOga2OINIwqDGZAkEA/Vz8dkcqsAVW +CL1F000hWTrM6tu0V+x8Nm8CRx7wM/Gy/19PbV0t26wCVG0GXyLWsV2//huY7w5b +3AcSl5pfJQJBAMosYQXk5L4S+qivz2zmZdtyz+Ik6IZ3PwZoED32PxGSdW5rG8iP +V+iSJek5ESkS1zeXwDMnF4LeoBY9H07DiLMCQQCrHm1o2SIMpl34IxWQ4+wdHuid +yuuf4pn2Db2lGVE0VA8ICXBUtfUuA5vDN6tw/8+vFVmBn1QISVNjZOd6uwl9AkA+ +jIRoAm0SsWSDlAEkvBN/VYIjgS+/il0haki8ItdYZGuYgeLSpiaYeb7o7RL2FjIn +rPd12/5WKvJ0buykvbIpAkEA5Uy3T8xQJkDGbp0+xA0yThoOYiB09lAok8I7Sv/5 +dpIe8YOINN27XaojJvVpT5uBVCcZLF+G7kaMjSwCTlDx3Q== +-----END RSA PRIVATE KEY-----` + + ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{ + PrivateSSHKey: String(dummyPrivateSSHKey), + }) + require.NoError(t, err) + + assert.Equal(t, otTest.ID, ot.ID) + assert.True(t, ot.HasSSHKey) + }) + + t.Run("when updating with an invalid SSH key", func(t *testing.T) { + ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{ + PrivateSSHKey: String(randomString(t)), + }) + assert.Nil(t, ot) + assert.Contains(t, err.Error(), "Ssh key is invalid") + }) + + t.Run("without a valid policy ID", func(t *testing.T) { + ot, err := client.OAuthTokens.Update(ctx, badIdentifier, OAuthTokenUpdateOptions{}) + assert.Nil(t, ot) + assert.EqualError(t, err, "invalid value for OAuth token ID") + }) +} + +func TestOAuthTokensDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + otTest, _ := createOAuthToken(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.OAuthTokens.Delete(ctx, otTest.ID) + require.NoError(t, err) + + // Try loading the OAuth token - it should fail. + _, err = client.OAuthTokens.Read(ctx, otTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the OAuth token does not exist", func(t *testing.T) { + err := client.OAuthTokens.Delete(ctx, otTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the OAuth token ID is invalid", func(t *testing.T) { + err := client.OAuthTokens.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for OAuth token ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/organization.go b/vendor/github.com/hashicorp/go-tfe/organization.go index e3a14f697..23d3041f0 100644 --- a/vendor/github.com/hashicorp/go-tfe/organization.go +++ b/vendor/github.com/hashicorp/go-tfe/organization.go @@ -77,6 +77,7 @@ type OrganizationList struct { type Organization struct { Name string `jsonapi:"primary,organizations"` CollaboratorAuthPolicy AuthPolicyType `jsonapi:"attr,collaborator-auth-policy"` + CostEstimationEnabled bool `jsonapi:"attr,cost-estimation-enabled"` CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"` Email string `jsonapi:"attr,email"` EnterprisePlan EnterprisePlanType `jsonapi:"attr,enterprise-plan"` @@ -167,6 +168,9 @@ type OrganizationCreateOptions struct { // Authentication policy. CollaboratorAuthPolicy *AuthPolicyType `jsonapi:"attr,collaborator-auth-policy,omitempty"` + // Enable Cost Estimation + CostEstimationEnabled *bool `jsonapi:"attr,cost-estimation-enabled,omitempty"` + // The name of the "owners" team OwnersTeamSAMLRoleID *string `jsonapi:"attr,owners-team-saml-role-id,omitempty"` } @@ -248,6 +252,9 @@ type OrganizationUpdateOptions struct { // Authentication policy. CollaboratorAuthPolicy *AuthPolicyType `jsonapi:"attr,collaborator-auth-policy,omitempty"` + // Enable Cost Estimation + CostEstimationEnabled *bool `jsonapi:"attr,cost-estimation-enabled,omitempty"` + // The name of the "owners" team OwnersTeamSAMLRoleID *string `jsonapi:"attr,owners-team-saml-role-id,omitempty"` } diff --git a/vendor/github.com/hashicorp/go-tfe/organization_test.go b/vendor/github.com/hashicorp/go-tfe/organization_test.go new file mode 100644 index 000000000..dda38d3de --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/organization_test.go @@ -0,0 +1,367 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOrganizationsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest1, orgTest1Cleanup := createOrganization(t, client) + defer orgTest1Cleanup() + orgTest2, orgTest2Cleanup := createOrganization(t, client) + defer orgTest2Cleanup() + + t.Run("with no list options", func(t *testing.T) { + orgl, err := client.Organizations.List(ctx, OrganizationListOptions{}) + require.NoError(t, err) + assert.Contains(t, orgl.Items, orgTest1) + assert.Contains(t, orgl.Items, orgTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, orgl.CurrentPage) + assert.Equal(t, 2, orgl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + orgl, err := client.Organizations.List(ctx, OrganizationListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, orgl) + assert.Equal(t, 999, orgl.CurrentPage) + assert.Equal(t, 2, orgl.TotalCount) + }) +} + +func TestOrganizationsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + t.Run("with valid options", func(t *testing.T) { + options := OrganizationCreateOptions{ + Name: String(randomString(t)), + Email: String(randomString(t) + "@tfe.local"), + } + + org, err := client.Organizations.Create(ctx, options) + require.NoError(t, err) + + // Make sure we clean up the created org. + defer client.Organizations.Delete(ctx, org.Name) + + assert.Equal(t, *options.Name, org.Name) + assert.Equal(t, *options.Email, org.Email) + }) + + t.Run("when no email is provided", func(t *testing.T) { + org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, org) + assert.EqualError(t, err, "email is required") + }) + + t.Run("when no name is provided", func(t *testing.T) { + _, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ + Email: String("foo@bar.com"), + }) + assert.EqualError(t, err, "name is required") + }) + + t.Run("with invalid name", func(t *testing.T) { + org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ + Name: String(badIdentifier), + Email: String("foo@bar.com"), + }) + assert.Nil(t, org) + assert.EqualError(t, err, "invalid value for name") + }) +} + +func TestOrganizationsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("when the org exists", func(t *testing.T) { + org, err := client.Organizations.Read(ctx, orgTest.Name) + require.NoError(t, err) + assert.Equal(t, orgTest, org) + + t.Run("permissions are properly decoded", func(t *testing.T) { + assert.True(t, org.Permissions.CanDestroy) + }) + + t.Run("timestamps are populated", func(t *testing.T) { + assert.NotEmpty(t, org.CreatedAt) + assert.NotEmpty(t, org.TrialExpiresAt) + }) + }) + + t.Run("with invalid name", func(t *testing.T) { + org, err := client.Organizations.Read(ctx, badIdentifier) + assert.Nil(t, org) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when the org does not exist", func(t *testing.T) { + _, err := client.Organizations.Read(ctx, randomString(t)) + assert.Error(t, err) + }) +} + +func TestOrganizationsUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + t.Run("with valid options", func(t *testing.T) { + orgTest, orgTestCleanup := createOrganization(t, client) + + options := OrganizationUpdateOptions{ + Name: String(randomString(t)), + Email: String(randomString(t) + "@tfe.local"), + SessionTimeout: Int(3600), + SessionRemember: Int(3600), + } + + org, err := client.Organizations.Update(ctx, orgTest.Name, options) + if err != nil { + orgTestCleanup() + } + require.NoError(t, err) + + // Make sure we clean up the renamed org. + defer client.Organizations.Delete(ctx, org.Name) + + // Also get a fresh result from the API to ensure we get the + // expected values back. + refreshed, err := client.Organizations.Read(ctx, *options.Name) + require.NoError(t, err) + + for _, item := range []*Organization{ + org, + refreshed, + } { + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, *options.Email, item.Email) + assert.Equal(t, *options.SessionTimeout, item.SessionTimeout) + assert.Equal(t, *options.SessionRemember, item.SessionRemember) + } + }) + + t.Run("with invalid name", func(t *testing.T) { + org, err := client.Organizations.Update(ctx, badIdentifier, OrganizationUpdateOptions{}) + assert.Nil(t, org) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when only updating a subset of fields", func(t *testing.T) { + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + org, err := client.Organizations.Update(ctx, orgTest.Name, OrganizationUpdateOptions{}) + require.NoError(t, err) + assert.Equal(t, orgTest.Name, org.Name) + assert.Equal(t, orgTest.Email, org.Email) + }) +} + +func TestOrganizationsDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + t.Run("with valid options", func(t *testing.T) { + orgTest, _ := createOrganization(t, client) + + err := client.Organizations.Delete(ctx, orgTest.Name) + require.NoError(t, err) + + // Try fetching the org again - it should error. + _, err = client.Organizations.Read(ctx, orgTest.Name) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid name", func(t *testing.T) { + err := client.Organizations.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestOrganizationsCapacity(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest1, _ := createWorkspace(t, client, orgTest) + wTest2, _ := createWorkspace(t, client, orgTest) + wTest3, _ := createWorkspace(t, client, orgTest) + wTest4, _ := createWorkspace(t, client, orgTest) + + t.Run("without queued runs", func(t *testing.T) { + c, err := client.Organizations.Capacity(ctx, orgTest.Name) + require.NoError(t, err) + assert.Equal(t, 0, c.Pending) + assert.Equal(t, 0, c.Running) + }) + + // For this test FRQ should be enabled and have a + // limit of 2 concurrent runs per organization. + t.Run("with queued runs", func(t *testing.T) { + _, _ = createRun(t, client, wTest1) + _, _ = createRun(t, client, wTest2) + _, _ = createRun(t, client, wTest3) + _, _ = createRun(t, client, wTest4) + + c, err := client.Organizations.Capacity(ctx, orgTest.Name) + require.NoError(t, err) + assert.Equal(t, 2, c.Pending) + assert.Equal(t, 2, c.Running) + }) + + t.Run("with invalid name", func(t *testing.T) { + org, err := client.Organizations.Read(ctx, badIdentifier) + assert.Nil(t, org) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when the org does not exist", func(t *testing.T) { + _, err := client.Organizations.Read(ctx, randomString(t)) + assert.Error(t, err) + }) +} + +func TestOrganizationsEntitlements(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("when the org exists", func(t *testing.T) { + entitlements, err := client.Organizations.Entitlements(ctx, orgTest.Name) + require.NoError(t, err) + + assert.NotEmpty(t, entitlements.ID) + assert.True(t, entitlements.Operations) + assert.True(t, entitlements.PrivateModuleRegistry) + assert.True(t, entitlements.Sentinel) + assert.True(t, entitlements.StateStorage) + assert.True(t, entitlements.Teams) + assert.True(t, entitlements.VCSIntegrations) + }) + + t.Run("with invalid name", func(t *testing.T) { + entitlements, err := client.Organizations.Entitlements(ctx, badIdentifier) + assert.Nil(t, entitlements) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when the org does not exist", func(t *testing.T) { + _, err := client.Organizations.Entitlements(ctx, randomString(t)) + assert.Equal(t, ErrResourceNotFound, err) + }) +} + +func TestOrganizationsRunQueue(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest1, _ := createWorkspace(t, client, orgTest) + wTest2, _ := createWorkspace(t, client, orgTest) + wTest3, _ := createWorkspace(t, client, orgTest) + wTest4, _ := createWorkspace(t, client, orgTest) + + t.Run("without queued runs", func(t *testing.T) { + rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) + require.NoError(t, err) + assert.Equal(t, 0, len(rq.Items)) + }) + + // Create a couple or runs to fill the queue. + rTest1, _ := createRun(t, client, wTest1) + rTest2, _ := createRun(t, client, wTest2) + rTest3, _ := createRun(t, client, wTest3) + rTest4, _ := createRun(t, client, wTest4) + + // For this test FRQ should be enabled and have a + // limit of 2 concurrent runs per organization. + t.Run("with queued runs", func(t *testing.T) { + rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) + require.NoError(t, err) + + found := []string{} + for _, r := range rq.Items { + found = append(found, r.ID) + } + + assert.Contains(t, found, rTest1.ID) + assert.Contains(t, found, rTest2.ID) + assert.Contains(t, found, rTest3.ID) + assert.Contains(t, found, rTest4.ID) + }) + + t.Run("without queue options", func(t *testing.T) { + rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) + require.NoError(t, err) + + found := []string{} + for _, r := range rq.Items { + found = append(found, r.ID) + } + + assert.Contains(t, found, rTest1.ID) + assert.Contains(t, found, rTest2.ID) + assert.Contains(t, found, rTest3.ID) + assert.Contains(t, found, rTest4.ID) + assert.Equal(t, 1, rq.CurrentPage) + assert.Equal(t, 4, rq.TotalCount) + }) + + t.Run("with queue options", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + + assert.Empty(t, rq.Items) + assert.Equal(t, 999, rq.CurrentPage) + assert.Equal(t, 4, rq.TotalCount) + }) + + t.Run("with invalid name", func(t *testing.T) { + org, err := client.Organizations.Read(ctx, badIdentifier) + assert.Nil(t, org) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when the org does not exist", func(t *testing.T) { + _, err := client.Organizations.Read(ctx, randomString(t)) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/organization_token_test.go b/vendor/github.com/hashicorp/go-tfe/organization_token_test.go new file mode 100644 index 000000000..d4b6a7dfe --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/organization_token_test.go @@ -0,0 +1,93 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOrganizationTokensGenerate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + var tkToken string + t.Run("with valid options", func(t *testing.T) { + ot, err := client.OrganizationTokens.Generate(ctx, orgTest.Name) + require.NoError(t, err) + require.NotEmpty(t, ot.Token) + tkToken = ot.Token + }) + + t.Run("when a token already exists", func(t *testing.T) { + ot, err := client.OrganizationTokens.Generate(ctx, orgTest.Name) + require.NoError(t, err) + require.NotEmpty(t, ot.Token) + assert.NotEqual(t, tkToken, ot.Token) + }) + + t.Run("without valid organization", func(t *testing.T) { + ot, err := client.OrganizationTokens.Generate(ctx, badIdentifier) + assert.Nil(t, ot) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestOrganizationTokensRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + _, otTestCleanup := createOrganizationToken(t, client, orgTest) + + ot, err := client.OrganizationTokens.Read(ctx, orgTest.Name) + assert.NoError(t, err) + assert.NotEmpty(t, ot) + + otTestCleanup() + }) + + t.Run("when a token doesn't exists", func(t *testing.T) { + ot, err := client.OrganizationTokens.Read(ctx, orgTest.Name) + assert.Equal(t, ErrResourceNotFound, err) + assert.Nil(t, ot) + }) + + t.Run("without valid organization", func(t *testing.T) { + ot, err := client.OrganizationTokens.Read(ctx, badIdentifier) + assert.Nil(t, ot) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestOrganizationTokensDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + createOrganizationToken(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.OrganizationTokens.Delete(ctx, orgTest.Name) + assert.NoError(t, err) + }) + + t.Run("when a token does not exist", func(t *testing.T) { + err := client.OrganizationTokens.Delete(ctx, orgTest.Name) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without valid organization", func(t *testing.T) { + err := client.OrganizationTokens.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for organization") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/plan_test.go b/vendor/github.com/hashicorp/go-tfe/plan_test.go new file mode 100644 index 000000000..2d92e1a84 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/plan_test.go @@ -0,0 +1,66 @@ +package tfe + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPlansRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createPlannedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the plan exists", func(t *testing.T) { + p, err := client.Plans.Read(ctx, rTest.Plan.ID) + require.NoError(t, err) + assert.True(t, p.HasChanges) + assert.NotEmpty(t, p.LogReadURL) + assert.Equal(t, p.Status, PlanFinished) + assert.NotEmpty(t, p.StatusTimestamps) + }) + + t.Run("when the plan does not exist", func(t *testing.T) { + p, err := client.Plans.Read(ctx, "nonexisting") + assert.Nil(t, p) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid plan ID", func(t *testing.T) { + p, err := client.Plans.Read(ctx, badIdentifier) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for plan ID") + }) +} + +func TestPlansLogs(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createPlannedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the log exists", func(t *testing.T) { + p, err := client.Plans.Read(ctx, rTest.Plan.ID) + require.NoError(t, err) + + logReader, err := client.Plans.Logs(ctx, p.ID) + require.NoError(t, err) + + logs, err := ioutil.ReadAll(logReader) + require.NoError(t, err) + + assert.Contains(t, string(logs), "1 to add, 0 to change, 0 to destroy") + }) + + t.Run("when the log does not exist", func(t *testing.T) { + logs, err := client.Plans.Logs(ctx, "nonexisting") + assert.Nil(t, logs) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_check_test.go b/vendor/github.com/hashicorp/go-tfe/policy_check_test.go new file mode 100644 index 000000000..e9e77c84b --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/policy_check_test.go @@ -0,0 +1,200 @@ +package tfe + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPolicyChecksList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest1, _ := createUploadedPolicy(t, client, true, orgTest) + pTest2, _ := createUploadedPolicy(t, client, true, orgTest) + wTest, _ := createWorkspace(t, client, orgTest) + createPolicySet(t, client, orgTest, []*Policy{pTest1, pTest2}, []*Workspace{wTest}) + + rTest, _ := createPlannedRun(t, client, wTest) + + t.Run("without list options", func(t *testing.T) { + pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) + require.NoError(t, err) + require.Equal(t, 1, len(pcl.Items)) + + t.Run("pagination is properly decoded", func(t *testing.T) { + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, pcl.CurrentPage) + assert.Equal(t, 1, pcl.TotalCount) + }) + + t.Run("permissions are properly decoded", func(t *testing.T) { + assert.NotEmpty(t, pcl.Items[0].Permissions) + }) + + t.Run("result is properly decoded", func(t *testing.T) { + require.NotEmpty(t, pcl.Items[0].Result) + assert.Equal(t, 2, pcl.Items[0].Result.Passed) + }) + + t.Run("timestamps are properly decoded", func(t *testing.T) { + assert.NotEmpty(t, pcl.Items[0].StatusTimestamps) + }) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, pcl.Items) + assert.Equal(t, 999, pcl.CurrentPage) + assert.Equal(t, 1, pcl.TotalCount) + }) + + t.Run("without a valid run ID", func(t *testing.T) { + pcl, err := client.PolicyChecks.List(ctx, badIdentifier, PolicyCheckListOptions{}) + assert.Nil(t, pcl) + assert.EqualError(t, err, "invalid value for run ID") + }) +} + +func TestPolicyChecksRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest, _ := createUploadedPolicy(t, client, true, orgTest) + wTest, _ := createWorkspace(t, client, orgTest) + createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) + + rTest, _ := createPlannedRun(t, client, wTest) + require.Equal(t, 1, len(rTest.PolicyChecks)) + + t.Run("when the policy check exists", func(t *testing.T) { + pc, err := client.PolicyChecks.Read(ctx, rTest.PolicyChecks[0].ID) + require.NoError(t, err) + + assert.NotEmpty(t, pc.Permissions) + assert.Equal(t, PolicyScopeOrganization, pc.Scope) + assert.Equal(t, PolicyPasses, pc.Status) + assert.NotEmpty(t, pc.StatusTimestamps) + + t.Run("result is properly decoded", func(t *testing.T) { + require.NotEmpty(t, pc.Result) + assert.Equal(t, 1, pc.Result.Passed) + }) + }) + + t.Run("when the policy check does not exist", func(t *testing.T) { + pc, err := client.PolicyChecks.Read(ctx, "nonexisting") + assert.Nil(t, pc) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid policy check ID", func(t *testing.T) { + pc, err := client.PolicyChecks.Read(ctx, badIdentifier) + assert.Nil(t, pc) + assert.EqualError(t, err, "invalid value for policy check ID") + }) +} + +func TestPolicyChecksOverride(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("when the policy failed", func(t *testing.T) { + pTest, pTestCleanup := createUploadedPolicy(t, client, false, orgTest) + defer pTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) + rTest, _ := createPlannedRun(t, client, wTest) + + pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) + require.NoError(t, err) + require.Equal(t, 1, len(pcl.Items)) + require.Equal(t, PolicySoftFailed, pcl.Items[0].Status) + + pc, err := client.PolicyChecks.Override(ctx, pcl.Items[0].ID) + require.NoError(t, err) + + assert.NotEmpty(t, pc.Result) + assert.Equal(t, PolicyOverridden, pc.Status) + }) + + t.Run("when the policy passed", func(t *testing.T) { + pTest, pTestCleanup := createUploadedPolicy(t, client, true, orgTest) + defer pTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) + rTest, _ := createPlannedRun(t, client, wTest) + + pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) + require.NoError(t, err) + require.Equal(t, 1, len(pcl.Items)) + require.Equal(t, PolicyPasses, pcl.Items[0].Status) + + _, err = client.PolicyChecks.Override(ctx, pcl.Items[0].ID) + assert.Error(t, err) + }) + + t.Run("without a valid policy check ID", func(t *testing.T) { + p, err := client.PolicyChecks.Override(ctx, badIdentifier) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for policy check ID") + }) +} + +func TestPolicyChecksLogs(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest, _ := createUploadedPolicy(t, client, true, orgTest) + wTest, _ := createWorkspace(t, client, orgTest) + createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) + + rTest, _ := createPlannedRun(t, client, wTest) + require.Equal(t, 1, len(rTest.PolicyChecks)) + + t.Run("when the log exists", func(t *testing.T) { + pc, err := client.PolicyChecks.Read(ctx, rTest.PolicyChecks[0].ID) + require.NoError(t, err) + + logReader, err := client.PolicyChecks.Logs(ctx, pc.ID) + require.NoError(t, err) + + logs, err := ioutil.ReadAll(logReader) + require.NoError(t, err) + + assert.Contains(t, string(logs), "1 policies evaluated") + }) + + t.Run("when the log does not exist", func(t *testing.T) { + logs, err := client.PolicyChecks.Logs(ctx, "nonexisting") + assert.Nil(t, logs) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_set_test.go b/vendor/github.com/hashicorp/go-tfe/policy_set_test.go new file mode 100644 index 000000000..edd403893 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/policy_set_test.go @@ -0,0 +1,454 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPolicySetsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + psTest1, _ := createPolicySet(t, client, orgTest, nil, nil) + psTest2, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("without list options", func(t *testing.T) { + psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{}) + require.NoError(t, err) + + assert.Contains(t, psl.Items, psTest1) + assert.Contains(t, psl.Items, psTest2) + assert.Equal(t, 1, psl.CurrentPage) + assert.Equal(t, 2, psl.TotalCount) + }) + + t.Run("with pagination", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + + assert.Empty(t, psl.Items) + assert.Equal(t, 999, psl.CurrentPage) + assert.Equal(t, 2, psl.TotalCount) + }) + + t.Run("with search", func(t *testing.T) { + // Search by one of the policy set's names; we should get only that policy + // set and pagination data should reflect the search as well + psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{ + Search: String(psTest1.Name), + }) + require.NoError(t, err) + + assert.Contains(t, psl.Items, psTest1) + assert.NotContains(t, psl.Items, psTest2) + assert.Equal(t, 1, psl.CurrentPage) + assert.Equal(t, 1, psl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + ps, err := client.PolicySets.List(ctx, badIdentifier, PolicySetListOptions{}) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestPolicySetsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid attributes", func(t *testing.T) { + options := PolicySetCreateOptions{ + Name: String("policy-set"), + } + + ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + assert.Equal(t, ps.Name, *options.Name) + assert.Equal(t, ps.Description, "") + assert.False(t, ps.Global) + }) + + t.Run("with all attributes provided", func(t *testing.T) { + options := PolicySetCreateOptions{ + Name: String("global"), + Description: String("Policies in this set will be checked in ALL workspaces!"), + Global: Bool(true), + } + + ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + assert.Equal(t, ps.Name, *options.Name) + assert.Equal(t, ps.Description, *options.Description) + assert.True(t, ps.Global) + }) + + t.Run("with policies and workspaces provided", func(t *testing.T) { + pTest, _ := createPolicy(t, client, orgTest) + wTest, _ := createWorkspace(t, client, orgTest) + + options := PolicySetCreateOptions{ + Name: String("populated-policy-set"), + Policies: []*Policy{pTest}, + Workspaces: []*Workspace{wTest}, + } + + ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + assert.Equal(t, ps.Name, *options.Name) + assert.Equal(t, ps.PolicyCount, 1) + assert.Equal(t, ps.Policies[0].ID, pTest.ID) + assert.Equal(t, ps.WorkspaceCount, 1) + assert.Equal(t, ps.Workspaces[0].ID, wTest.ID) + }) + + t.Run("without a name provided", func(t *testing.T) { + ps, err := client.PolicySets.Create(ctx, orgTest.Name, PolicySetCreateOptions{}) + assert.Nil(t, ps) + assert.EqualError(t, err, "name is required") + }) + + t.Run("with an invalid name provided", func(t *testing.T) { + ps, err := client.PolicySets.Create(ctx, orgTest.Name, PolicySetCreateOptions{ + Name: String("nope!"), + }) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for name") + }) + + t.Run("without a valid organization", func(t *testing.T) { + ps, err := client.PolicySets.Create(ctx, badIdentifier, PolicySetCreateOptions{ + Name: String("policy-set"), + }) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestPolicySetsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + psTest, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("with a valid ID", func(t *testing.T) { + ps, err := client.PolicySets.Read(ctx, psTest.ID) + require.NoError(t, err) + + assert.Equal(t, ps.ID, psTest.ID) + }) + + t.Run("without a valid ID", func(t *testing.T) { + ps, err := client.PolicySets.Read(ctx, badIdentifier) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + psTest, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("with valid attributes", func(t *testing.T) { + options := PolicySetUpdateOptions{ + Name: String("global"), + Description: String("Policies in this set will be checked in ALL workspaces!"), + Global: Bool(true), + } + + ps, err := client.PolicySets.Update(ctx, psTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, ps.Name, *options.Name) + assert.Equal(t, ps.Description, *options.Description) + assert.True(t, ps.Global) + }) + + t.Run("with invalid attributes", func(t *testing.T) { + ps, err := client.PolicySets.Update(ctx, psTest.ID, PolicySetUpdateOptions{ + Name: String("nope!"), + }) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for name") + }) + + t.Run("without a valid ID", func(t *testing.T) { + ps, err := client.PolicySets.Update(ctx, badIdentifier, PolicySetUpdateOptions{ + Name: String("policy-set"), + }) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsAddPolicies(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest1, _ := createPolicy(t, client, orgTest) + pTest2, _ := createPolicy(t, client, orgTest) + psTest, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("with policies provided", func(t *testing.T) { + err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{ + Policies: []*Policy{pTest1, pTest2}, + }) + require.NoError(t, err) + + ps, err := client.PolicySets.Read(ctx, psTest.ID) + require.NoError(t, err) + assert.Equal(t, ps.PolicyCount, 2) + + ids := []string{} + for _, policy := range ps.Policies { + ids = append(ids, policy.ID) + } + + assert.Contains(t, ids, pTest1.ID) + assert.Contains(t, ids, pTest2.ID) + }) + + t.Run("without policies provided", func(t *testing.T) { + err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{}) + assert.EqualError(t, err, "policies is required") + }) + + t.Run("with empty policies slice", func(t *testing.T) { + err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{ + Policies: []*Policy{}, + }) + assert.EqualError(t, err, "must provide at least one policy") + }) + + t.Run("without a valid ID", func(t *testing.T) { + err := client.PolicySets.AddPolicies(ctx, badIdentifier, PolicySetAddPoliciesOptions{ + Policies: []*Policy{pTest1, pTest2}, + }) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsRemovePolicies(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest1, _ := createPolicy(t, client, orgTest) + pTest2, _ := createPolicy(t, client, orgTest) + psTest, _ := createPolicySet(t, client, orgTest, []*Policy{pTest1, pTest2}, nil) + + t.Run("with policies provided", func(t *testing.T) { + err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{ + Policies: []*Policy{pTest1, pTest2}, + }) + require.NoError(t, err) + + ps, err := client.PolicySets.Read(ctx, psTest.ID) + require.NoError(t, err) + + assert.Equal(t, 0, ps.PolicyCount) + assert.Empty(t, ps.Policies) + }) + + t.Run("without policies provided", func(t *testing.T) { + err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{}) + assert.EqualError(t, err, "policies is required") + }) + + t.Run("with empty policies slice", func(t *testing.T) { + err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{ + Policies: []*Policy{}, + }) + assert.EqualError(t, err, "must provide at least one policy") + }) + + t.Run("without a valid ID", func(t *testing.T) { + err := client.PolicySets.RemovePolicies(ctx, badIdentifier, PolicySetRemovePoliciesOptions{ + Policies: []*Policy{pTest1, pTest2}, + }) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsAddWorkspaces(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest1, _ := createWorkspace(t, client, orgTest) + wTest2, _ := createWorkspace(t, client, orgTest) + psTest, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("with workspaces provided", func(t *testing.T) { + err := client.PolicySets.AddWorkspaces( + ctx, + psTest.ID, + PolicySetAddWorkspacesOptions{ + Workspaces: []*Workspace{wTest1, wTest2}, + }, + ) + require.NoError(t, err) + + ps, err := client.PolicySets.Read(ctx, psTest.ID) + require.NoError(t, err) + assert.Equal(t, 2, ps.WorkspaceCount) + + ids := []string{} + for _, ws := range ps.Workspaces { + ids = append(ids, ws.ID) + } + + assert.Contains(t, ids, wTest1.ID) + assert.Contains(t, ids, wTest2.ID) + }) + + t.Run("without workspaces provided", func(t *testing.T) { + err := client.PolicySets.AddWorkspaces( + ctx, + psTest.ID, + PolicySetAddWorkspacesOptions{}, + ) + assert.EqualError(t, err, "workspaces is required") + }) + + t.Run("with empty workspaces slice", func(t *testing.T) { + err := client.PolicySets.AddWorkspaces( + ctx, + psTest.ID, + PolicySetAddWorkspacesOptions{Workspaces: []*Workspace{}}, + ) + assert.EqualError(t, err, "must provide at least one workspace") + }) + + t.Run("without a valid ID", func(t *testing.T) { + err := client.PolicySets.AddWorkspaces( + ctx, + badIdentifier, + PolicySetAddWorkspacesOptions{ + Workspaces: []*Workspace{wTest1, wTest2}, + }, + ) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsRemoveWorkspaces(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest1, _ := createWorkspace(t, client, orgTest) + wTest2, _ := createWorkspace(t, client, orgTest) + psTest, _ := createPolicySet(t, client, orgTest, nil, []*Workspace{wTest1, wTest2}) + + t.Run("with workspaces provided", func(t *testing.T) { + err := client.PolicySets.RemoveWorkspaces( + ctx, + psTest.ID, + PolicySetRemoveWorkspacesOptions{ + Workspaces: []*Workspace{wTest1, wTest2}, + }, + ) + require.NoError(t, err) + + ps, err := client.PolicySets.Read(ctx, psTest.ID) + require.NoError(t, err) + + assert.Equal(t, 0, ps.WorkspaceCount) + assert.Empty(t, ps.Workspaces) + }) + + t.Run("without workspaces provided", func(t *testing.T) { + err := client.PolicySets.RemoveWorkspaces( + ctx, + psTest.ID, + PolicySetRemoveWorkspacesOptions{}, + ) + assert.EqualError(t, err, "workspaces is required") + }) + + t.Run("with empty workspaces slice", func(t *testing.T) { + err := client.PolicySets.RemoveWorkspaces( + ctx, + psTest.ID, + PolicySetRemoveWorkspacesOptions{Workspaces: []*Workspace{}}, + ) + assert.EqualError(t, err, "must provide at least one workspace") + }) + + t.Run("without a valid ID", func(t *testing.T) { + err := client.PolicySets.RemoveWorkspaces( + ctx, + badIdentifier, + PolicySetRemoveWorkspacesOptions{ + Workspaces: []*Workspace{wTest1, wTest2}, + }, + ) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} + +func TestPolicySetsDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + psTest, _ := createPolicySet(t, client, orgTest, nil, nil) + + t.Run("with valid options", func(t *testing.T) { + err := client.PolicySets.Delete(ctx, psTest.ID) + require.NoError(t, err) + + // Try loading the policy - it should fail. + _, err = client.PolicySets.Read(ctx, psTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the policy does not exist", func(t *testing.T) { + err := client.PolicySets.Delete(ctx, psTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the policy ID is invalid", func(t *testing.T) { + err := client.PolicySets.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for policy set ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_test.go b/vendor/github.com/hashicorp/go-tfe/policy_test.go new file mode 100644 index 000000000..888ca482d --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/policy_test.go @@ -0,0 +1,393 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPoliciesList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest1, _ := createPolicy(t, client, orgTest) + pTest2, _ := createPolicy(t, client, orgTest) + + t.Run("without list options", func(t *testing.T) { + pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{}) + require.NoError(t, err) + assert.Contains(t, pl.Items, pTest1) + assert.Contains(t, pl.Items, pTest2) + + assert.Equal(t, 1, pl.CurrentPage) + assert.Equal(t, 2, pl.TotalCount) + }) + + t.Run("with pagination", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + + assert.Empty(t, pl.Items) + assert.Equal(t, 999, pl.CurrentPage) + assert.Equal(t, 2, pl.TotalCount) + }) + + t.Run("with search", func(t *testing.T) { + // Search by one of the policy's names; we should get only that policy + // and pagination data should reflect the search as well + pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{ + Search: &pTest1.Name, + }) + require.NoError(t, err) + + assert.Contains(t, pl.Items, pTest1) + assert.NotContains(t, pl.Items, pTest2) + assert.Equal(t, 1, pl.CurrentPage) + assert.Equal(t, 1, pl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + ps, err := client.Policies.List(ctx, badIdentifier, PolicyListOptions{}) + assert.Nil(t, ps) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestPoliciesCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + name := randomString(t) + options := PolicyCreateOptions{ + Name: String(name), + Description: String("A sample policy"), + Enforce: []*EnforcementOptions{ + { + Path: String(name + ".sentinel"), + Mode: EnforcementMode(EnforcementSoft), + }, + }, + } + + p, err := client.Policies.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.Policies.Read(ctx, p.ID) + require.NoError(t, err) + + for _, item := range []*Policy{ + p, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, *options.Description, item.Description) + } + }) + + t.Run("when options has an invalid name", func(t *testing.T) { + p, err := client.Policies.Create(ctx, orgTest.Name, PolicyCreateOptions{ + Name: String(badIdentifier), + Enforce: []*EnforcementOptions{ + { + Path: String(badIdentifier + ".sentinel"), + Mode: EnforcementMode(EnforcementSoft), + }, + }, + }) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for name") + }) + + t.Run("when options is missing name", func(t *testing.T) { + p, err := client.Policies.Create(ctx, orgTest.Name, PolicyCreateOptions{ + Enforce: []*EnforcementOptions{ + { + Path: String(randomString(t) + ".sentinel"), + Mode: EnforcementMode(EnforcementSoft), + }, + }, + }) + assert.Nil(t, p) + assert.EqualError(t, err, "name is required") + }) + + t.Run("when options is missing an enforcement", func(t *testing.T) { + options := PolicyCreateOptions{ + Name: String(randomString(t)), + } + + p, err := client.Policies.Create(ctx, orgTest.Name, options) + assert.Nil(t, p) + assert.EqualError(t, err, "enforce is required") + }) + + t.Run("when options is missing enforcement path", func(t *testing.T) { + options := PolicyCreateOptions{ + Name: String(randomString(t)), + Enforce: []*EnforcementOptions{ + { + Mode: EnforcementMode(EnforcementSoft), + }, + }, + } + + p, err := client.Policies.Create(ctx, orgTest.Name, options) + assert.Nil(t, p) + assert.EqualError(t, err, "enforcement path is required") + }) + + t.Run("when options is missing enforcement path", func(t *testing.T) { + name := randomString(t) + options := PolicyCreateOptions{ + Name: String(name), + Enforce: []*EnforcementOptions{ + { + Path: String(name + ".sentinel"), + }, + }, + } + + p, err := client.Policies.Create(ctx, orgTest.Name, options) + assert.Nil(t, p) + assert.EqualError(t, err, "enforcement mode is required") + }) + + t.Run("when options has an invalid organization", func(t *testing.T) { + p, err := client.Policies.Create(ctx, badIdentifier, PolicyCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestPoliciesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest, pTestCleanup := createPolicy(t, client, orgTest) + defer pTestCleanup() + + t.Run("when the policy exists without content", func(t *testing.T) { + p, err := client.Policies.Read(ctx, pTest.ID) + require.NoError(t, err) + + assert.Equal(t, pTest.ID, p.ID) + assert.Equal(t, pTest.Name, p.Name) + assert.Equal(t, pTest.PolicySetCount, p.PolicySetCount) + assert.Empty(t, p.Enforce) + assert.Equal(t, pTest.Organization.Name, p.Organization.Name) + }) + + err := client.Policies.Upload(ctx, pTest.ID, []byte(`main = rule { true }`)) + require.NoError(t, err) + + t.Run("when the policy exists with content", func(t *testing.T) { + p, err := client.Policies.Read(ctx, pTest.ID) + require.NoError(t, err) + + assert.Equal(t, pTest.ID, p.ID) + assert.Equal(t, pTest.Name, p.Name) + assert.Equal(t, pTest.Description, p.Description) + assert.Equal(t, pTest.PolicySetCount, p.PolicySetCount) + assert.NotEmpty(t, p.Enforce) + assert.Equal(t, pTest.Organization.Name, p.Organization.Name) + }) + + t.Run("when the policy does not exist", func(t *testing.T) { + p, err := client.Policies.Read(ctx, "nonexisting") + assert.Nil(t, p) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid policy ID", func(t *testing.T) { + p, err := client.Policies.Read(ctx, badIdentifier) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for policy ID") + }) +} + +func TestPoliciesUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("when updating with an existing path", func(t *testing.T) { + pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) + defer pBeforeCleanup() + + require.Equal(t, 1, len(pBefore.Enforce)) + + pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ + Enforce: []*EnforcementOptions{ + { + Path: String(pBefore.Enforce[0].Path), + Mode: EnforcementMode(EnforcementAdvisory), + }, + }, + }) + require.NoError(t, err) + require.Equal(t, 1, len(pAfter.Enforce)) + + assert.Equal(t, pBefore.ID, pAfter.ID) + assert.Equal(t, pBefore.Name, pAfter.Name) + assert.Equal(t, pBefore.Description, pAfter.Description) + assert.Equal(t, pBefore.Enforce[0].Path, pAfter.Enforce[0].Path) + assert.Equal(t, EnforcementAdvisory, pAfter.Enforce[0].Mode) + }) + + t.Run("when updating with a nonexisting path", func(t *testing.T) { + pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) + defer pBeforeCleanup() + + require.Equal(t, 1, len(pBefore.Enforce)) + + pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ + Enforce: []*EnforcementOptions{ + { + Path: String("nonexisting"), + Mode: EnforcementMode(EnforcementAdvisory), + }, + }, + }) + require.NoError(t, err) + + // Weirdly enough this is not equal as updating a nonexisting path + // causes the enforce mode to reset to the default hard-mandatory + t.Skip("see comment...") + assert.Equal(t, pBefore, pAfter) + }) + + t.Run("with a new description", func(t *testing.T) { + pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) + defer pBeforeCleanup() + + pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ + Description: String("A brand new description"), + }) + require.NoError(t, err) + + assert.Equal(t, pBefore.Name, pAfter.Name) + assert.Equal(t, pBefore.Enforce, pAfter.Enforce) + assert.NotEqual(t, pBefore.Description, pAfter.Description) + assert.Equal(t, "A brand new description", pAfter.Description) + }) + + t.Run("without a valid policy ID", func(t *testing.T) { + p, err := client.Policies.Update(ctx, badIdentifier, PolicyUpdateOptions{}) + assert.Nil(t, p) + assert.EqualError(t, err, "invalid value for policy ID") + }) +} + +func TestPoliciesDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + pTest, _ := createPolicy(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.Policies.Delete(ctx, pTest.ID) + require.NoError(t, err) + + // Try loading the policy - it should fail. + _, err = client.Policies.Read(ctx, pTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the policy does not exist", func(t *testing.T) { + err := client.Policies.Delete(ctx, pTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the policy ID is invalid", func(t *testing.T) { + err := client.Policies.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for policy ID") + }) +} + +func TestPoliciesUpload(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + pTest, pTestCleanup := createPolicy(t, client, nil) + defer pTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + err := client.Policies.Upload(ctx, pTest.ID, []byte(`main = rule { true }`)) + assert.NoError(t, err) + }) + + t.Run("with empty content", func(t *testing.T) { + err := client.Policies.Upload(ctx, pTest.ID, []byte{}) + assert.NoError(t, err) + }) + + t.Run("without any content", func(t *testing.T) { + err := client.Policies.Upload(ctx, pTest.ID, nil) + assert.NoError(t, err) + }) + + t.Run("without a valid policy ID", func(t *testing.T) { + err := client.Policies.Upload(ctx, badIdentifier, []byte(`main = rule { true }`)) + assert.EqualError(t, err, "invalid value for policy ID") + }) +} + +func TestPoliciesDownload(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + pTest, pTestCleanup := createPolicy(t, client, nil) + defer pTestCleanup() + + testContent := []byte(`main = rule { true }`) + + t.Run("without existing content", func(t *testing.T) { + content, err := client.Policies.Download(ctx, pTest.ID) + assert.Equal(t, ErrResourceNotFound, err) + assert.Nil(t, content) + }) + + t.Run("with valid options", func(t *testing.T) { + err := client.Policies.Upload(ctx, pTest.ID, testContent) + require.NoError(t, err) + + content, err := client.Policies.Download(ctx, pTest.ID) + assert.NoError(t, err) + assert.Equal(t, testContent, content) + }) + + t.Run("without a valid policy ID", func(t *testing.T) { + content, err := client.Policies.Download(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for policy ID") + assert.Nil(t, content) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/run.go b/vendor/github.com/hashicorp/go-tfe/run.go index 58528739b..702a3de81 100644 --- a/vendor/github.com/hashicorp/go-tfe/run.go +++ b/vendor/github.com/hashicorp/go-tfe/run.go @@ -49,12 +49,16 @@ type RunStatus string //List all available run statuses. const ( RunApplied RunStatus = "applied" + RunApplyQueued RunStatus = "apply_queued" RunApplying RunStatus = "applying" RunCanceled RunStatus = "canceled" RunConfirmed RunStatus = "confirmed" + RunCostEstimated RunStatus = "cost_estimated" + RunCostEstimating RunStatus = "cost_estimating" RunDiscarded RunStatus = "discarded" RunErrored RunStatus = "errored" RunPending RunStatus = "pending" + RunPlanQueued RunStatus = "plan_queued" RunPlanned RunStatus = "planned" RunPlannedAndFinished RunStatus = "planned_and_finished" RunPlanning RunStatus = "planning" @@ -98,6 +102,7 @@ type Run struct { // Relations Apply *Apply `jsonapi:"relation,apply"` ConfigurationVersion *ConfigurationVersion `jsonapi:"relation,configuration-version"` + CostEstimation *CostEstimation `jsonapi:"relation,cost-estimation"` Plan *Plan `jsonapi:"relation,plan"` PolicyChecks []*PolicyCheck `jsonapi:"relation,policy-checks"` Workspace *Workspace `jsonapi:"relation,workspace"` @@ -274,7 +279,7 @@ func (s *runs) Cancel(ctx context.Context, runID string, options RunCancelOption return s.client.do(ctx, req, nil) } -// RunCancelOptions represents the options for force-canceling a run. +// RunForceCancelOptions represents the options for force-canceling a run. type RunForceCancelOptions struct { // An optional comment explaining the reason for the force-cancel. Comment *string `json:"comment,omitempty"` diff --git a/vendor/github.com/hashicorp/go-tfe/run_test.go b/vendor/github.com/hashicorp/go-tfe/run_test.go new file mode 100644 index 000000000..a23e8dd86 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/run_test.go @@ -0,0 +1,277 @@ +package tfe + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRunsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + rTest1, _ := createRun(t, client, wTest) + rTest2, _ := createRun(t, client, wTest) + + t.Run("without list options", func(t *testing.T) { + rl, err := client.Runs.List(ctx, wTest.ID, RunListOptions{}) + require.NoError(t, err) + + found := []string{} + for _, r := range rl.Items { + found = append(found, r.ID) + } + + assert.Contains(t, found, rTest1.ID) + assert.Contains(t, found, rTest2.ID) + assert.Equal(t, 1, rl.CurrentPage) + assert.Equal(t, 2, rl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + rl, err := client.Runs.List(ctx, wTest.ID, RunListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, rl.Items) + assert.Equal(t, 999, rl.CurrentPage) + assert.Equal(t, 2, rl.TotalCount) + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + rl, err := client.Runs.List(ctx, badIdentifier, RunListOptions{}) + assert.Nil(t, rl) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestRunsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + cvTest, _ := createUploadedConfigurationVersion(t, client, wTest) + + t.Run("without a configuration version", func(t *testing.T) { + options := RunCreateOptions{ + Workspace: wTest, + } + + _, err := client.Runs.Create(ctx, options) + assert.NoError(t, err) + }) + + t.Run("with a configuration version", func(t *testing.T) { + options := RunCreateOptions{ + ConfigurationVersion: cvTest, + Workspace: wTest, + } + + r, err := client.Runs.Create(ctx, options) + require.NoError(t, err) + assert.Equal(t, cvTest.ID, r.ConfigurationVersion.ID) + }) + + t.Run("without a workspace", func(t *testing.T) { + r, err := client.Runs.Create(ctx, RunCreateOptions{}) + assert.Nil(t, r) + assert.EqualError(t, err, "workspace is required") + }) + + t.Run("with additional attributes", func(t *testing.T) { + options := RunCreateOptions{ + Message: String("yo"), + Workspace: wTest, + } + + r, err := client.Runs.Create(ctx, options) + require.NoError(t, err) + assert.Equal(t, *options.Message, r.Message) + }) +} + +func TestRunsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createPlannedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the run exists", func(t *testing.T) { + r, err := client.Runs.Read(ctx, rTest.ID) + assert.NoError(t, err) + assert.Equal(t, rTest, r) + }) + + t.Run("when the run does not exist", func(t *testing.T) { + r, err := client.Runs.Read(ctx, "nonexisting") + assert.Nil(t, r) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid run ID", func(t *testing.T) { + r, err := client.Runs.Read(ctx, badIdentifier) + assert.Nil(t, r) + assert.EqualError(t, err, "invalid value for run ID") + }) +} + +func TestRunsApply(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createPlannedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the run exists", func(t *testing.T) { + err := client.Runs.Apply(ctx, rTest.ID, RunApplyOptions{}) + assert.NoError(t, err) + }) + + t.Run("when the run does not exist", func(t *testing.T) { + err := client.Runs.Apply(ctx, "nonexisting", RunApplyOptions{}) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid run ID", func(t *testing.T) { + err := client.Runs.Apply(ctx, badIdentifier, RunApplyOptions{}) + assert.EqualError(t, err, "invalid value for run ID") + }) +} + +func TestRunsCancel(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + // We need to create 2 runs here. The first run will automatically + // be planned so that one cannot be cancelled. The second one will + // be pending until the first one is confirmed or discarded, so we + // can cancel that one. + _, _ = createRun(t, client, wTest) + rTest2, _ := createRun(t, client, wTest) + + t.Run("when the run exists", func(t *testing.T) { + err := client.Runs.Cancel(ctx, rTest2.ID, RunCancelOptions{}) + assert.NoError(t, err) + }) + + t.Run("when the run does not exist", func(t *testing.T) { + err := client.Runs.Cancel(ctx, "nonexisting", RunCancelOptions{}) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid run ID", func(t *testing.T) { + err := client.Runs.Cancel(ctx, badIdentifier, RunCancelOptions{}) + assert.EqualError(t, err, "invalid value for run ID") + }) +} + +func TestRunsForceCancel(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + // We need to create 2 runs here. The first run will automatically + // be planned so that one cannot be cancelled. The second one will + // be pending until the first one is confirmed or discarded, so we + // can cancel that one. + _, _ = createRun(t, client, wTest) + rTest, _ := createRun(t, client, wTest) + + t.Run("run is not force-cancelable", func(t *testing.T) { + assert.False(t, rTest.Actions.IsForceCancelable) + }) + + t.Run("user is allowed to force-cancel", func(t *testing.T) { + assert.True(t, rTest.Permissions.CanForceCancel) + }) + + t.Run("after a normal cancel", func(t *testing.T) { + // Request the normal cancel + err := client.Runs.Cancel(ctx, rTest.ID, RunCancelOptions{}) + require.NoError(t, err) + + for i := 1; ; i++ { + // Refresh the view of the run + rTest, err = client.Runs.Read(ctx, rTest.ID) + require.NoError(t, err) + + // Check if the timestamp is present. + if !rTest.ForceCancelAvailableAt.IsZero() { + break + } + + if i > 30 { + t.Fatal("Timeout waiting for run to be canceled") + } + + time.Sleep(time.Second) + } + + t.Run("force-cancel-available-at timestamp is present", func(t *testing.T) { + assert.True(t, rTest.ForceCancelAvailableAt.After(time.Now())) + }) + + // This test case is minimal because a force-cancel is not needed in + // any normal circumstance. Only if Terraform encounters unexpected + // errors or behaves abnormally should this functionality be required. + // Force-cancel only becomes available if a normal cancel is performed + // first, and the desired canceled state is not reached within a pre- + // determined amount of time (see + // https://www.terraform.io/docs/enterprise/api/run.html#forcefully-cancel-a-run). + }) + + t.Run("when the run does not exist", func(t *testing.T) { + err := client.Runs.ForceCancel(ctx, "nonexisting", RunForceCancelOptions{}) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid run ID", func(t *testing.T) { + err := client.Runs.ForceCancel(ctx, badIdentifier, RunForceCancelOptions{}) + assert.EqualError(t, err, "invalid value for run ID") + }) +} + +func TestRunsDiscard(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + rTest, rTestCleanup := createPlannedRun(t, client, nil) + defer rTestCleanup() + + t.Run("when the run exists", func(t *testing.T) { + err := client.Runs.Discard(ctx, rTest.ID, RunDiscardOptions{}) + assert.NoError(t, err) + }) + + t.Run("when the run does not exist", func(t *testing.T) { + err := client.Runs.Discard(ctx, "nonexisting", RunDiscardOptions{}) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid run ID", func(t *testing.T) { + err := client.Runs.Discard(ctx, badIdentifier, RunDiscardOptions{}) + assert.EqualError(t, err, "invalid value for run ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go b/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go new file mode 100644 index 000000000..c34adc0a6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go @@ -0,0 +1,219 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSSHKeysList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + kTest1, _ := createSSHKey(t, client, orgTest) + kTest2, _ := createSSHKey(t, client, orgTest) + + t.Run("without list options", func(t *testing.T) { + kl, err := client.SSHKeys.List(ctx, orgTest.Name, SSHKeyListOptions{}) + require.NoError(t, err) + assert.Contains(t, kl.Items, kTest1) + assert.Contains(t, kl.Items, kTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, kl.CurrentPage) + assert.Equal(t, 2, kl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + kl, err := client.SSHKeys.List(ctx, orgTest.Name, SSHKeyListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, kl.Items) + assert.Equal(t, 999, kl.CurrentPage) + assert.Equal(t, 2, kl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + kl, err := client.SSHKeys.List(ctx, badIdentifier, SSHKeyListOptions{}) + assert.Nil(t, kl) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestSSHKeysCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := SSHKeyCreateOptions{ + Name: String(randomString(t)), + Value: String(randomString(t)), + } + + k, err := client.SSHKeys.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.SSHKeys.Read(ctx, k.ID) + require.NoError(t, err) + + for _, item := range []*SSHKey{ + k, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + } + }) + + t.Run("when options is missing name", func(t *testing.T) { + k, err := client.SSHKeys.Create(ctx, "foo", SSHKeyCreateOptions{ + Value: String(randomString(t)), + }) + assert.Nil(t, k) + assert.EqualError(t, err, "name is required") + }) + + t.Run("when options is missing value", func(t *testing.T) { + k, err := client.SSHKeys.Create(ctx, "foo", SSHKeyCreateOptions{ + Name: String(randomString(t)), + }) + assert.Nil(t, k) + assert.EqualError(t, err, "value is required") + }) + + t.Run("when options has an invalid organization", func(t *testing.T) { + k, err := client.SSHKeys.Create(ctx, badIdentifier, SSHKeyCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, k) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestSSHKeysRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + kTest, _ := createSSHKey(t, client, orgTest) + + t.Run("when the SSH key exists", func(t *testing.T) { + k, err := client.SSHKeys.Read(ctx, kTest.ID) + require.NoError(t, err) + assert.Equal(t, kTest, k) + }) + + t.Run("when the SSH key does not exist", func(t *testing.T) { + k, err := client.SSHKeys.Read(ctx, "nonexisting") + assert.Nil(t, k) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without a valid SSH key ID", func(t *testing.T) { + k, err := client.SSHKeys.Read(ctx, badIdentifier) + assert.Nil(t, k) + assert.EqualError(t, err, "invalid value for SSH key ID") + }) +} + +func TestSSHKeysUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + kBefore, kTestCleanup := createSSHKey(t, client, orgTest) + defer kTestCleanup() + + kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ + Name: String(randomString(t)), + Value: String(randomString(t)), + }) + require.NoError(t, err) + + assert.Equal(t, kBefore.ID, kAfter.ID) + assert.NotEqual(t, kBefore.Name, kAfter.Name) + }) + + t.Run("when updating the name", func(t *testing.T) { + kBefore, kTestCleanup := createSSHKey(t, client, orgTest) + defer kTestCleanup() + + kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ + Name: String("updated-key-name"), + }) + require.NoError(t, err) + + assert.Equal(t, kBefore.ID, kAfter.ID) + assert.Equal(t, "updated-key-name", kAfter.Name) + }) + + t.Run("when updating the value", func(t *testing.T) { + kBefore, kTestCleanup := createSSHKey(t, client, orgTest) + defer kTestCleanup() + + kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ + Value: String("updated-key-value"), + }) + require.NoError(t, err) + + assert.Equal(t, kBefore.ID, kAfter.ID) + assert.Equal(t, kBefore.Name, kAfter.Name) + }) + + t.Run("without a valid SSH key ID", func(t *testing.T) { + w, err := client.SSHKeys.Update(ctx, badIdentifier, SSHKeyUpdateOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for SSH key ID") + }) +} + +func TestSSHKeysDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + kTest, _ := createSSHKey(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.SSHKeys.Delete(ctx, kTest.ID) + require.NoError(t, err) + + // Try loading the SSH key - it should fail. + _, err = client.SSHKeys.Read(ctx, kTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the SSH key does not exist", func(t *testing.T) { + err := client.SSHKeys.Delete(ctx, kTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the SSH key ID is invalid", func(t *testing.T) { + err := client.SSHKeys.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for SSH key ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/state_version_test.go b/vendor/github.com/hashicorp/go-tfe/state_version_test.go new file mode 100644 index 000000000..a6b77dd27 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/state_version_test.go @@ -0,0 +1,350 @@ +package tfe + +import ( + "context" + "crypto/md5" + "encoding/base64" + "fmt" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStateVersionsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, wTestCleanup := createWorkspace(t, client, orgTest) + defer wTestCleanup() + + svTest1, _ := createStateVersion(t, client, 0, wTest) + svTest2, _ := createStateVersion(t, client, 1, wTest) + + t.Run("without list options", func(t *testing.T) { + options := StateVersionListOptions{ + Organization: String(orgTest.Name), + Workspace: String(wTest.Name), + } + + svl, err := client.StateVersions.List(ctx, options) + require.NoError(t, err) + + // We need to strip the upload URL as that is a dynamic link. + svTest1.DownloadURL = "" + svTest2.DownloadURL = "" + + // And for the retrieved configuration versions as well. + for _, sv := range svl.Items { + sv.DownloadURL = "" + } + + assert.Contains(t, svl.Items, svTest1) + assert.Contains(t, svl.Items, svTest2) + assert.Equal(t, 1, svl.CurrentPage) + assert.Equal(t, 2, svl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + options := StateVersionListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + Organization: String(orgTest.Name), + Workspace: String(wTest.Name), + } + + svl, err := client.StateVersions.List(ctx, options) + require.NoError(t, err) + assert.Empty(t, svl.Items) + assert.Equal(t, 999, svl.CurrentPage) + assert.Equal(t, 2, svl.TotalCount) + }) + + t.Run("without an organization", func(t *testing.T) { + options := StateVersionListOptions{ + Workspace: String(wTest.Name), + } + + svl, err := client.StateVersions.List(ctx, options) + assert.Nil(t, svl) + assert.EqualError(t, err, "organization is required") + }) + + t.Run("without a workspace", func(t *testing.T) { + options := StateVersionListOptions{ + Organization: String(orgTest.Name), + } + + svl, err := client.StateVersions.List(ctx, options) + assert.Nil(t, svl) + assert.EqualError(t, err, "workspace is required") + }) +} + +func TestStateVersionsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + state, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") + if err != nil { + t.Fatal(err) + } + + t.Run("with valid options", func(t *testing.T) { + ctx := context.Background() + _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + if err != nil { + t.Fatal(err) + } + + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + Lineage: String("741c4949-60b9-5bb1-5bf8-b14f4bb14af3"), + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(1), + State: String(base64.StdEncoding.EncodeToString(state)), + }) + require.NoError(t, err) + + // Get a refreshed view of the configuration version. + refreshed, err := client.StateVersions.Read(ctx, sv.ID) + require.NoError(t, err) + + _, err = client.Workspaces.Unlock(ctx, wTest.ID) + if err != nil { + t.Fatal(err) + } + + for _, item := range []*StateVersion{ + sv, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, int64(1), item.Serial) + assert.NotEmpty(t, item.CreatedAt) + assert.NotEmpty(t, item.DownloadURL) + } + }) + + t.Run("with the force flag set", func(t *testing.T) { + ctx := context.Background() + _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + if err != nil { + t.Fatal(err) + } + + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + Lineage: String("741c4949-60b9-5bb1-5bf8-b14f4bb14af3"), + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(1), + State: String(base64.StdEncoding.EncodeToString(state)), + }) + require.NoError(t, err) + + sv, err = client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + Lineage: String("821c4747-a0b9-3bd1-8bf3-c14f4bb14be7"), + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(2), + State: String(base64.StdEncoding.EncodeToString(state)), + Force: Bool(true), + }) + require.NoError(t, err) + + // Get a refreshed view of the configuration version. + refreshed, err := client.StateVersions.Read(ctx, sv.ID) + require.NoError(t, err) + + _, err = client.Workspaces.Unlock(ctx, wTest.ID) + if err != nil { + t.Fatal(err) + } + + for _, item := range []*StateVersion{ + sv, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, int64(2), item.Serial) + assert.NotEmpty(t, item.CreatedAt) + assert.NotEmpty(t, item.DownloadURL) + } + }) + + t.Run("with a run to associate with", func(t *testing.T) { + t.Skip("This can only be tested with the run specific token") + + rTest, _ := createRun(t, client, wTest) + + ctx := context.Background() + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(0), + State: String(base64.StdEncoding.EncodeToString(state)), + Run: rTest, + }) + require.NoError(t, err) + require.NotEmpty(t, sv.Run) + + // Get a refreshed view of the configuration version. + refreshed, err := client.StateVersions.Read(ctx, sv.ID) + require.NoError(t, err) + require.NotEmpty(t, refreshed.Run) + + for _, item := range []*StateVersion{ + sv, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, int64(0), item.Serial) + assert.NotEmpty(t, item.CreatedAt) + assert.NotEmpty(t, item.DownloadURL) + assert.Equal(t, rTest.ID, item.Run.ID) + } + }) + + t.Run("without md5 hash", func(t *testing.T) { + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + Serial: Int64(0), + State: String(base64.StdEncoding.EncodeToString(state)), + }) + assert.Nil(t, sv) + assert.EqualError(t, err, "MD5 is required") + }) + + t.Run("withous serial", func(t *testing.T) { + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + State: String(base64.StdEncoding.EncodeToString(state)), + }) + assert.Nil(t, sv) + assert.EqualError(t, err, "serial is required") + }) + + t.Run("without state", func(t *testing.T) { + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(0), + }) + assert.Nil(t, sv) + assert.EqualError(t, err, "state is required") + }) + + t.Run("with invalid workspace id", func(t *testing.T) { + sv, err := client.StateVersions.Create(ctx, badIdentifier, StateVersionCreateOptions{}) + assert.Nil(t, sv) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestStateVersionsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + svTest, svTestCleanup := createStateVersion(t, client, 0, nil) + defer svTestCleanup() + + t.Run("when the state version exists", func(t *testing.T) { + sv, err := client.StateVersions.Read(ctx, svTest.ID) + require.NoError(t, err) + + // Don't compare the DownloadURL because it will be generated twice + // in this test - once at creation of the configuration version, and + // again during the GET. + svTest.DownloadURL, sv.DownloadURL = "", "" + + assert.Equal(t, svTest, sv) + }) + + t.Run("when the state version does not exist", func(t *testing.T) { + sv, err := client.StateVersions.Read(ctx, "nonexisting") + assert.Nil(t, sv) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("with invalid state version id", func(t *testing.T) { + sv, err := client.StateVersions.Read(ctx, badIdentifier) + assert.Nil(t, sv) + assert.EqualError(t, err, "invalid value for state version ID") + }) +} + +func TestStateVersionsCurrent(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest1, wTest1Cleanup := createWorkspace(t, client, nil) + defer wTest1Cleanup() + + wTest2, wTest2Cleanup := createWorkspace(t, client, nil) + defer wTest2Cleanup() + + svTest, svTestCleanup := createStateVersion(t, client, 0, wTest1) + defer svTestCleanup() + + t.Run("when a state version exists", func(t *testing.T) { + sv, err := client.StateVersions.Current(ctx, wTest1.ID) + require.NoError(t, err) + + // Don't compare the DownloadURL because it will be generated twice + // in this test - once at creation of the configuration version, and + // again during the GET. + svTest.DownloadURL, sv.DownloadURL = "", "" + + assert.Equal(t, svTest, sv) + }) + + t.Run("when a state version does not exist", func(t *testing.T) { + sv, err := client.StateVersions.Current(ctx, wTest2.ID) + assert.Nil(t, sv) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("with invalid workspace id", func(t *testing.T) { + sv, err := client.StateVersions.Current(ctx, badIdentifier) + assert.Nil(t, sv) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestStateVersionsDownload(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + svTest, svTestCleanup := createStateVersion(t, client, 0, nil) + defer svTestCleanup() + + stateTest, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") + require.NoError(t, err) + + t.Run("when the state version exists", func(t *testing.T) { + state, err := client.StateVersions.Download(ctx, svTest.DownloadURL) + require.NoError(t, err) + assert.Equal(t, stateTest, state) + }) + + t.Run("when the state version does not exist", func(t *testing.T) { + state, err := client.StateVersions.Download( + ctx, + svTest.DownloadURL[:len(svTest.DownloadURL)-10]+"nonexisting", + ) + assert.Nil(t, state) + assert.Error(t, err) + }) + + t.Run("with an invalid url", func(t *testing.T) { + state, err := client.StateVersions.Download(ctx, badIdentifier) + assert.Nil(t, state) + assert.Equal(t, ErrResourceNotFound, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/team_access_test.go b/vendor/github.com/hashicorp/go-tfe/team_access_test.go new file mode 100644 index 000000000..fea595e98 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/team_access_test.go @@ -0,0 +1,214 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTeamAccessesList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + tmTest1, tmTest1Cleanup := createTeam(t, client, orgTest) + defer tmTest1Cleanup() + tmTest2, tmTest2Cleanup := createTeam(t, client, orgTest) + defer tmTest2Cleanup() + + taTest1, taTest1Cleanup := createTeamAccess(t, client, tmTest1, wTest, orgTest) + defer taTest1Cleanup() + taTest2, taTest2Cleanup := createTeamAccess(t, client, tmTest2, wTest, orgTest) + defer taTest2Cleanup() + + t.Run("with valid options", func(t *testing.T) { + tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ + WorkspaceID: String(wTest.ID), + }) + require.NoError(t, err) + assert.Contains(t, tal.Items, taTest1) + assert.Contains(t, tal.Items, taTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, tal.CurrentPage) + assert.Equal(t, 2, tal.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, tal.Items) + assert.Equal(t, 999, tal.CurrentPage) + assert.Equal(t, 2, tal.TotalCount) + }) + + t.Run("without list options", func(t *testing.T) { + tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{}) + assert.Nil(t, tal) + assert.EqualError(t, err, "workspace ID is required") + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ + WorkspaceID: String(badIdentifier), + }) + assert.Nil(t, tal) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestTeamAccessesAdd(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + tmTest, tmTestCleanup := createTeam(t, client, orgTest) + defer tmTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := TeamAccessAddOptions{ + Access: Access(AccessAdmin), + Team: tmTest, + Workspace: wTest, + } + + ta, err := client.TeamAccess.Add(ctx, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.TeamAccess.Read(ctx, ta.ID) + require.NoError(t, err) + + for _, item := range []*TeamAccess{ + ta, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Access, item.Access) + } + }) + + t.Run("when the team already has access", func(t *testing.T) { + options := TeamAccessAddOptions{ + Access: Access(AccessAdmin), + Team: tmTest, + Workspace: wTest, + } + + _, err := client.TeamAccess.Add(ctx, options) + assert.Error(t, err) + }) + + t.Run("when options is missing access", func(t *testing.T) { + ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ + Team: tmTest, + Workspace: wTest, + }) + assert.Nil(t, ta) + assert.EqualError(t, err, "access is required") + }) + + t.Run("when options is missing team", func(t *testing.T) { + ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ + Access: Access(AccessAdmin), + Workspace: wTest, + }) + assert.Nil(t, ta) + assert.EqualError(t, err, "team is required") + }) + + t.Run("when options is missing workspace", func(t *testing.T) { + ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ + Access: Access(AccessAdmin), + Team: tmTest, + }) + assert.Nil(t, ta) + assert.EqualError(t, err, "workspace is required") + }) +} + +func TestTeamAccessesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + taTest, taTestCleanup := createTeamAccess(t, client, nil, nil, nil) + defer taTestCleanup() + + t.Run("when the team access exists", func(t *testing.T) { + ta, err := client.TeamAccess.Read(ctx, taTest.ID) + require.NoError(t, err) + + assert.Equal(t, AccessAdmin, ta.Access) + + t.Run("team relationship is decoded", func(t *testing.T) { + assert.NotEmpty(t, ta.Team) + }) + + t.Run("workspace relationship is decoded", func(t *testing.T) { + assert.NotEmpty(t, ta.Workspace) + }) + }) + + t.Run("when the team access does not exist", func(t *testing.T) { + ta, err := client.TeamAccess.Read(ctx, "nonexisting") + assert.Nil(t, ta) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without a valid team access ID", func(t *testing.T) { + ta, err := client.TeamAccess.Read(ctx, badIdentifier) + assert.Nil(t, ta) + assert.EqualError(t, err, "invalid value for team access ID") + }) +} + +func TestTeamAccessesRemove(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + tmTest, tmTestCleanup := createTeam(t, client, orgTest) + defer tmTestCleanup() + + taTest, _ := createTeamAccess(t, client, tmTest, nil, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.TeamAccess.Remove(ctx, taTest.ID) + require.NoError(t, err) + + // Try loading the workspace - it should fail. + _, err = client.TeamAccess.Read(ctx, taTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the team access does not exist", func(t *testing.T) { + err := client.TeamAccess.Remove(ctx, taTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("when the team access ID is invalid", func(t *testing.T) { + err := client.TeamAccess.Remove(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for team access ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/team_member_test.go b/vendor/github.com/hashicorp/go-tfe/team_member_test.go new file mode 100644 index 000000000..1182bac5d --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/team_member_test.go @@ -0,0 +1,136 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTeamMembersList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + options := TeamMemberAddOptions{ + Usernames: []string{"admin"}, + } + err := client.TeamMembers.Add(ctx, tmTest.ID, options) + require.NoError(t, err) + + t.Run("with valid options", func(t *testing.T) { + users, err := client.TeamMembers.List(ctx, tmTest.ID) + require.NoError(t, err) + require.Equal(t, 1, len(users)) + + found := false + for _, user := range users { + if user.Username == "admin" { + found = true + break + } + } + + assert.True(t, found) + }) + + t.Run("when the team ID is invalid", func(t *testing.T) { + users, err := client.TeamMembers.List(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for team ID") + assert.Nil(t, users) + }) +} + +func TestTeamMembersAdd(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := TeamMemberAddOptions{ + Usernames: []string{"admin"}, + } + + err := client.TeamMembers.Add(ctx, tmTest.ID, options) + require.NoError(t, err) + + users, err := client.TeamMembers.List(ctx, tmTest.ID) + require.NoError(t, err) + + found := false + for _, user := range users { + if user.Username == "admin" { + found = true + break + } + } + + assert.True(t, found) + }) + + t.Run("when options is missing usernames", func(t *testing.T) { + err := client.TeamMembers.Add(ctx, tmTest.ID, TeamMemberAddOptions{}) + assert.EqualError(t, err, "usernames is required") + }) + + t.Run("when usernames is empty", func(t *testing.T) { + err := client.TeamMembers.Add(ctx, tmTest.ID, TeamMemberAddOptions{ + Usernames: []string{}, + }) + assert.EqualError(t, err, "invalid value for usernames") + }) + + t.Run("when the team ID is invalid", func(t *testing.T) { + err := client.TeamMembers.Add(ctx, badIdentifier, TeamMemberAddOptions{ + Usernames: []string{"user1"}, + }) + assert.EqualError(t, err, "invalid value for team ID") + }) +} + +func TestTeamMembersRemove(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + options := TeamMemberAddOptions{ + Usernames: []string{"admin"}, + } + err := client.TeamMembers.Add(ctx, tmTest.ID, options) + require.NoError(t, err) + + t.Run("with valid options", func(t *testing.T) { + options := TeamMemberRemoveOptions{ + Usernames: []string{"admin"}, + } + + err := client.TeamMembers.Remove(ctx, tmTest.ID, options) + assert.NoError(t, err) + }) + + t.Run("when options is missing usernames", func(t *testing.T) { + err := client.TeamMembers.Remove(ctx, tmTest.ID, TeamMemberRemoveOptions{}) + assert.EqualError(t, err, "usernames is required") + }) + + t.Run("when usernames is empty", func(t *testing.T) { + err := client.TeamMembers.Remove(ctx, tmTest.ID, TeamMemberRemoveOptions{ + Usernames: []string{}, + }) + assert.EqualError(t, err, "invalid value for usernames") + }) + + t.Run("when the team ID is invalid", func(t *testing.T) { + err := client.TeamMembers.Remove(ctx, badIdentifier, TeamMemberRemoveOptions{ + Usernames: []string{"user1"}, + }) + assert.EqualError(t, err, "invalid value for team ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/team_test.go b/vendor/github.com/hashicorp/go-tfe/team_test.go new file mode 100644 index 000000000..81cf0cb6d --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/team_test.go @@ -0,0 +1,216 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTeamsList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + tmTest1, tmTest1Cleanup := createTeam(t, client, orgTest) + defer tmTest1Cleanup() + tmTest2, tmTest2Cleanup := createTeam(t, client, orgTest) + defer tmTest2Cleanup() + + t.Run("without list options", func(t *testing.T) { + tl, err := client.Teams.List(ctx, orgTest.Name, TeamListOptions{}) + require.NoError(t, err) + assert.Contains(t, tl.Items, tmTest1) + assert.Contains(t, tl.Items, tmTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, tl.CurrentPage) + assert.Equal(t, 2, tl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + tl, err := client.Teams.List(ctx, orgTest.Name, TeamListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, tl.Items) + assert.Equal(t, 999, tl.CurrentPage) + assert.Equal(t, 2, tl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + tl, err := client.Teams.List(ctx, badIdentifier, TeamListOptions{}) + assert.Nil(t, tl) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestTeamsCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := TeamCreateOptions{ + Name: String("foo"), + } + + tm, err := client.Teams.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.Teams.Read(ctx, tm.ID) + require.NoError(t, err) + + for _, item := range []*Team{ + tm, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + } + }) + + t.Run("when options is missing name", func(t *testing.T) { + tm, err := client.Teams.Create(ctx, "foo", TeamCreateOptions{}) + assert.Nil(t, tm) + assert.EqualError(t, err, "name is required") + }) + + t.Run("when options has an invalid organization", func(t *testing.T) { + tm, err := client.Teams.Create(ctx, badIdentifier, TeamCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, tm) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestTeamsRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + tmTest, tmTestCleanup := createTeam(t, client, orgTest) + defer tmTestCleanup() + + t.Run("when the team exists", func(t *testing.T) { + tm, err := client.Teams.Read(ctx, tmTest.ID) + require.NoError(t, err) + assert.Equal(t, tmTest, tm) + + t.Run("permissions are properly decoded", func(t *testing.T) { + assert.True(t, tm.Permissions.CanDestroy) + }) + + t.Run("organization access is properly decoded", func(t *testing.T) { + assert.True(t, tm.OrganizationAccess.ManagePolicies) + assert.False(t, tm.OrganizationAccess.ManageWorkspaces) + }) + }) + + t.Run("when the team does not exist", func(t *testing.T) { + tm, err := client.Teams.Read(ctx, "nonexisting") + assert.Nil(t, tm) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without a valid team ID", func(t *testing.T) { + tm, err := client.Teams.Read(ctx, badIdentifier) + assert.Nil(t, tm) + assert.EqualError(t, err, "invalid value for team ID") + }) +} + +func TestTeamsUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + tmTest, tmTestCleanup := createTeam(t, client, orgTest) + defer tmTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := TeamUpdateOptions{ + Name: String("foo bar"), + OrganizationAccess: &OrganizationAccessOptions{ + ManagePolicies: Bool(false), + ManageVCSSettings: Bool(true)}, + } + + tm, err := client.Teams.Update(ctx, tmTest.ID, options) + require.NoError(t, err) + + refreshed, err := client.Teams.Read(ctx, tmTest.ID) + require.NoError(t, err) + + for _, item := range []*Team{ + tm, + refreshed, + } { + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, + *options.OrganizationAccess.ManagePolicies, + item.OrganizationAccess.ManagePolicies, + ) + assert.Equal(t, + *options.OrganizationAccess.ManageVCSSettings, + item.OrganizationAccess.ManageVCSSettings, + ) + } + }) + + t.Run("when the team does not exist", func(t *testing.T) { + tm, err := client.Teams.Update(ctx, "nonexisting", TeamUpdateOptions{ + Name: String("foo bar"), + }) + assert.Nil(t, tm) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without a valid team ID", func(t *testing.T) { + tm, err := client.Teams.Update(ctx, badIdentifier, TeamUpdateOptions{}) + assert.Nil(t, tm) + assert.EqualError(t, err, "invalid value for team ID") + }) +} + +func TestTeamsDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + tmTest, _ := createTeam(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.Teams.Delete(ctx, tmTest.ID) + require.NoError(t, err) + + // Try loading the workspace - it should fail. + _, err = client.Teams.Read(ctx, tmTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without valid team ID", func(t *testing.T) { + err := client.Teams.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for team ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/team_token_test.go b/vendor/github.com/hashicorp/go-tfe/team_token_test.go new file mode 100644 index 000000000..abbc69c13 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/team_token_test.go @@ -0,0 +1,92 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTeamTokensGenerate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + var tmToken string + t.Run("with valid options", func(t *testing.T) { + tt, err := client.TeamTokens.Generate(ctx, tmTest.ID) + require.NoError(t, err) + require.NotEmpty(t, tt.Token) + tmToken = tt.Token + }) + + t.Run("when a token already exists", func(t *testing.T) { + tt, err := client.TeamTokens.Generate(ctx, tmTest.ID) + require.NoError(t, err) + require.NotEmpty(t, tt.Token) + assert.NotEqual(t, tmToken, tt.Token) + }) + + t.Run("without valid team ID", func(t *testing.T) { + tt, err := client.TeamTokens.Generate(ctx, badIdentifier) + assert.Nil(t, tt) + assert.EqualError(t, err, "invalid value for team ID") + }) +} +func TestTeamTokensRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + _, ttTestCleanup := createTeamToken(t, client, tmTest) + + tt, err := client.TeamTokens.Read(ctx, tmTest.ID) + assert.NoError(t, err) + assert.NotEmpty(t, tt) + + ttTestCleanup() + }) + + t.Run("when a token doesn't exists", func(t *testing.T) { + tt, err := client.TeamTokens.Read(ctx, tmTest.ID) + assert.Equal(t, ErrResourceNotFound, err) + assert.Nil(t, tt) + }) + + t.Run("without valid organization", func(t *testing.T) { + tt, err := client.OrganizationTokens.Read(ctx, badIdentifier) + assert.Nil(t, tt) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestTeamTokensDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + tmTest, tmTestCleanup := createTeam(t, client, nil) + defer tmTestCleanup() + + createTeamToken(t, client, tmTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.TeamTokens.Delete(ctx, tmTest.ID) + assert.NoError(t, err) + }) + + t.Run("when a token does not exist", func(t *testing.T) { + err := client.TeamTokens.Delete(ctx, tmTest.ID) + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("without valid team ID", func(t *testing.T) { + err := client.TeamTokens.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for team ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt new file mode 100644 index 000000000..5716ca598 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt @@ -0,0 +1 @@ +bar diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe new file mode 100755 index 000000000..e69de29bb diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt @@ -0,0 +1 @@ +foo diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt new file mode 120000 index 000000000..416e3bd9e --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt @@ -0,0 +1 @@ +../foo.txt \ No newline at end of file diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt new file mode 100644 index 000000000..d0513b233 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt @@ -0,0 +1 @@ +zip diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf b/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf new file mode 100644 index 000000000..3911a2a9b --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf @@ -0,0 +1 @@ +resource "null_resource" "foo" {} diff --git a/vendor/github.com/hashicorp/go-tfe/tfe.go b/vendor/github.com/hashicorp/go-tfe/tfe.go index 00cda2ea2..c9d173b6a 100644 --- a/vendor/github.com/hashicorp/go-tfe/tfe.go +++ b/vendor/github.com/hashicorp/go-tfe/tfe.go @@ -106,6 +106,7 @@ type Client struct { Applies Applies ConfigurationVersions ConfigurationVersions + CostEstimations CostEstimations NotificationConfigurations NotificationConfigurations OAuthClients OAuthClients OAuthTokens OAuthTokens @@ -195,6 +196,7 @@ func NewClient(cfg *Config) (*Client, error) { // Create the services. client.Applies = &applies{client: client} client.ConfigurationVersions = &configurationVersions{client: client} + client.CostEstimations = &costEstimations{client: client} client.NotificationConfigurations = ¬ificationConfigurations{client: client} client.OAuthClients = &oAuthClients{client: client} client.OAuthTokens = &oAuthTokens{client: client} @@ -247,7 +249,7 @@ func (c *Client) retryHTTPBackoff(min, max time.Duration, attemptNum int, resp * } // Use the rate limit backoff function when we are rate limited. - if resp.StatusCode == 429 { + if resp != nil && resp.StatusCode == 429 { return rateLimitBackoff(min, max, attemptNum, resp) } diff --git a/vendor/github.com/hashicorp/go-tfe/tfe_test.go b/vendor/github.com/hashicorp/go-tfe/tfe_test.go new file mode 100644 index 000000000..bb23af2e8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/tfe_test.go @@ -0,0 +1,398 @@ +package tfe + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "golang.org/x/time/rate" +) + +func TestClient_newClient(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", "30") + w.WriteHeader(404) // We query the configured base URL which should return a 404. + })) + defer ts.Close() + + cfg := &Config{ + HTTPClient: ts.Client(), + } + + t.Run("uses env vars if values are missing", func(t *testing.T) { + defer setupEnvVars("abcd1234", ts.URL)() + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + if client.token != "abcd1234" { + t.Fatalf("unexpected token: %q", client.token) + } + if client.baseURL.String() != ts.URL+DefaultBasePath { + t.Fatalf("unexpected address: %q", client.baseURL.String()) + } + }) + + t.Run("fails if token is empty", func(t *testing.T) { + defer setupEnvVars("", "")() + + _, err := NewClient(cfg) + if err == nil || err.Error() != "missing API token" { + t.Fatalf("unexpected error: %v", err) + } + }) + + t.Run("makes a new client with good settings", func(t *testing.T) { + config := &Config{ + Address: ts.URL, + Token: "abcd1234", + HTTPClient: ts.Client(), + } + + client, err := NewClient(config) + if err != nil { + t.Fatal(err) + } + + if config.Address+DefaultBasePath != client.baseURL.String() { + t.Fatalf("unexpected client address %q", client.baseURL.String()) + } + if config.Token != client.token { + t.Fatalf("unexpected client token %q", client.token) + } + if ts.Client() != client.http.HTTPClient { + t.Fatal("unexpected HTTP client value") + } + }) +} + +func TestClient_defaultConfig(t *testing.T) { + t.Run("with no environment variables", func(t *testing.T) { + defer setupEnvVars("", "")() + + config := DefaultConfig() + + if config.Address != DefaultAddress { + t.Fatalf("expected %q, got %q", DefaultAddress, config.Address) + } + if config.Token != "" { + t.Fatalf("expected empty token, got %q", config.Token) + } + if config.HTTPClient == nil { + t.Fatalf("expected default http client, got %v", config.HTTPClient) + } + }) + + t.Run("with environment variables", func(t *testing.T) { + defer setupEnvVars("abcd1234", "https://mytfe.local")() + }) +} + +func TestClient_headers(t *testing.T) { + testedCalls := 0 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + testedCalls++ + + if testedCalls == 1 { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", "30") + w.WriteHeader(404) // We query the configured base URL which should return a 404. + return + } + + if r.Header.Get("Accept") != "application/vnd.api+json" { + t.Fatalf("unexpected accept header: %q", r.Header.Get("Accept")) + } + if r.Header.Get("Authorization") != "Bearer dummy-token" { + t.Fatalf("unexpected authorization header: %q", r.Header.Get("Authorization")) + } + if r.Header.Get("My-Custom-Header") != "foobar" { + t.Fatalf("unexpected custom header: %q", r.Header.Get("My-Custom-Header")) + } + if r.Header.Get("Terraform-Version") != "0.11.9" { + t.Fatalf("unexpected Terraform version header: %q", r.Header.Get("Terraform-Version")) + } + if r.Header.Get("User-Agent") != "go-tfe" { + t.Fatalf("unexpected user agent header: %q", r.Header.Get("User-Agent")) + } + })) + defer ts.Close() + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + Headers: make(http.Header), + HTTPClient: ts.Client(), + } + + // Set some custom header. + cfg.Headers.Set("My-Custom-Header", "foobar") + cfg.Headers.Set("Terraform-Version", "0.11.9") + + // This one should be overridden! + cfg.Headers.Set("Authorization", "bad-token") + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + // Make a few calls so we can check they all send the expected headers. + _, _ = client.Organizations.List(ctx, OrganizationListOptions{}) + _, _ = client.Plans.Logs(ctx, "plan-123456789") + _ = client.Runs.Apply(ctx, "run-123456789", RunApplyOptions{}) + _, _ = client.Workspaces.Lock(ctx, "ws-123456789", WorkspaceLockOptions{}) + _, _ = client.Workspaces.Read(ctx, "organization", "workspace") + + if testedCalls != 6 { + t.Fatalf("expected 6 tested calls, got: %d", testedCalls) + } +} + +func TestClient_userAgent(t *testing.T) { + testedCalls := 0 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + testedCalls++ + + if testedCalls == 1 { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", "30") + w.WriteHeader(404) // We query the configured base URL which should return a 404. + return + } + + if r.Header.Get("User-Agent") != "hashicorp" { + t.Fatalf("unexpected user agent header: %q", r.Header.Get("User-Agent")) + } + })) + defer ts.Close() + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + Headers: make(http.Header), + HTTPClient: ts.Client(), + } + + // Set a custom user agent. + cfg.Headers.Set("User-Agent", "hashicorp") + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + // Make a few calls so we can check they all send the expected headers. + _, _ = client.Organizations.List(ctx, OrganizationListOptions{}) + _, _ = client.Plans.Logs(ctx, "plan-123456789") + _ = client.Runs.Apply(ctx, "run-123456789", RunApplyOptions{}) + _, _ = client.Workspaces.Lock(ctx, "ws-123456789", WorkspaceLockOptions{}) + _, _ = client.Workspaces.Read(ctx, "organization", "workspace") + + if testedCalls != 6 { + t.Fatalf("expected 6 tested calls, got: %d", testedCalls) + } +} + +func TestClient_configureLimiter(t *testing.T) { + rateLimit := "" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", rateLimit) + w.WriteHeader(404) // We query the configured base URL which should return a 404. + })) + defer ts.Close() + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + HTTPClient: ts.Client(), + } + + cases := map[string]struct { + rate string + limit rate.Limit + burst int + }{ + "no-value": { + rate: "", + limit: rate.Inf, + burst: 0, + }, + "limit-0": { + rate: "0", + limit: rate.Inf, + burst: 0, + }, + "limit-30": { + rate: "30", + limit: rate.Limit(19.8), + burst: 9, + }, + "limit-100": { + rate: "100", + limit: rate.Limit(66), + burst: 33, + }, + } + + for name, tc := range cases { + // First set the test rate limit. + rateLimit = tc.rate + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + if client.limiter.Limit() != tc.limit { + t.Fatalf("test %s expected limit %f, got: %f", name, tc.limit, client.limiter.Limit()) + } + + if client.limiter.Burst() != tc.burst { + t.Fatalf("test %s expected burst %d, got: %d", name, tc.burst, client.limiter.Burst()) + } + } +} + +func TestClient_retryHTTPCheck(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", "30") + w.WriteHeader(404) // We query the configured base URL which should return a 404. + })) + defer ts.Close() + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + HTTPClient: ts.Client(), + } + + connErr := errors.New("connection error") + + cases := map[string]struct { + resp *http.Response + err error + retryServerErrors bool + checkOK bool + checkErr error + }{ + "429-no-server-errors": { + resp: &http.Response{StatusCode: 429}, + err: nil, + checkOK: true, + checkErr: nil, + }, + "429-with-server-errors": { + resp: &http.Response{StatusCode: 429}, + err: nil, + retryServerErrors: true, + checkOK: true, + checkErr: nil, + }, + "500-no-server-errors": { + resp: &http.Response{StatusCode: 500}, + err: nil, + checkOK: false, + checkErr: nil, + }, + "500-with-server-errors": { + resp: &http.Response{StatusCode: 500}, + err: nil, + retryServerErrors: true, + checkOK: true, + checkErr: nil, + }, + "err-no-server-errors": { + err: connErr, + checkOK: false, + checkErr: connErr, + }, + "err-with-server-errors": { + err: connErr, + retryServerErrors: true, + checkOK: true, + checkErr: connErr, + }, + } + + ctx := context.Background() + + for name, tc := range cases { + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + client.RetryServerErrors(tc.retryServerErrors) + + checkOK, checkErr := client.retryHTTPCheck(ctx, tc.resp, tc.err) + if checkOK != tc.checkOK { + t.Fatalf("test %s expected checkOK %t, got: %t", name, tc.checkOK, checkOK) + } + if checkErr != tc.checkErr { + t.Fatalf("test %s expected checkErr %v, got: %v", name, tc.checkErr, checkErr) + } + } +} + +func TestClient_retryHTTPBackoff(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.api+json") + w.Header().Set("X-RateLimit-Limit", "30") + w.WriteHeader(404) // We query the configured base URL which should return a 404. + })) + defer ts.Close() + + var attempts int + retryLogHook := func(attemptNum int, resp *http.Response) { + attempts++ + } + + cfg := &Config{ + Address: ts.URL, + Token: "dummy-token", + HTTPClient: ts.Client(), + RetryLogHook: retryLogHook, + } + + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + retries := 3 + resp := &http.Response{StatusCode: 500} + + for i := 0; i < retries; i++ { + client.retryHTTPBackoff(time.Second, time.Second, i, resp) + } + + if attempts != retries { + t.Fatalf("expected %d log hook callbacks, got: %d callbacks", retries, attempts) + } +} + +func setupEnvVars(token, address string) func() { + origToken := os.Getenv("TFE_TOKEN") + origAddress := os.Getenv("TFE_ADDRESS") + + os.Setenv("TFE_TOKEN", token) + os.Setenv("TFE_ADDRESS", address) + + return func() { + os.Setenv("TFE_TOKEN", origToken) + os.Setenv("TFE_ADDRESS", origAddress) + } +} diff --git a/vendor/github.com/hashicorp/go-tfe/user_test.go b/vendor/github.com/hashicorp/go-tfe/user_test.go new file mode 100644 index 000000000..2fd6c7503 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/user_test.go @@ -0,0 +1,79 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUsersReadCurrent(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + u, err := client.Users.ReadCurrent(ctx) + assert.NoError(t, err) + assert.NotEmpty(t, u.ID) + assert.NotEmpty(t, u.AvatarURL) + assert.NotEmpty(t, u.Username) + + t.Run("two factor options are decoded", func(t *testing.T) { + assert.NotNil(t, u.TwoFactor) + }) +} + +func TestUsersUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + uTest, err := client.Users.ReadCurrent(ctx) + require.NoError(t, err) + + // Make sure we reset the current user when were done. + defer func() { + client.Users.Update(ctx, UserUpdateOptions{ + Email: String(uTest.Email), + Username: String(uTest.Username), + }) + }() + + t.Run("without any options", func(t *testing.T) { + _, err := client.Users.Update(ctx, UserUpdateOptions{}) + require.NoError(t, err) + + u, err := client.Users.ReadCurrent(ctx) + assert.NoError(t, err) + assert.Equal(t, u, uTest) + }) + + t.Run("with a new username", func(t *testing.T) { + _, err := client.Users.Update(ctx, UserUpdateOptions{ + Username: String("NewTestUsername"), + }) + require.NoError(t, err) + + u, err := client.Users.ReadCurrent(ctx) + assert.NoError(t, err) + assert.Equal(t, "NewTestUsername", u.Username) + }) + + t.Run("with a new email address", func(t *testing.T) { + _, err := client.Users.Update(ctx, UserUpdateOptions{ + Email: String("newtestemail@hashicorp.com"), + }) + require.NoError(t, err) + + u, err := client.Users.ReadCurrent(ctx) + assert.NoError(t, err) + assert.Equal(t, "newtestemail@hashicorp.com", u.UnconfirmedEmail) + }) + + t.Run("with invalid email address", func(t *testing.T) { + u, err := client.Users.Update(ctx, UserUpdateOptions{ + Email: String("notamailaddress"), + }) + assert.Nil(t, u) + assert.Error(t, err) + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/variable_test.go b/vendor/github.com/hashicorp/go-tfe/variable_test.go new file mode 100644 index 000000000..3fe85d988 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/variable_test.go @@ -0,0 +1,295 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVariablesList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + vTest1, _ := createVariable(t, client, wTest) + vTest2, _ := createVariable(t, client, wTest) + + t.Run("without list options", func(t *testing.T) { + vl, err := client.Variables.List(ctx, VariableListOptions{ + Organization: String(orgTest.Name), + Workspace: String(wTest.Name), + }) + require.NoError(t, err) + assert.Contains(t, vl.Items, vTest1) + assert.Contains(t, vl.Items, vTest2) + + t.Skip("paging not supported yet in API") + assert.Equal(t, 1, vl.CurrentPage) + assert.Equal(t, 2, vl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + t.Skip("paging not supported yet in API") + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + vl, err := client.Variables.List(ctx, VariableListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + Organization: String(orgTest.Name), + Workspace: String(wTest.Name), + }) + require.NoError(t, err) + assert.Empty(t, vl.Items) + assert.Equal(t, 999, vl.CurrentPage) + assert.Equal(t, 2, vl.TotalCount) + }) + + t.Run("when options is missing an organization", func(t *testing.T) { + vl, err := client.Variables.List(ctx, VariableListOptions{ + Workspace: String(wTest.Name), + }) + assert.Nil(t, vl) + assert.EqualError(t, err, "organization is required") + }) + + t.Run("when options is missing an workspace", func(t *testing.T) { + vl, err := client.Variables.List(ctx, VariableListOptions{ + Organization: String(orgTest.Name), + }) + assert.Nil(t, vl) + assert.EqualError(t, err, "workspace is required") + }) +} + +func TestVariablesCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Workspace: wTest, + } + + v, err := client.Variables.Create(ctx, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.Value, v.Value) + assert.Equal(t, *options.Category, v.Category) + // The workspace isn't returned correcly by the API. + // assert.Equal(t, *options.Workspace, v.Workspace) + }) + + t.Run("when options has an empty string value", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(""), + Category: Category(CategoryTerraform), + Workspace: wTest, + } + + v, err := client.Variables.Create(ctx, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.Value, v.Value) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options is missing value", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(randomString(t)), + Category: Category(CategoryTerraform), + Workspace: wTest, + } + + v, err := client.Variables.Create(ctx, options) + require.NoError(t, err) + + assert.NotEmpty(t, v.ID) + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, "", v.Value) + assert.Equal(t, *options.Category, v.Category) + }) + + t.Run("when options is missing key", func(t *testing.T) { + options := VariableCreateOptions{ + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Workspace: wTest, + } + + _, err := client.Variables.Create(ctx, options) + assert.EqualError(t, err, "key is required") + }) + + t.Run("when options has an empty key", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(""), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Workspace: wTest, + } + + _, err := client.Variables.Create(ctx, options) + assert.EqualError(t, err, "key is required") + }) + + t.Run("when options is missing category", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Workspace: wTest, + } + + _, err := client.Variables.Create(ctx, options) + assert.EqualError(t, err, "category is required") + }) + + t.Run("when options is missing workspace", func(t *testing.T) { + options := VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + } + + _, err := client.Variables.Create(ctx, options) + assert.EqualError(t, err, "workspace is required") + }) +} + +func TestVariablesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + vTest, vTestCleanup := createVariable(t, client, nil) + defer vTestCleanup() + + t.Run("when the variable exists", func(t *testing.T) { + v, err := client.Variables.Read(ctx, vTest.ID) + require.NoError(t, err) + assert.Equal(t, vTest.ID, v.ID) + assert.Equal(t, vTest.Category, v.Category) + assert.Equal(t, vTest.HCL, v.HCL) + assert.Equal(t, vTest.Key, v.Key) + assert.Equal(t, vTest.Sensitive, v.Sensitive) + assert.Equal(t, vTest.Value, v.Value) + }) + + t.Run("when the variable does not exist", func(t *testing.T) { + v, err := client.Variables.Read(ctx, "nonexisting") + assert.Nil(t, v) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("without a valid variable ID", func(t *testing.T) { + v, err := client.Variables.Read(ctx, badIdentifier) + assert.Nil(t, v) + assert.EqualError(t, err, "invalid value for variable ID") + }) +} + +func TestVariablesUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + vTest, vTestCleanup := createVariable(t, client, nil) + defer vTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := VariableUpdateOptions{ + Key: String("newname"), + Value: String("newvalue"), + HCL: Bool(true), + } + + v, err := client.Variables.Update(ctx, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.HCL, v.HCL) + assert.Equal(t, *options.Value, v.Value) + }) + + t.Run("when updating a subset of values", func(t *testing.T) { + options := VariableUpdateOptions{ + Key: String("someothername"), + HCL: Bool(false), + } + + v, err := client.Variables.Update(ctx, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Key, v.Key) + assert.Equal(t, *options.HCL, v.HCL) + }) + + t.Run("with sensitive set", func(t *testing.T) { + options := VariableUpdateOptions{ + Sensitive: Bool(true), + } + + v, err := client.Variables.Update(ctx, vTest.ID, options) + require.NoError(t, err) + + assert.Equal(t, *options.Sensitive, v.Sensitive) + assert.Empty(t, v.Value) // Because its now sensitive + }) + + t.Run("without any changes", func(t *testing.T) { + vTest, vTestCleanup := createVariable(t, client, nil) + defer vTestCleanup() + + v, err := client.Variables.Update(ctx, vTest.ID, VariableUpdateOptions{}) + require.NoError(t, err) + + assert.Equal(t, vTest, v) + }) + + t.Run("with invalid variable ID", func(t *testing.T) { + _, err := client.Variables.Update(ctx, badIdentifier, VariableUpdateOptions{}) + assert.EqualError(t, err, "invalid value for variable ID") + }) +} + +func TestVariablesDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + wTest, wTestCleanup := createWorkspace(t, client, nil) + defer wTestCleanup() + + vTest, _ := createVariable(t, client, wTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.Variables.Delete(ctx, vTest.ID) + assert.NoError(t, err) + }) + + t.Run("with non existing variable ID", func(t *testing.T) { + err := client.Variables.Delete(ctx, "nonexisting") + assert.Equal(t, err, ErrResourceNotFound) + }) + + t.Run("with invalid variable ID", func(t *testing.T) { + err := client.Variables.Delete(ctx, badIdentifier) + assert.EqualError(t, err, "invalid value for variable ID") + }) +} diff --git a/vendor/github.com/hashicorp/go-tfe/workspace_test.go b/vendor/github.com/hashicorp/go-tfe/workspace_test.go new file mode 100644 index 000000000..fc853ecac --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/workspace_test.go @@ -0,0 +1,496 @@ +package tfe + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWorkspacesList(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest1, wTest1Cleanup := createWorkspace(t, client, orgTest) + defer wTest1Cleanup() + wTest2, wTest2Cleanup := createWorkspace(t, client, orgTest) + defer wTest2Cleanup() + + t.Run("without list options", func(t *testing.T) { + wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{}) + require.NoError(t, err) + assert.Contains(t, wl.Items, wTest1) + assert.Contains(t, wl.Items, wTest2) + assert.Equal(t, 1, wl.CurrentPage) + assert.Equal(t, 2, wl.TotalCount) + }) + + t.Run("with list options", func(t *testing.T) { + // Request a page number which is out of range. The result should + // be successful, but return no results if the paging options are + // properly passed along. + wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ + ListOptions: ListOptions{ + PageNumber: 999, + PageSize: 100, + }, + }) + require.NoError(t, err) + assert.Empty(t, wl.Items) + assert.Equal(t, 999, wl.CurrentPage) + assert.Equal(t, 2, wl.TotalCount) + }) + + t.Run("when searching a known workspace", func(t *testing.T) { + // Use a known workspace prefix as search attribute. The result + // should be successful and only contain the matching workspace. + wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ + Search: String(wTest1.Name[:len(wTest1.Name)-5]), + }) + require.NoError(t, err) + assert.Contains(t, wl.Items, wTest1) + assert.NotContains(t, wl.Items, wTest2) + assert.Equal(t, 1, wl.CurrentPage) + assert.Equal(t, 1, wl.TotalCount) + }) + + t.Run("when searching an unknown workspace", func(t *testing.T) { + // Use a nonexisting workspace name as search attribute. The result + // should be successful, but return no results. + wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ + Search: String("nonexisting"), + }) + require.NoError(t, err) + assert.Empty(t, wl.Items) + assert.Equal(t, 1, wl.CurrentPage) + assert.Equal(t, 0, wl.TotalCount) + }) + + t.Run("without a valid organization", func(t *testing.T) { + wl, err := client.Workspaces.List(ctx, badIdentifier, WorkspaceListOptions{}) + assert.Nil(t, wl) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestWorkspacesCreate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + options := WorkspaceCreateOptions{ + Name: String("foo"), + AutoApply: Bool(true), + QueueAllRuns: Bool(true), + TerraformVersion: String("0.11.0"), + WorkingDirectory: String("bar/"), + } + + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) + require.NoError(t, err) + + for _, item := range []*Workspace{ + w, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, *options.AutoApply, item.AutoApply) + assert.Equal(t, *options.QueueAllRuns, item.QueueAllRuns) + assert.Equal(t, *options.TerraformVersion, item.TerraformVersion) + assert.Equal(t, *options.WorkingDirectory, item.WorkingDirectory) + } + }) + + t.Run("when options is missing name", func(t *testing.T) { + w, err := client.Workspaces.Create(ctx, "foo", WorkspaceCreateOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "name is required") + }) + + t.Run("when options has an invalid name", func(t *testing.T) { + w, err := client.Workspaces.Create(ctx, "foo", WorkspaceCreateOptions{ + Name: String(badIdentifier), + }) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for name") + }) + + t.Run("when options has an invalid organization", func(t *testing.T) { + w, err := client.Workspaces.Create(ctx, badIdentifier, WorkspaceCreateOptions{ + Name: String("foo"), + }) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when an error is returned from the api", func(t *testing.T) { + w, err := client.Workspaces.Create(ctx, "bar", WorkspaceCreateOptions{ + Name: String("bar"), + TerraformVersion: String("nonexisting"), + }) + assert.Nil(t, w) + assert.Error(t, err) + }) +} + +func TestWorkspacesRead(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, wTestCleanup := createWorkspace(t, client, orgTest) + defer wTestCleanup() + + t.Run("when the workspace exists", func(t *testing.T) { + w, err := client.Workspaces.Read(ctx, orgTest.Name, wTest.Name) + require.NoError(t, err) + assert.Equal(t, wTest, w) + + t.Run("permissions are properly decoded", func(t *testing.T) { + assert.True(t, w.Permissions.CanDestroy) + }) + + t.Run("relationships are properly decoded", func(t *testing.T) { + assert.Equal(t, orgTest.Name, w.Organization.Name) + }) + + t.Run("timestamps are properly decoded", func(t *testing.T) { + assert.NotEmpty(t, w.CreatedAt) + }) + }) + + t.Run("when the workspace does not exist", func(t *testing.T) { + w, err := client.Workspaces.Read(ctx, orgTest.Name, "nonexisting") + assert.Nil(t, w) + assert.Error(t, err) + }) + + t.Run("when the organization does not exist", func(t *testing.T) { + w, err := client.Workspaces.Read(ctx, "nonexisting", "nonexisting") + assert.Nil(t, w) + assert.Error(t, err) + }) + + t.Run("without a valid organization", func(t *testing.T) { + w, err := client.Workspaces.Read(ctx, badIdentifier, wTest.Name) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("without a valid workspace", func(t *testing.T) { + w, err := client.Workspaces.Read(ctx, orgTest.Name, badIdentifier) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace") + }) +} + +func TestWorkspacesUpdate(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + t.Run("when updating a subset of values", func(t *testing.T) { + options := WorkspaceUpdateOptions{ + Name: String(wTest.Name), + AutoApply: Bool(true), + QueueAllRuns: Bool(true), + TerraformVersion: String("0.10.0"), + } + + wAfter, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + require.NoError(t, err) + + assert.Equal(t, wTest.Name, wAfter.Name) + assert.NotEqual(t, wTest.AutoApply, wAfter.AutoApply) + assert.NotEqual(t, wTest.QueueAllRuns, wAfter.QueueAllRuns) + assert.NotEqual(t, wTest.TerraformVersion, wAfter.TerraformVersion) + assert.Equal(t, wTest.WorkingDirectory, wAfter.WorkingDirectory) + }) + + t.Run("with valid options", func(t *testing.T) { + options := WorkspaceUpdateOptions{ + Name: String(randomString(t)), + AutoApply: Bool(false), + QueueAllRuns: Bool(false), + TerraformVersion: String("0.11.1"), + WorkingDirectory: String("baz/"), + } + + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view of the workspace from the API + refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) + require.NoError(t, err) + + for _, item := range []*Workspace{ + w, + refreshed, + } { + assert.Equal(t, *options.Name, item.Name) + assert.Equal(t, *options.AutoApply, item.AutoApply) + assert.Equal(t, *options.QueueAllRuns, item.QueueAllRuns) + assert.Equal(t, *options.TerraformVersion, item.TerraformVersion) + assert.Equal(t, *options.WorkingDirectory, item.WorkingDirectory) + } + }) + + t.Run("when an error is returned from the api", func(t *testing.T) { + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, WorkspaceUpdateOptions{ + TerraformVersion: String("nonexisting"), + }) + assert.Nil(t, w) + assert.Error(t, err) + }) + + t.Run("when options has an invalid name", func(t *testing.T) { + w, err := client.Workspaces.Update(ctx, orgTest.Name, badIdentifier, WorkspaceUpdateOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace") + }) + + t.Run("when options has an invalid organization", func(t *testing.T) { + w, err := client.Workspaces.Update(ctx, badIdentifier, wTest.Name, WorkspaceUpdateOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for organization") + }) +} + +func TestWorkspacesDelete(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + err := client.Workspaces.Delete(ctx, orgTest.Name, wTest.Name) + require.NoError(t, err) + + // Try loading the workspace - it should fail. + _, err = client.Workspaces.Read(ctx, orgTest.Name, wTest.Name) + assert.Equal(t, ErrResourceNotFound, err) + }) + + t.Run("when organization is invalid", func(t *testing.T) { + err := client.Workspaces.Delete(ctx, badIdentifier, wTest.Name) + assert.EqualError(t, err, "invalid value for organization") + }) + + t.Run("when workspace is invalid", func(t *testing.T) { + err := client.Workspaces.Delete(ctx, orgTest.Name, badIdentifier) + assert.EqualError(t, err, "invalid value for workspace") + }) +} + +func TestWorkspacesRemoveVCSConnection(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspaceWithVCS(t, client, orgTest) + + t.Run("remove vcs integration", func(t *testing.T) { + w, err := client.Workspaces.RemoveVCSConnection(ctx, orgTest.Name, wTest.Name) + require.NoError(t, err) + assert.Equal(t, (*VCSRepo)(nil), w.VCSRepo) + }) +} + +func TestWorkspacesLock(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + t.Run("with valid options", func(t *testing.T) { + w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + require.NoError(t, err) + assert.True(t, w.Locked) + }) + + t.Run("when workspace is already locked", func(t *testing.T) { + _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + assert.Equal(t, ErrWorkspaceLocked, err) + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + w, err := client.Workspaces.Lock(ctx, badIdentifier, WorkspaceLockOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestWorkspacesUnlock(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + if err != nil { + orgTestCleanup() + } + require.NoError(t, err) + require.True(t, w.Locked) + + t.Run("with valid options", func(t *testing.T) { + w, err := client.Workspaces.Unlock(ctx, wTest.ID) + require.NoError(t, err) + assert.False(t, w.Locked) + }) + + t.Run("when workspace is already unlocked", func(t *testing.T) { + _, err := client.Workspaces.Unlock(ctx, wTest.ID) + assert.Equal(t, ErrWorkspaceNotLocked, err) + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + w, err := client.Workspaces.Unlock(ctx, badIdentifier) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestWorkspacesForceUnlock(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, _ := createWorkspace(t, client, orgTest) + + w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + if err != nil { + orgTestCleanup() + } + require.NoError(t, err) + require.True(t, w.Locked) + + t.Run("with valid options", func(t *testing.T) { + w, err := client.Workspaces.ForceUnlock(ctx, wTest.ID) + require.NoError(t, err) + assert.False(t, w.Locked) + }) + + t.Run("when workspace is already unlocked", func(t *testing.T) { + _, err := client.Workspaces.ForceUnlock(ctx, wTest.ID) + assert.Equal(t, ErrWorkspaceNotLocked, err) + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + w, err := client.Workspaces.ForceUnlock(ctx, badIdentifier) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestWorkspacesAssignSSHKey(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, wTestCleanup := createWorkspace(t, client, orgTest) + defer wTestCleanup() + + sshKeyTest, sshKeyTestCleanup := createSSHKey(t, client, orgTest) + defer sshKeyTestCleanup() + + t.Run("with valid options", func(t *testing.T) { + w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ + SSHKeyID: String(sshKeyTest.ID), + }) + require.NoError(t, err) + require.NotNil(t, w.SSHKey) + assert.Equal(t, w.SSHKey.ID, sshKeyTest.ID) + }) + + t.Run("without an SSH key ID", func(t *testing.T) { + w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{}) + assert.Nil(t, w) + assert.EqualError(t, err, "SSH key ID is required") + }) + + t.Run("without a valid SSH key ID", func(t *testing.T) { + w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ + SSHKeyID: String(badIdentifier), + }) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for SSH key ID") + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + w, err := client.Workspaces.AssignSSHKey(ctx, badIdentifier, WorkspaceAssignSSHKeyOptions{ + SSHKeyID: String(sshKeyTest.ID), + }) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} + +func TestWorkspacesUnassignSSHKey(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + wTest, wTestCleanup := createWorkspace(t, client, orgTest) + defer wTestCleanup() + + sshKeyTest, sshKeyTestCleanup := createSSHKey(t, client, orgTest) + defer sshKeyTestCleanup() + + w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ + SSHKeyID: String(sshKeyTest.ID), + }) + if err != nil { + orgTestCleanup() + } + require.NoError(t, err) + require.NotNil(t, w.SSHKey) + require.Equal(t, w.SSHKey.ID, sshKeyTest.ID) + + t.Run("with valid options", func(t *testing.T) { + w, err := client.Workspaces.UnassignSSHKey(ctx, wTest.ID) + assert.Nil(t, err) + assert.Nil(t, w.SSHKey) + }) + + t.Run("without a valid workspace ID", func(t *testing.T) { + w, err := client.Workspaces.UnassignSSHKey(ctx, badIdentifier) + assert.Nil(t, w) + assert.EqualError(t, err, "invalid value for workspace ID") + }) +} From c7a023a95c75bac5db5f8b1bc90c1b06fb69bc87 Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Wed, 24 Apr 2019 16:40:13 -0700 Subject: [PATCH 4/9] update test for new go-tfe version Signed-off-by: Paul Thrasher --- backend/remote/backend_common.go | 6 - backend/remote/backend_mock.go | 131 +++++++++++++++++- backend/remote/backend_plan_test.go | 30 ++++ .../test-fixtures/cost-estimation/ce.log | 6 + .../test-fixtures/cost-estimation/main.tf | 1 + .../test-fixtures/cost-estimation/plan.log | 21 +++ backend/remote/testing.go | 1 + 7 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 backend/remote/test-fixtures/cost-estimation/ce.log create mode 100644 backend/remote/test-fixtures/cost-estimation/main.tf create mode 100644 backend/remote/test-fixtures/cost-estimation/plan.log diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 91a13b763..d53b0a791 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -273,12 +273,6 @@ func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend. default: return fmt.Errorf("Unknown or unexpected cost estimation state: %s", ce.Status) } - - if b.CLI != nil { - b.CLI.Output("------------------------------------------------------------------------") - } - - return nil } func (b *Remote) checkPolicy(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { diff --git a/backend/remote/backend_mock.go b/backend/remote/backend_mock.go index 8985dd0e3..59a1a1393 100644 --- a/backend/remote/backend_mock.go +++ b/backend/remote/backend_mock.go @@ -21,6 +21,7 @@ import ( type mockClient struct { Applies *mockApplies ConfigurationVersions *mockConfigurationVersions + CostEstimations *mockCostEstimations Organizations *mockOrganizations Plans *mockPlans PolicyChecks *mockPolicyChecks @@ -33,6 +34,7 @@ func newMockClient() *mockClient { c := &mockClient{} c.Applies = newMockApplies(c) c.ConfigurationVersions = newMockConfigurationVersions(c) + c.CostEstimations = newMockCostEstimations(c) c.Organizations = newMockOrganizations(c) c.Plans = newMockPlans(c) c.PolicyChecks = newMockPolicyChecks(c) @@ -212,6 +214,111 @@ func (m *mockConfigurationVersions) Upload(ctx context.Context, url, path string return nil } +type mockCostEstimations struct { + client *mockClient + estimations map[string]*tfe.CostEstimation + logs map[string]string +} + +func newMockCostEstimations(client *mockClient) *mockCostEstimations { + return &mockCostEstimations{ + client: client, + estimations: make(map[string]*tfe.CostEstimation), + logs: make(map[string]string), + } +} + +// create is a helper function to create a mock plan that uses the configured +// working directory to find the logfile. +func (m *mockCostEstimations) create(cvID, workspaceID string) (*tfe.CostEstimation, error) { + id := generateID("ce-") + + ce := &tfe.CostEstimation{ + ID: id, + Status: tfe.CostEstimationQueued, + } + + w, ok := m.client.Workspaces.workspaceIDs[workspaceID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + logfile := filepath.Join( + m.client.ConfigurationVersions.uploadPaths[cvID], + w.WorkingDirectory, + "ce.log", + ) + + if _, err := os.Stat(logfile); os.IsNotExist(err) { + return nil, nil + } + + m.logs[ce.ID] = logfile + m.estimations[ce.ID] = ce + + return ce, nil +} + +func (m *mockCostEstimations) Read(ctx context.Context, costEstimationID string) (*tfe.CostEstimation, error) { + ce, ok := m.estimations[costEstimationID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + logfile, ok := m.logs[ce.ID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + if _, err := os.Stat(logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("logfile does not exist") + } + + logs, err := ioutil.ReadFile(logfile) + if err != nil { + return nil, err + } + + if bytes.Contains(logs, []byte("SKU")) { + ce.Status = tfe.CostEstimationFinished + } else { + // As this is an unexpected state, we say the estimation errored. + ce.Status = tfe.CostEstimationErrored + } + + return ce, nil +} + +func (m *mockCostEstimations) Logs(ctx context.Context, costEstimationID string) (io.Reader, error) { + ce, ok := m.estimations[costEstimationID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + logfile, ok := m.logs[ce.ID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + if _, err := os.Stat(logfile); os.IsNotExist(err) { + return bytes.NewBufferString("logfile does not exist"), nil + } + + logs, err := ioutil.ReadFile(logfile) + if err != nil { + return nil, err + } + + if bytes.Contains(logs, []byte("SKU")) { + ce.Status = tfe.CostEstimationFinished + } else { + // As this is an unexpected state, we say the estimation errored. + ce.Status = tfe.CostEstimationErrored + } + + return bytes.NewBuffer(logs), nil +} + // mockInput is a mock implementation of terraform.UIInput. type mockInput struct { answers map[string]string @@ -652,19 +759,25 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t return nil, err } + ce, err := m.client.CostEstimations.create(options.ConfigurationVersion.ID, options.Workspace.ID) + if err != nil { + return nil, err + } + pc, err := m.client.PolicyChecks.create(options.ConfigurationVersion.ID, options.Workspace.ID) if err != nil { return nil, err } r := &tfe.Run{ - ID: generateID("run-"), - Actions: &tfe.RunActions{IsCancelable: true}, - Apply: a, - HasChanges: false, - Permissions: &tfe.RunPermissions{}, - Plan: p, - Status: tfe.RunPending, + ID: generateID("run-"), + Actions: &tfe.RunActions{IsCancelable: true}, + Apply: a, + CostEstimation: ce, + HasChanges: false, + Permissions: &tfe.RunPermissions{}, + Plan: p, + Status: tfe.RunPending, } if pc != nil { @@ -1038,6 +1151,10 @@ func (m *mockWorkspaces) UnassignSSHKey(ctx context.Context, workspaceID string) panic("not implemented") } +func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organizationID string, workspaceID string) (*tfe.Workspace, error) { + panic("not implemented") +} + const alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func generateID(s string) string { diff --git a/backend/remote/backend_plan_test.go b/backend/remote/backend_plan_test.go index 389444200..d89ef8685 100644 --- a/backend/remote/backend_plan_test.go +++ b/backend/remote/backend_plan_test.go @@ -655,6 +655,36 @@ func TestRemote_planWithWorkingDirectory(t *testing.T) { } } +func TestRemote_costEstimationFinish(t *testing.T) { + b := testBackendDefault(t) + + op, configCleanup := testOperationPlan(t, "./test-fixtures/cost-estimation") + defer configCleanup() + + 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()) + } + + output := b.CLI.(*cli.MockUi).OutputWriter.String() + if !strings.Contains(output, "Running plan in the remote backend") { + t.Fatalf("expected remote backend header in output: %s", output) + } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summary in output: %s", output) + } + if !strings.Contains(output, "SKU") { + t.Fatalf("expected cost estimation result in output: %s", output) + } +} + func TestRemote_planPolicyPass(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() diff --git a/backend/remote/test-fixtures/cost-estimation/ce.log b/backend/remote/test-fixtures/cost-estimation/ce.log new file mode 100644 index 000000000..d73c8915b --- /dev/null +++ b/backend/remote/test-fixtures/cost-estimation/ce.log @@ -0,0 +1,6 @@ ++---------+------+-----+-------------+----------------------+ +| PRODUCT | NAME | SKU | DESCRIPTION | DELTA | ++---------+------+-----+-------------+----------------------+ ++---------+------+-----+-------------+----------------------+ +| TOTAL | $0.000 USD / 720 HRS | ++---------+------+-----+-------------+----------------------+ \ No newline at end of file diff --git a/backend/remote/test-fixtures/cost-estimation/main.tf b/backend/remote/test-fixtures/cost-estimation/main.tf new file mode 100644 index 000000000..3911a2a9b --- /dev/null +++ b/backend/remote/test-fixtures/cost-estimation/main.tf @@ -0,0 +1 @@ +resource "null_resource" "foo" {} diff --git a/backend/remote/test-fixtures/cost-estimation/plan.log b/backend/remote/test-fixtures/cost-estimation/plan.log new file mode 100644 index 000000000..5849e5759 --- /dev/null +++ b/backend/remote/test-fixtures/cost-estimation/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/backend/remote/testing.go b/backend/remote/testing.go index 09c541897..2213ba18c 100644 --- a/backend/remote/testing.go +++ b/backend/remote/testing.go @@ -115,6 +115,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) { b.CLI = cli.NewMockUi() b.client.Applies = mc.Applies b.client.ConfigurationVersions = mc.ConfigurationVersions + b.client.CostEstimations = mc.CostEstimations b.client.Organizations = mc.Organizations b.client.Plans = mc.Plans b.client.PolicyChecks = mc.PolicyChecks From cc38debace50a9b9a52205c0a1279cde9fa118b3 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Thu, 25 Apr 2019 09:29:52 +0200 Subject: [PATCH 5/9] Fixup the `go-tfe` update ``` go get github.com/hashicorp/go-tfe go mod tidy go mod vendor ``` --- go.sum | 4 - .../github.com/hashicorp/go-tfe/apply_test.go | 65 -- .../go-tfe/configuration_version_test.go | 195 ----- .../hashicorp/go-tfe/cost_estimation_test.go | 92 --- .../go-tfe/examples/organizations/main.go | 39 - .../go-tfe/examples/workspaces/main.go | 40 -- .../hashicorp/go-tfe/helper_test.go | 666 ------------------ .../hashicorp/go-tfe/logreader_test.go | 253 ------- .../go-tfe/notification_configuration_test.go | 218 ------ .../hashicorp/go-tfe/oauth_client_test.go | 226 ------ .../hashicorp/go-tfe/oauth_token_test.go | 189 ----- .../hashicorp/go-tfe/organization_test.go | 367 ---------- .../go-tfe/organization_token_test.go | 93 --- .../github.com/hashicorp/go-tfe/plan_test.go | 66 -- .../hashicorp/go-tfe/policy_check_test.go | 200 ------ .../hashicorp/go-tfe/policy_set_test.go | 454 ------------ .../hashicorp/go-tfe/policy_test.go | 393 ----------- .../github.com/hashicorp/go-tfe/run_test.go | 277 -------- .../hashicorp/go-tfe/ssh_key_test.go | 219 ------ .../hashicorp/go-tfe/state_version_test.go | 350 --------- .../hashicorp/go-tfe/team_access_test.go | 214 ------ .../hashicorp/go-tfe/team_member_test.go | 136 ---- .../github.com/hashicorp/go-tfe/team_test.go | 216 ------ .../hashicorp/go-tfe/team_token_test.go | 92 --- .../go-tfe/test-fixtures/archive-dir/bar.txt | 1 - .../go-tfe/test-fixtures/archive-dir/exe | 0 .../go-tfe/test-fixtures/archive-dir/foo.txt | 1 - .../test-fixtures/archive-dir/sub/foo.txt | 1 - .../test-fixtures/archive-dir/sub/zip.txt | 1 - .../test-fixtures/config-version/main.tf | 1 - .../github.com/hashicorp/go-tfe/tfe_test.go | 398 ----------- .../github.com/hashicorp/go-tfe/user_test.go | 79 --- .../hashicorp/go-tfe/variable_test.go | 295 -------- .../hashicorp/go-tfe/workspace_test.go | 496 ------------- vendor/modules.txt | 2 +- 35 files changed, 1 insertion(+), 6338 deletions(-) delete mode 100644 vendor/github.com/hashicorp/go-tfe/apply_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/configuration_version_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/helper_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/logreader_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/oauth_client_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/oauth_token_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/organization_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/organization_token_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/plan_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/policy_check_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/policy_set_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/policy_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/run_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/ssh_key_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/state_version_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/team_access_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/team_member_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/team_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/team_token_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt delete mode 100755 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe delete mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt delete mode 120000 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt delete mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt delete mode 100644 vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf delete mode 100644 vendor/github.com/hashicorp/go-tfe/tfe_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/user_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/variable_test.go delete mode 100644 vendor/github.com/hashicorp/go-tfe/workspace_test.go diff --git a/go.sum b/go.sum index 3c46c122f..3e0545274 100644 --- a/go.sum +++ b/go.sum @@ -189,10 +189,6 @@ github.com/hashicorp/go-slug v0.3.0 h1:L0c+AvH/J64iMNF4VqRaRku2DMTEuHioPVS7kMjWI github.com/hashicorp/go-slug v0.3.0/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.3.4 h1:A9pKjZMDTSGozXf2wQlWhBI7QoxCoas14Xg/TSiEAV8= -github.com/hashicorp/go-tfe v0.3.4/go.mod h1:Vssg8/lwVz+PyJ/nAK97zYmXxxLe28MCIMhKo+rva1o= -github.com/hashicorp/go-tfe v0.3.15-0.20190415182703-eb3b9edefdda h1:qWY67Uh98jh4BMYSnLVIXsz2AF1hRiHG4k43wzn1NoI= -github.com/hashicorp/go-tfe v0.3.15-0.20190415182703-eb3b9edefdda/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM= github.com/hashicorp/go-tfe v0.3.16 h1:GS2yv580p0co4j3FBVaC6Zahd9mxdCGehhJ0qqzFMH0= github.com/hashicorp/go-tfe v0.3.16/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= diff --git a/vendor/github.com/hashicorp/go-tfe/apply_test.go b/vendor/github.com/hashicorp/go-tfe/apply_test.go deleted file mode 100644 index bdc4b5d4a..000000000 --- a/vendor/github.com/hashicorp/go-tfe/apply_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package tfe - -import ( - "context" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAppliesRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createAppliedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the plan exists", func(t *testing.T) { - a, err := client.Applies.Read(ctx, rTest.Apply.ID) - require.NoError(t, err) - assert.NotEmpty(t, a.LogReadURL) - assert.Equal(t, a.Status, ApplyFinished) - assert.NotEmpty(t, a.StatusTimestamps) - }) - - t.Run("when the apply does not exist", func(t *testing.T) { - a, err := client.Applies.Read(ctx, "nonexisting") - assert.Nil(t, a) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid apply ID", func(t *testing.T) { - a, err := client.Applies.Read(ctx, badIdentifier) - assert.Nil(t, a) - assert.EqualError(t, err, "invalid value for apply ID") - }) -} - -func TestAppliesLogs(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createAppliedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the log exists", func(t *testing.T) { - a, err := client.Applies.Read(ctx, rTest.Apply.ID) - require.NoError(t, err) - - logReader, err := client.Applies.Logs(ctx, a.ID) - require.NoError(t, err) - - logs, err := ioutil.ReadAll(logReader) - require.NoError(t, err) - - assert.Contains(t, string(logs), "1 added, 0 changed, 0 destroyed") - }) - - t.Run("when the log does not exist", func(t *testing.T) { - logs, err := client.Applies.Logs(ctx, "nonexisting") - assert.Nil(t, logs) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go b/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go deleted file mode 100644 index e2766d516..000000000 --- a/vendor/github.com/hashicorp/go-tfe/configuration_version_test.go +++ /dev/null @@ -1,195 +0,0 @@ -package tfe - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestConfigurationVersionsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - cvTest1, cvTest1Cleanup := createConfigurationVersion(t, client, wTest) - defer cvTest1Cleanup() - cvTest2, cvTest2Cleanup := createConfigurationVersion(t, client, wTest) - defer cvTest2Cleanup() - - t.Run("without list options", func(t *testing.T) { - options := ConfigurationVersionListOptions{} - - cvl, err := client.ConfigurationVersions.List(ctx, wTest.ID, options) - require.NoError(t, err) - - // We need to strip the upload URL as that is a dynamic link. - cvTest1.UploadURL = "" - cvTest2.UploadURL = "" - - // And for the retrieved configuration versions as well. - for _, cv := range cvl.Items { - cv.UploadURL = "" - } - - assert.Contains(t, cvl.Items, cvTest1) - assert.Contains(t, cvl.Items, cvTest2) - assert.Equal(t, 1, cvl.CurrentPage) - assert.Equal(t, 2, cvl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - options := ConfigurationVersionListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - } - - cvl, err := client.ConfigurationVersions.List(ctx, wTest.ID, options) - require.NoError(t, err) - assert.Empty(t, cvl.Items) - assert.Equal(t, 999, cvl.CurrentPage) - assert.Equal(t, 2, cvl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - options := ConfigurationVersionListOptions{} - - cvl, err := client.ConfigurationVersions.List(ctx, badIdentifier, options) - assert.Nil(t, cvl) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestConfigurationVersionsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - cv, err := client.ConfigurationVersions.Create(ctx, - wTest.ID, - ConfigurationVersionCreateOptions{}, - ) - require.NoError(t, err) - - // Get a refreshed view of the configuration version. - refreshed, err := client.ConfigurationVersions.Read(ctx, cv.ID) - require.NoError(t, err) - - for _, item := range []*ConfigurationVersion{ - cv, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Empty(t, item.Error) - assert.Equal(t, item.Source, ConfigurationSourceAPI) - assert.Equal(t, item.Status, ConfigurationPending) - assert.NotEmpty(t, item.UploadURL) - } - }) - - t.Run("with invalid workspace id", func(t *testing.T) { - cv, err := client.ConfigurationVersions.Create( - ctx, - badIdentifier, - ConfigurationVersionCreateOptions{}, - ) - assert.Nil(t, cv) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestConfigurationVersionsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - cvTest, cvTestCleanup := createConfigurationVersion(t, client, nil) - defer cvTestCleanup() - - t.Run("when the configuration version exists", func(t *testing.T) { - cv, err := client.ConfigurationVersions.Read(ctx, cvTest.ID) - require.NoError(t, err) - - // Don't compare the UploadURL because it will be generated twice in - // this test - once at creation of the configuration version, and - // again during the GET. - cvTest.UploadURL, cv.UploadURL = "", "" - - assert.Equal(t, cvTest, cv) - }) - - t.Run("when the configuration version does not exist", func(t *testing.T) { - cv, err := client.ConfigurationVersions.Read(ctx, "nonexisting") - assert.Nil(t, cv) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid configuration version id", func(t *testing.T) { - cv, err := client.ConfigurationVersions.Read(ctx, badIdentifier) - assert.Nil(t, cv) - assert.EqualError(t, err, "invalid value for configuration version ID") - }) -} - -func TestConfigurationVersionsUpload(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - cv, cvCleanup := createConfigurationVersion(t, client, nil) - defer cvCleanup() - - t.Run("with valid options", func(t *testing.T) { - err := client.ConfigurationVersions.Upload( - ctx, - cv.UploadURL, - "test-fixtures/config-version", - ) - require.NoError(t, err) - - // We do this is a small loop, because it can take a second - // before the upload is finished. - for i := 0; ; i++ { - refreshed, err := client.ConfigurationVersions.Read(ctx, cv.ID) - require.NoError(t, err) - - if refreshed.Status == ConfigurationUploaded { - break - } - - if i > 10 { - t.Fatal("Timeout waiting for the configuration version to be uploaded") - } - - time.Sleep(1 * time.Second) - } - }) - - t.Run("without a valid upload URL", func(t *testing.T) { - err := client.ConfigurationVersions.Upload( - ctx, - cv.UploadURL[:len(cv.UploadURL)-10]+"nonexisting", - "test-fixtures/config-version", - ) - assert.Error(t, err) - }) - - t.Run("without a valid path", func(t *testing.T) { - err := client.ConfigurationVersions.Upload( - ctx, - cv.UploadURL, - "nonexisting", - ) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go b/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go deleted file mode 100644 index 1c89aa7c3..000000000 --- a/vendor/github.com/hashicorp/go-tfe/cost_estimation_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package tfe - -import ( - "context" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCostEstimationsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - // Enable cost estimation for the test organization. - orgTest, err := client.Organizations.Update( - ctx, - orgTest.Name, - OrganizationUpdateOptions{ - CostEstimationEnabled: Bool(true), - }, - ) - require.NoError(t, err) - - wTest, _ := createWorkspace(t, client, orgTest) - rTest, _ := createPlannedRun(t, client, wTest) - - t.Run("when the costEstimation exists", func(t *testing.T) { - ce, err := client.CostEstimations.Read(ctx, rTest.CostEstimation.ID) - require.NoError(t, err) - assert.Equal(t, ce.Status, CostEstimationFinished) - assert.NotEmpty(t, ce.StatusTimestamps) - }) - - t.Run("when the costEstimation does not exist", func(t *testing.T) { - ce, err := client.CostEstimations.Read(ctx, "nonexisting") - assert.Nil(t, ce) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("with invalid costEstimation ID", func(t *testing.T) { - ce, err := client.CostEstimations.Read(ctx, badIdentifier) - assert.Nil(t, ce) - assert.EqualError(t, err, "invalid value for cost estimation ID") - }) -} - -func TestCostEstimationsLogs(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - // Enable cost estimation for the test organization. - orgTest, err := client.Organizations.Update( - ctx, - orgTest.Name, - OrganizationUpdateOptions{ - CostEstimationEnabled: Bool(true), - }, - ) - require.NoError(t, err) - - wTest, _ := createWorkspace(t, client, orgTest) - rTest, _ := createPlannedRun(t, client, wTest) - - t.Run("when the log exists", func(t *testing.T) { - ce, err := client.CostEstimations.Read(ctx, rTest.CostEstimation.ID) - require.NoError(t, err) - - logReader, err := client.CostEstimations.Logs(ctx, ce.ID) - require.NotNil(t, logReader) - require.NoError(t, err) - - logs, err := ioutil.ReadAll(logReader) - require.NoError(t, err) - - t.Skip("log output is likely to change") - assert.Contains(t, string(logs), "SKU") - }) - - t.Run("when the log does not exist", func(t *testing.T) { - logs, err := client.CostEstimations.Logs(ctx, "nonexisting") - assert.Nil(t, logs) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go b/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go deleted file mode 100644 index 96fc95b31..000000000 --- a/vendor/github.com/hashicorp/go-tfe/examples/organizations/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "context" - "log" - - tfe "github.com/hashicorp/go-tfe" -) - -func main() { - config := &tfe.Config{ - Token: "insert-your-token-here", - } - - client, err := tfe.NewClient(config) - if err != nil { - log.Fatal(err) - } - - // Create a context - ctx := context.Background() - - // Create a new organization - options := tfe.OrganizationCreateOptions{ - Name: tfe.String("example"), - Email: tfe.String("info@example.com"), - } - - org, err := client.Organizations.Create(ctx, options) - if err != nil { - log.Fatal(err) - } - - // Delete an organization - err = client.Organizations.Delete(ctx, org.Name) - if err != nil { - log.Fatal(err) - } -} diff --git a/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go b/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go deleted file mode 100644 index a809c0bc0..000000000 --- a/vendor/github.com/hashicorp/go-tfe/examples/workspaces/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "context" - "log" - - tfe "github.com/hashicorp/go-tfe" -) - -func main() { - config := &tfe.Config{ - Token: "insert-your-token-here", - } - - client, err := tfe.NewClient(config) - if err != nil { - log.Fatal(err) - } - - // Create a context - ctx := context.Background() - - // Create a new workspace - w, err := client.Workspaces.Create(ctx, "org-name", tfe.WorkspaceCreateOptions{ - Name: tfe.String("my-app-tst"), - }) - if err != nil { - log.Fatal(err) - } - - // Update the workspace - w, err = client.Workspaces.Update(ctx, "org-name", w.Name, tfe.WorkspaceUpdateOptions{ - AutoApply: tfe.Bool(false), - TerraformVersion: tfe.String("0.11.1"), - WorkingDirectory: tfe.String("my-app/infra"), - }) - if err != nil { - log.Fatal(err) - } -} diff --git a/vendor/github.com/hashicorp/go-tfe/helper_test.go b/vendor/github.com/hashicorp/go-tfe/helper_test.go deleted file mode 100644 index 83a8a2483..000000000 --- a/vendor/github.com/hashicorp/go-tfe/helper_test.go +++ /dev/null @@ -1,666 +0,0 @@ -package tfe - -import ( - "context" - "crypto/md5" - "encoding/base64" - "fmt" - "io/ioutil" - "os" - "testing" - "time" - - "github.com/hashicorp/go-uuid" -) - -const badIdentifier = "! / nope" - -func testClient(t *testing.T) *Client { - client, err := NewClient(nil) - if err != nil { - t.Fatal(err) - } - - return client -} - -func createConfigurationVersion(t *testing.T, client *Client, w *Workspace) (*ConfigurationVersion, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - ctx := context.Background() - cv, err := client.ConfigurationVersions.Create( - ctx, - w.ID, - ConfigurationVersionCreateOptions{AutoQueueRuns: Bool(false)}, - ) - if err != nil { - t.Fatal(err) - } - - return cv, func() { - if wCleanup != nil { - wCleanup() - } - } -} - -func createUploadedConfigurationVersion(t *testing.T, client *Client, w *Workspace) (*ConfigurationVersion, func()) { - cv, cvCleanup := createConfigurationVersion(t, client, w) - - ctx := context.Background() - err := client.ConfigurationVersions.Upload(ctx, cv.UploadURL, "test-fixtures/config-version") - if err != nil { - cvCleanup() - t.Fatal(err) - } - - for i := 0; ; i++ { - cv, err = client.ConfigurationVersions.Read(ctx, cv.ID) - if err != nil { - cvCleanup() - t.Fatal(err) - } - - if cv.Status == ConfigurationUploaded { - break - } - - if i > 10 { - cvCleanup() - t.Fatal("Timeout waiting for the configuration version to be uploaded") - } - - time.Sleep(1 * time.Second) - } - - return cv, cvCleanup -} - -func createNotificationConfiguration(t *testing.T, client *Client, w *Workspace) (*NotificationConfiguration, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - ctx := context.Background() - nc, err := client.NotificationConfigurations.Create( - ctx, - w.ID, - NotificationConfigurationCreateOptions{ - DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), - Enabled: Bool(false), - Name: String(randomString(t)), - Token: String(randomString(t)), - URL: String("http://example.com"), - Triggers: []string{NotificationTriggerCreated}, - }, - ) - if err != nil { - t.Fatal(err) - } - - return nc, func() { - if err := client.NotificationConfigurations.Delete(ctx, nc.ID); err != nil { - t.Errorf("Error destroying notification configuration! WARNING: Dangling\n"+ - "resources may exist! The full error is shown below.\n\n"+ - "NotificationConfiguration: %s\nError: %s", nc.ID, err) - } - - if wCleanup != nil { - wCleanup() - } - } -} - -func createPolicySet(t *testing.T, client *Client, org *Organization, policies []*Policy, workspaces []*Workspace) (*PolicySet, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - ps, err := client.PolicySets.Create(ctx, org.Name, PolicySetCreateOptions{ - Name: String(randomString(t)), - Policies: policies, - Workspaces: workspaces, - }) - if err != nil { - t.Fatal(err) - } - - return ps, func() { - if err := client.PolicySets.Delete(ctx, ps.ID); err != nil { - t.Errorf("Error destroying policy set! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "PolicySet: %s\nError: %s", ps.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createPolicy(t *testing.T, client *Client, org *Organization) (*Policy, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - name := randomString(t) - options := PolicyCreateOptions{ - Name: String(name), - Enforce: []*EnforcementOptions{ - { - Path: String(name + ".sentinel"), - Mode: EnforcementMode(EnforcementSoft), - }, - }, - } - - ctx := context.Background() - p, err := client.Policies.Create(ctx, org.Name, options) - if err != nil { - t.Fatal(err) - } - - return p, func() { - if err := client.Policies.Delete(ctx, p.ID); err != nil { - t.Errorf("Error destroying policy! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Policy: %s\nError: %s", p.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createUploadedPolicy(t *testing.T, client *Client, pass bool, org *Organization) (*Policy, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - p, pCleanup := createPolicy(t, client, org) - - ctx := context.Background() - err := client.Policies.Upload(ctx, p.ID, []byte(fmt.Sprintf("main = rule { %t }", pass))) - if err != nil { - t.Fatal(err) - } - - p, err = client.Policies.Read(ctx, p.ID) - if err != nil { - t.Fatal(err) - } - - return p, func() { - pCleanup() - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createOAuthClient(t *testing.T, client *Client, org *Organization) (*OAuthClient, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - githubToken := os.Getenv("GITHUB_TOKEN") - if githubToken == "" { - t.Fatal("Export a valid GITHUB_TOKEN before running this test!") - } - - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - HTTPURL: String("https://github.com"), - OAuthToken: String(githubToken), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - ctx := context.Background() - oc, err := client.OAuthClients.Create(ctx, org.Name, options) - if err != nil { - t.Fatal(err) - } - - // This currently panics as the token will not be there when the client is - // created. To get a token, the client needs to be connected through the UI - // first. So the test using this (TestOAuthTokensList) is currently disabled. - return oc, func() { - if err := client.OAuthClients.Delete(ctx, oc.ID); err != nil { - t.Errorf("Error destroying OAuth client! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "OAuthClient: %s\nError: %s", oc.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createOAuthToken(t *testing.T, client *Client, org *Organization) (*OAuthToken, func()) { - ocTest, ocTestCleanup := createOAuthClient(t, client, org) - return ocTest.OAuthTokens[0], ocTestCleanup -} - -func createOrganization(t *testing.T, client *Client) (*Organization, func()) { - ctx := context.Background() - org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ - Name: String("tst-" + randomString(t)), - Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), - }) - if err != nil { - t.Fatal(err) - } - - return org, func() { - if err := client.Organizations.Delete(ctx, org.Name); err != nil { - t.Errorf("Error destroying organization! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Organization: %s\nError: %s", org.Name, err) - } - } -} - -func createOrganizationToken(t *testing.T, client *Client, org *Organization) (*OrganizationToken, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - tk, err := client.OrganizationTokens.Generate(ctx, org.Name) - if err != nil { - t.Fatal(err) - } - - return tk, func() { - if err := client.OrganizationTokens.Delete(ctx, org.Name); err != nil { - t.Errorf("Error destroying organization token! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "OrganizationToken: %s\nError: %s", tk.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - cv, cvCleanup := createUploadedConfigurationVersion(t, client, w) - - ctx := context.Background() - r, err := client.Runs.Create(ctx, RunCreateOptions{ - ConfigurationVersion: cv, - Workspace: w, - }) - if err != nil { - t.Fatal(err) - } - - return r, func() { - if wCleanup != nil { - wCleanup() - } else { - cvCleanup() - } - } -} - -func createPlannedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - r, rCleanup := createRun(t, client, w) - - var err error - ctx := context.Background() - for i := 0; ; i++ { - r, err = client.Runs.Read(ctx, r.ID) - if err != nil { - t.Fatal(err) - } - - switch r.Status { - case RunPlanned, RunCostEstimated, RunPolicyChecked, RunPolicyOverride: - return r, rCleanup - } - - if i > 45 { - rCleanup() - t.Fatal("Timeout waiting for run to be applied") - } - - time.Sleep(1 * time.Second) - } -} - -func createAppliedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - r, rCleanup := createPlannedRun(t, client, w) - ctx := context.Background() - - err := client.Runs.Apply(ctx, r.ID, RunApplyOptions{}) - if err != nil { - t.Fatal(err) - } - - for i := 0; ; i++ { - r, err = client.Runs.Read(ctx, r.ID) - if err != nil { - t.Fatal(err) - } - - if r.Status == RunApplied { - return r, rCleanup - } - - if i > 45 { - rCleanup() - t.Fatal("Timeout waiting for run to be applied") - } - - time.Sleep(1 * time.Second) - } -} - -func createSSHKey(t *testing.T, client *Client, org *Organization) (*SSHKey, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - key, err := client.SSHKeys.Create(ctx, org.Name, SSHKeyCreateOptions{ - Name: String(randomString(t)), - Value: String(randomString(t)), - }) - if err != nil { - t.Fatal(err) - } - - return key, func() { - if err := client.SSHKeys.Delete(ctx, key.ID); err != nil { - t.Errorf("Error destroying SSH key! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "SSHKey: %s\nError: %s", key.Name, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createStateVersion(t *testing.T, client *Client, serial int64, w *Workspace) (*StateVersion, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - state, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") - if err != nil { - t.Fatal(err) - } - - ctx := context.Background() - - _, err = client.Workspaces.Lock(ctx, w.ID, WorkspaceLockOptions{}) - if err != nil { - t.Fatal(err) - } - defer func() { - _, err := client.Workspaces.Unlock(ctx, w.ID) - if err != nil { - t.Fatal(err) - } - }() - - sv, err := client.StateVersions.Create(ctx, w.ID, StateVersionCreateOptions{ - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(serial), - State: String(base64.StdEncoding.EncodeToString(state)), - }) - if err != nil { - t.Fatal(err) - } - - return sv, func() { - // There currently isn't a way to delete a state, so we - // can only cleanup by deleting the workspace. - if wCleanup != nil { - wCleanup() - } - } -} - -func createTeam(t *testing.T, client *Client, org *Organization) (*Team, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - tm, err := client.Teams.Create(ctx, org.Name, TeamCreateOptions{ - Name: String(randomString(t)), - OrganizationAccess: &OrganizationAccessOptions{ManagePolicies: Bool(true)}, - }) - if err != nil { - t.Fatal(err) - } - - return tm, func() { - if err := client.Teams.Delete(ctx, tm.ID); err != nil { - t.Errorf("Error destroying team! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Team: %s\nError: %s", tm.Name, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createTeamAccess(t *testing.T, client *Client, tm *Team, w *Workspace, org *Organization) (*TeamAccess, func()) { - var orgCleanup, tmCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - if tm == nil { - tm, tmCleanup = createTeam(t, client, org) - } - - if w == nil { - w, _ = createWorkspace(t, client, org) - } - - ctx := context.Background() - ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ - Access: Access(AccessAdmin), - Team: tm, - Workspace: w, - }) - if err != nil { - t.Fatal(err) - } - - return ta, func() { - if err := client.TeamAccess.Remove(ctx, ta.ID); err != nil { - t.Errorf("Error removing team access! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "TeamAccess: %s\nError: %s", ta.ID, err) - } - - if tmCleanup != nil { - tmCleanup() - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createTeamToken(t *testing.T, client *Client, tm *Team) (*TeamToken, func()) { - var tmCleanup func() - - if tm == nil { - tm, tmCleanup = createTeam(t, client, nil) - } - - ctx := context.Background() - tt, err := client.TeamTokens.Generate(ctx, tm.ID) - if err != nil { - t.Fatal(err) - } - - return tt, func() { - if err := client.TeamTokens.Delete(ctx, tm.ID); err != nil { - t.Errorf("Error destroying team token! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "TeamToken: %s\nError: %s", tm.ID, err) - } - - if tmCleanup != nil { - tmCleanup() - } - } -} - -func createVariable(t *testing.T, client *Client, w *Workspace) (*Variable, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - ctx := context.Background() - v, err := client.Variables.Create(ctx, VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - Workspace: w, - }) - if err != nil { - t.Fatal(err) - } - - return v, func() { - if err := client.Variables.Delete(ctx, v.ID); err != nil { - t.Errorf("Error destroying variable! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Variable: %s\nError: %s", v.Key, err) - } - - if wCleanup != nil { - wCleanup() - } - } -} - -func createWorkspace(t *testing.T, client *Client, org *Organization) (*Workspace, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - w, err := client.Workspaces.Create(ctx, org.Name, WorkspaceCreateOptions{ - Name: String(randomString(t)), - }) - if err != nil { - t.Fatal(err) - } - - return w, func() { - if err := client.Workspaces.Delete(ctx, org.Name, w.Name); err != nil { - t.Errorf("Error destroying workspace! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Workspace: %s\nError: %s", w.Name, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization) (*Workspace, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - oc, ocCleanup := createOAuthToken(t, client, org) - - githubIdentifier := os.Getenv("GITHUB_IDENTIFIER") - if githubIdentifier == "" { - t.Fatal("Export a valid GITHUB_IDENTIFIER before running this test!") - } - - options := WorkspaceCreateOptions{ - Name: String(randomString(t)), - VCSRepo: &VCSRepoOptions{ - Identifier: String(githubIdentifier), - OAuthTokenID: String(oc.ID), - }, - } - - ctx := context.Background() - w, err := client.Workspaces.Create(ctx, org.Name, options) - if err != nil { - t.Fatal(err) - } - - return w, func() { - if err := client.Workspaces.Delete(ctx, org.Name, w.Name); err != nil { - t.Errorf("Error destroying workspace! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Workspace: %s\nError: %s", w.Name, err) - } - - if ocCleanup != nil { - ocCleanup() - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func randomString(t *testing.T) string { - v, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - return v -} diff --git a/vendor/github.com/hashicorp/go-tfe/logreader_test.go b/vendor/github.com/hashicorp/go-tfe/logreader_test.go deleted file mode 100644 index e240937b6..000000000 --- a/vendor/github.com/hashicorp/go-tfe/logreader_test.go +++ /dev/null @@ -1,253 +0,0 @@ -package tfe - -import ( - "context" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "testing" -) - -func testLogReader(t *testing.T, h http.HandlerFunc) (*httptest.Server, *LogReader) { - ts := httptest.NewServer(h) - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - HTTPClient: ts.Client(), - } - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - logURL, err := url.Parse(ts.URL) - if err != nil { - t.Fatal(err) - } - - lr := &LogReader{ - client: client, - ctx: context.Background(), - logURL: logURL, - } - - return ts, lr -} - -func TestLogReader_withMarkersSingle(t *testing.T) { - t.Parallel() - - logReads := 0 - ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logReads++ - switch { - case logReads == 2: - w.Write([]byte("\x02Terraform run started - logs - Terraform run finished\x03")) - } - })) - defer ts.Close() - - doneReads := 0 - lr.done = func() (bool, error) { - doneReads++ - if logReads >= 2 { - return true, nil - } - return false, nil - } - - logs, err := ioutil.ReadAll(lr) - if err != nil { - t.Fatal(err) - } - - expected := "Terraform run started - logs - Terraform run finished" - if string(logs) != expected { - t.Fatalf("expected %s, got: %s", expected, string(logs)) - } - if doneReads != 1 { - t.Fatalf("expected 1 done reads, got %d reads", doneReads) - } - if logReads != 3 { - t.Fatalf("expected 3 log reads, got %d reads", logReads) - } -} - -func TestLogReader_withMarkersDouble(t *testing.T) { - t.Parallel() - - logReads := 0 - ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logReads++ - switch { - case logReads == 2: - w.Write([]byte("\x02Terraform run started")) - case logReads == 3: - w.Write([]byte(" - logs - Terraform run finished\x03")) - } - })) - defer ts.Close() - - doneReads := 0 - lr.done = func() (bool, error) { - doneReads++ - if logReads >= 3 { - return true, nil - } - return false, nil - } - - logs, err := ioutil.ReadAll(lr) - if err != nil { - t.Fatal(err) - } - - expected := "Terraform run started - logs - Terraform run finished" - if string(logs) != expected { - t.Fatalf("expected %s, got: %s", expected, string(logs)) - } - if doneReads != 1 { - t.Fatalf("expected 1 done reads, got %d reads", doneReads) - } - if logReads != 4 { - t.Fatalf("expected 4 log reads, got %d reads", logReads) - } -} - -func TestLogReader_withMarkersMulti(t *testing.T) { - t.Parallel() - - logReads := 0 - ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logReads++ - switch { - case logReads == 2: - w.Write([]byte("\x02")) - case logReads == 3: - w.Write([]byte("Terraform run started")) - case logReads == 16: - w.Write([]byte(" - logs - ")) - case logReads == 30: - w.Write([]byte("Terraform run finished")) - case logReads == 31: - w.Write([]byte("\x03")) - } - })) - defer ts.Close() - - doneReads := 0 - lr.done = func() (bool, error) { - doneReads++ - if logReads >= 31 { - return true, nil - } - return false, nil - } - - logs, err := ioutil.ReadAll(lr) - if err != nil { - t.Fatal(err) - } - - expected := "Terraform run started - logs - Terraform run finished" - if string(logs) != expected { - t.Fatalf("expected %s, got: %s", expected, string(logs)) - } - if doneReads != 3 { - t.Fatalf("expected 3 done reads, got %d reads", doneReads) - } - if logReads != 31 { - t.Fatalf("expected 31 log reads, got %d reads", logReads) - } -} - -func TestLogReader_withoutMarkers(t *testing.T) { - t.Parallel() - - logReads := 0 - ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logReads++ - switch { - case logReads == 2: - w.Write([]byte("Terraform run started")) - case logReads == 16: - w.Write([]byte(" - logs - ")) - case logReads == 31: - w.Write([]byte("Terraform run finished")) - } - })) - defer ts.Close() - - doneReads := 0 - lr.done = func() (bool, error) { - doneReads++ - if logReads >= 31 { - return true, nil - } - return false, nil - } - - logs, err := ioutil.ReadAll(lr) - if err != nil { - t.Fatal(err) - } - - expected := "Terraform run started - logs - Terraform run finished" - if string(logs) != expected { - t.Fatalf("expected %s, got: %s", expected, string(logs)) - } - if doneReads != 25 { - t.Fatalf("expected 14 done reads, got %d reads", doneReads) - } - if logReads != 32 { - t.Fatalf("expected 32 log reads, got %d reads", logReads) - } -} - -func TestLogReader_withoutEndOfTextMarker(t *testing.T) { - t.Parallel() - - logReads := 0 - ts, lr := testLogReader(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logReads++ - switch { - case logReads == 2: - w.Write([]byte("\x02")) - case logReads == 3: - w.Write([]byte("Terraform run started")) - case logReads == 16: - w.Write([]byte(" - logs - ")) - case logReads == 31: - w.Write([]byte("Terraform run finished")) - } - })) - defer ts.Close() - - doneReads := 0 - lr.done = func() (bool, error) { - doneReads++ - if logReads >= 31 { - return true, nil - } - return false, nil - } - - logs, err := ioutil.ReadAll(lr) - if err != nil { - t.Fatal(err) - } - - expected := "Terraform run started - logs - Terraform run finished" - if string(logs) != expected { - t.Fatalf("expected %s, got: %s", expected, string(logs)) - } - if doneReads != 3 { - t.Fatalf("expected 3 done reads, got %d reads", doneReads) - } - if logReads != 42 { - t.Fatalf("expected 42 log reads, got %d reads", logReads) - } -} diff --git a/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go b/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go deleted file mode 100644 index b0ecf51e8..000000000 --- a/vendor/github.com/hashicorp/go-tfe/notification_configuration_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNotificationConfigurationList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - ncTest1, _ := createNotificationConfiguration(t, client, wTest) - ncTest2, _ := createNotificationConfiguration(t, client, wTest) - - t.Run("with a valid workspace", func(t *testing.T) { - ncl, err := client.NotificationConfigurations.List( - ctx, - wTest.ID, - NotificationConfigurationListOptions{}, - ) - require.NoError(t, err) - assert.Contains(t, ncl.Items, ncTest1) - assert.Contains(t, ncl.Items, ncTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, ncl.CurrentPage) - assert.Equal(t, 2, ncl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - ncl, err := client.NotificationConfigurations.List( - ctx, - wTest.ID, - NotificationConfigurationListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }, - ) - require.NoError(t, err) - assert.Empty(t, ncl.Items) - assert.Equal(t, 999, ncl.CurrentPage) - assert.Equal(t, 2, ncl.TotalCount) - }) - - t.Run("without a valid workspace", func(t *testing.T) { - ncl, err := client.NotificationConfigurations.List( - ctx, - badIdentifier, - NotificationConfigurationListOptions{}, - ) - assert.Nil(t, ncl) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestNotificationConfigurationCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - t.Run("with all required values", func(t *testing.T) { - options := NotificationConfigurationCreateOptions{ - DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), - Enabled: Bool(false), - Name: String(randomString(t)), - Token: String(randomString(t)), - URL: String("http://example.com"), - Triggers: []string{NotificationTriggerCreated}, - } - - _, err := client.NotificationConfigurations.Create(ctx, wTest.ID, options) - require.NoError(t, err) - }) - - t.Run("without a required value", func(t *testing.T) { - options := NotificationConfigurationCreateOptions{ - DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), - Enabled: Bool(false), - Token: String(randomString(t)), - URL: String("http://example.com"), - Triggers: []string{NotificationTriggerCreated}, - } - - nc, err := client.NotificationConfigurations.Create(ctx, wTest.ID, options) - assert.Nil(t, nc) - assert.EqualError(t, err, "name is required") - }) - - t.Run("without a valid workspace", func(t *testing.T) { - nc, err := client.NotificationConfigurations.Create(ctx, badIdentifier, NotificationConfigurationCreateOptions{}) - assert.Nil(t, nc) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestNotificationConfigurationRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) - defer ncTestCleanup() - - t.Run("with a valid ID", func(t *testing.T) { - nc, err := client.NotificationConfigurations.Read(ctx, ncTest.ID) - require.NoError(t, err) - assert.Equal(t, ncTest.ID, nc.ID) - }) - - t.Run("when the notification configuration does not exist", func(t *testing.T) { - _, err := client.NotificationConfigurations.Read(ctx, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the notification configuration ID is invalid", func(t *testing.T) { - _, err := client.NotificationConfigurations.Read(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for notification configuration ID") - }) -} - -func TestNotificationConfigurationUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) - defer ncTestCleanup() - - t.Run("with options", func(t *testing.T) { - options := NotificationConfigurationUpdateOptions{ - Enabled: Bool(true), - Name: String("newName"), - } - - nc, err := client.NotificationConfigurations.Update(ctx, ncTest.ID, options) - require.NoError(t, err) - assert.Equal(t, nc.Enabled, true) - assert.Equal(t, nc.Name, "newName") - }) - - t.Run("without options", func(t *testing.T) { - _, err := client.NotificationConfigurations.Update(ctx, ncTest.ID, NotificationConfigurationUpdateOptions{}) - require.NoError(t, err) - }) - - t.Run("when the notification configuration does not exist", func(t *testing.T) { - _, err := client.NotificationConfigurations.Update(ctx, "nonexisting", NotificationConfigurationUpdateOptions{}) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the notification configuration ID is invalid", func(t *testing.T) { - _, err := client.NotificationConfigurations.Update(ctx, badIdentifier, NotificationConfigurationUpdateOptions{}) - assert.EqualError(t, err, "invalid value for notification configuration ID") - }) -} - -func TestNotificationConfigurationDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - ncTest, _ := createNotificationConfiguration(t, client, wTest) - - t.Run("with a valid ID", func(t *testing.T) { - err := client.NotificationConfigurations.Delete(ctx, ncTest.ID) - require.NoError(t, err) - - _, err = client.NotificationConfigurations.Read(ctx, ncTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the notification configuration does not exist", func(t *testing.T) { - err := client.NotificationConfigurations.Delete(ctx, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the notification configuration ID is invalid", func(t *testing.T) { - err := client.NotificationConfigurations.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for notification configuration ID") - }) -} - -func TestNotificationConfigurationVerify(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - ncTest, ncTestCleanup := createNotificationConfiguration(t, client, nil) - defer ncTestCleanup() - - t.Run("with a valid ID", func(t *testing.T) { - _, err := client.NotificationConfigurations.Verify(ctx, ncTest.ID) - require.NoError(t, err) - }) - - t.Run("when the notification configuration does not exists", func(t *testing.T) { - _, err := client.NotificationConfigurations.Verify(ctx, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the notification configuration ID is invalid", func(t *testing.T) { - _, err := client.NotificationConfigurations.Verify(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for notification configuration ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go b/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go deleted file mode 100644 index 9478194b0..000000000 --- a/vendor/github.com/hashicorp/go-tfe/oauth_client_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package tfe - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestOAuthClientsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - ocTest1, _ := createOAuthClient(t, client, orgTest) - ocTest2, _ := createOAuthClient(t, client, orgTest) - - t.Run("without list options", func(t *testing.T) { - options := OAuthClientListOptions{} - - ocl, err := client.OAuthClients.List(ctx, orgTest.Name, options) - require.NoError(t, err) - - t.Run("the OAuth tokens relationship is decoded correcly", func(t *testing.T) { - for _, oc := range ocl.Items { - assert.Equal(t, 1, len(oc.OAuthTokens)) - } - }) - - // We need to strip some fields before the next test. - for _, oc := range append(ocl.Items, ocTest1, ocTest2) { - oc.OAuthTokens = nil - oc.Organization = nil - } - - assert.Contains(t, ocl.Items, ocTest1) - assert.Contains(t, ocl.Items, ocTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, ocl.CurrentPage) - assert.Equal(t, 2, ocl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - options := OAuthClientListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - } - - ocl, err := client.OAuthClients.List(ctx, orgTest.Name, options) - require.NoError(t, err) - assert.Empty(t, ocl.Items) - assert.Equal(t, 999, ocl.CurrentPage) - assert.Equal(t, 2, ocl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - options := OAuthClientListOptions{} - - ocl, err := client.OAuthClients.List(ctx, badIdentifier, options) - assert.Nil(t, ocl) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestOAuthClientsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - githubToken := os.Getenv("GITHUB_TOKEN") - if githubToken == "" { - t.Fatal("Export a valid GITHUB_TOKEN before running this test!") - } - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - HTTPURL: String("https://github.com"), - OAuthToken: String(githubToken), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - oc, err := client.OAuthClients.Create(ctx, orgTest.Name, options) - assert.NoError(t, err) - assert.NotEmpty(t, oc.ID) - assert.Equal(t, "https://api.github.com", oc.APIURL) - assert.Equal(t, "https://github.com", oc.HTTPURL) - assert.Equal(t, 1, len(oc.OAuthTokens)) - assert.Equal(t, ServiceProviderGithub, oc.ServiceProvider) - - t.Run("the organization relationship is decoded correcly", func(t *testing.T) { - assert.NotEmpty(t, oc.Organization) - }) - }) - - t.Run("without an valid organization", func(t *testing.T) { - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - HTTPURL: String("https://github.com"), - OAuthToken: String(githubToken), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - _, err := client.OAuthClients.Create(ctx, badIdentifier, options) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("without an API URL", func(t *testing.T) { - options := OAuthClientCreateOptions{ - HTTPURL: String("https://github.com"), - OAuthToken: String(githubToken), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) - assert.EqualError(t, err, "API URL is required") - }) - - t.Run("without a HTTP URL", func(t *testing.T) { - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - OAuthToken: String(githubToken), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) - assert.EqualError(t, err, "HTTP URL is required") - }) - - t.Run("without an OAuth token", func(t *testing.T) { - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - HTTPURL: String("https://github.com"), - ServiceProvider: ServiceProvider(ServiceProviderGithub), - } - - _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) - assert.EqualError(t, err, "OAuth token is required") - }) - - t.Run("without a service provider", func(t *testing.T) { - options := OAuthClientCreateOptions{ - APIURL: String("https://api.github.com"), - HTTPURL: String("https://github.com"), - OAuthToken: String(githubToken), - } - - _, err := client.OAuthClients.Create(ctx, orgTest.Name, options) - assert.EqualError(t, err, "service provider is required") - }) -} - -func TestOAuthClientsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - ocTest, ocTestCleanup := createOAuthClient(t, client, nil) - defer ocTestCleanup() - - t.Run("when the OAuth client exists", func(t *testing.T) { - oc, err := client.OAuthClients.Read(ctx, ocTest.ID) - require.NoError(t, err) - assert.Equal(t, ocTest.ID, oc.ID) - assert.Equal(t, ocTest.APIURL, oc.APIURL) - assert.Equal(t, ocTest.CallbackURL, oc.CallbackURL) - assert.Equal(t, ocTest.ConnectPath, oc.ConnectPath) - assert.Equal(t, ocTest.HTTPURL, oc.HTTPURL) - assert.Equal(t, ocTest.ServiceProvider, oc.ServiceProvider) - assert.Equal(t, ocTest.ServiceProviderName, oc.ServiceProviderName) - assert.Equal(t, ocTest.OAuthTokens, oc.OAuthTokens) - }) - - t.Run("when the OAuth client does not exist", func(t *testing.T) { - oc, err := client.OAuthClients.Read(ctx, "nonexisting") - assert.Nil(t, oc) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("without a valid OAuth client ID", func(t *testing.T) { - oc, err := client.OAuthClients.Read(ctx, badIdentifier) - assert.Nil(t, oc) - assert.EqualError(t, err, "invalid value for OAuth client ID") - }) -} - -func TestOAuthClientsDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - ocTest, _ := createOAuthClient(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.OAuthClients.Delete(ctx, ocTest.ID) - require.NoError(t, err) - - // Try loading the OAuth client - it should fail. - _, err = client.OAuthClients.Read(ctx, ocTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the OAuth client does not exist", func(t *testing.T) { - err := client.OAuthClients.Delete(ctx, ocTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the OAuth client ID is invalid", func(t *testing.T) { - err := client.OAuthClients.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for OAuth client ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go b/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go deleted file mode 100644 index 1ba20e44b..000000000 --- a/vendor/github.com/hashicorp/go-tfe/oauth_token_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package tfe - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestOAuthTokensList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - otTest1, _ := createOAuthToken(t, client, orgTest) - otTest2, _ := createOAuthToken(t, client, orgTest) - - t.Run("without list options", func(t *testing.T) { - options := OAuthTokenListOptions{} - - otl, err := client.OAuthTokens.List(ctx, orgTest.Name, options) - require.NoError(t, err) - - t.Run("the OAuth client relationship is decoded correcly", func(t *testing.T) { - for _, ot := range otl.Items { - assert.NotEmpty(t, ot.OAuthClient) - } - }) - - // We need to strip some fields before the next test. - for _, ot := range otl.Items { - ot.CreatedAt = time.Time{} - ot.ServiceProviderUser = "" - ot.OAuthClient = nil - } - - assert.Contains(t, otl.Items, otTest1) - assert.Contains(t, otl.Items, otTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, otl.CurrentPage) - assert.Equal(t, 2, otl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - options := OAuthTokenListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - } - - otl, err := client.OAuthTokens.List(ctx, orgTest.Name, options) - require.NoError(t, err) - assert.Empty(t, otl.Items) - assert.Equal(t, 999, otl.CurrentPage) - assert.Equal(t, 2, otl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - options := OAuthTokenListOptions{} - - otl, err := client.OAuthTokens.List(ctx, badIdentifier, options) - assert.Nil(t, otl) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestOAuthTokensRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - otTest, otTestCleanup := createOAuthToken(t, client, nil) - defer otTestCleanup() - - t.Run("when the OAuth token exists", func(t *testing.T) { - ot, err := client.OAuthTokens.Read(ctx, otTest.ID) - require.NoError(t, err) - assert.Equal(t, otTest.ID, ot.ID) - assert.NotEmpty(t, ot.OAuthClient) - }) - - t.Run("when the OAuth token does not exist", func(t *testing.T) { - ot, err := client.OAuthTokens.Read(ctx, "nonexisting") - assert.Nil(t, ot) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("without a valid OAuth token ID", func(t *testing.T) { - ot, err := client.OAuthTokens.Read(ctx, badIdentifier) - assert.Nil(t, ot) - assert.EqualError(t, err, "invalid value for OAuth token ID") - }) -} - -func TestOAuthTokensUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - otTest, otTestCleanup := createOAuthToken(t, client, nil) - defer otTestCleanup() - - t.Run("before updating with an SSH key", func(t *testing.T) { - assert.False(t, otTest.HasSSHKey) - }) - - t.Run("without options", func(t *testing.T) { - ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{}) - require.NoError(t, err) - assert.False(t, ot.HasSSHKey) - }) - - t.Run("when updating with a valid SSH key", func(t *testing.T) { - dummyPrivateSSHKey := `-----BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDIF0s2yX7dSQQL1grdTbai1Mb7sEco6RIOz8iqrHTGqmESpu5n -d8imMkV5KadgVBJ/UvHsWpg446O3DAMYn0Y6f8dDlK7pmCEtiGVKTR1PaVRMpF8R -5Guvrmlru8Kex5ozh0pPMB15aGsIzSezCKgSs74Od9YL4smdgKyYwqsu3wIDAQAB -AoGBAKCs6+4j4icqYgBrMjBCHp4lRWCJTqtQdfrE6jv73o5F9Uu4FwupScwD5HwG -cezNtkjeP3zvxvsv+aCdGcNk60vSz4n9Nt6gEJveWFSpePYXKZ9cz/IjFLI7nSzc -1msLyE3DfUqB91s/A/aT5p0LiVDc8i4mCGDOga2OINIwqDGZAkEA/Vz8dkcqsAVW -CL1F000hWTrM6tu0V+x8Nm8CRx7wM/Gy/19PbV0t26wCVG0GXyLWsV2//huY7w5b -3AcSl5pfJQJBAMosYQXk5L4S+qivz2zmZdtyz+Ik6IZ3PwZoED32PxGSdW5rG8iP -V+iSJek5ESkS1zeXwDMnF4LeoBY9H07DiLMCQQCrHm1o2SIMpl34IxWQ4+wdHuid -yuuf4pn2Db2lGVE0VA8ICXBUtfUuA5vDN6tw/8+vFVmBn1QISVNjZOd6uwl9AkA+ -jIRoAm0SsWSDlAEkvBN/VYIjgS+/il0haki8ItdYZGuYgeLSpiaYeb7o7RL2FjIn -rPd12/5WKvJ0buykvbIpAkEA5Uy3T8xQJkDGbp0+xA0yThoOYiB09lAok8I7Sv/5 -dpIe8YOINN27XaojJvVpT5uBVCcZLF+G7kaMjSwCTlDx3Q== ------END RSA PRIVATE KEY-----` - - ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{ - PrivateSSHKey: String(dummyPrivateSSHKey), - }) - require.NoError(t, err) - - assert.Equal(t, otTest.ID, ot.ID) - assert.True(t, ot.HasSSHKey) - }) - - t.Run("when updating with an invalid SSH key", func(t *testing.T) { - ot, err := client.OAuthTokens.Update(ctx, otTest.ID, OAuthTokenUpdateOptions{ - PrivateSSHKey: String(randomString(t)), - }) - assert.Nil(t, ot) - assert.Contains(t, err.Error(), "Ssh key is invalid") - }) - - t.Run("without a valid policy ID", func(t *testing.T) { - ot, err := client.OAuthTokens.Update(ctx, badIdentifier, OAuthTokenUpdateOptions{}) - assert.Nil(t, ot) - assert.EqualError(t, err, "invalid value for OAuth token ID") - }) -} - -func TestOAuthTokensDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - otTest, _ := createOAuthToken(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.OAuthTokens.Delete(ctx, otTest.ID) - require.NoError(t, err) - - // Try loading the OAuth token - it should fail. - _, err = client.OAuthTokens.Read(ctx, otTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the OAuth token does not exist", func(t *testing.T) { - err := client.OAuthTokens.Delete(ctx, otTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the OAuth token ID is invalid", func(t *testing.T) { - err := client.OAuthTokens.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for OAuth token ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/organization_test.go b/vendor/github.com/hashicorp/go-tfe/organization_test.go deleted file mode 100644 index dda38d3de..000000000 --- a/vendor/github.com/hashicorp/go-tfe/organization_test.go +++ /dev/null @@ -1,367 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestOrganizationsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest1, orgTest1Cleanup := createOrganization(t, client) - defer orgTest1Cleanup() - orgTest2, orgTest2Cleanup := createOrganization(t, client) - defer orgTest2Cleanup() - - t.Run("with no list options", func(t *testing.T) { - orgl, err := client.Organizations.List(ctx, OrganizationListOptions{}) - require.NoError(t, err) - assert.Contains(t, orgl.Items, orgTest1) - assert.Contains(t, orgl.Items, orgTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, orgl.CurrentPage) - assert.Equal(t, 2, orgl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - orgl, err := client.Organizations.List(ctx, OrganizationListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, orgl) - assert.Equal(t, 999, orgl.CurrentPage) - assert.Equal(t, 2, orgl.TotalCount) - }) -} - -func TestOrganizationsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - t.Run("with valid options", func(t *testing.T) { - options := OrganizationCreateOptions{ - Name: String(randomString(t)), - Email: String(randomString(t) + "@tfe.local"), - } - - org, err := client.Organizations.Create(ctx, options) - require.NoError(t, err) - - // Make sure we clean up the created org. - defer client.Organizations.Delete(ctx, org.Name) - - assert.Equal(t, *options.Name, org.Name) - assert.Equal(t, *options.Email, org.Email) - }) - - t.Run("when no email is provided", func(t *testing.T) { - org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ - Name: String("foo"), - }) - assert.Nil(t, org) - assert.EqualError(t, err, "email is required") - }) - - t.Run("when no name is provided", func(t *testing.T) { - _, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ - Email: String("foo@bar.com"), - }) - assert.EqualError(t, err, "name is required") - }) - - t.Run("with invalid name", func(t *testing.T) { - org, err := client.Organizations.Create(ctx, OrganizationCreateOptions{ - Name: String(badIdentifier), - Email: String("foo@bar.com"), - }) - assert.Nil(t, org) - assert.EqualError(t, err, "invalid value for name") - }) -} - -func TestOrganizationsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("when the org exists", func(t *testing.T) { - org, err := client.Organizations.Read(ctx, orgTest.Name) - require.NoError(t, err) - assert.Equal(t, orgTest, org) - - t.Run("permissions are properly decoded", func(t *testing.T) { - assert.True(t, org.Permissions.CanDestroy) - }) - - t.Run("timestamps are populated", func(t *testing.T) { - assert.NotEmpty(t, org.CreatedAt) - assert.NotEmpty(t, org.TrialExpiresAt) - }) - }) - - t.Run("with invalid name", func(t *testing.T) { - org, err := client.Organizations.Read(ctx, badIdentifier) - assert.Nil(t, org) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when the org does not exist", func(t *testing.T) { - _, err := client.Organizations.Read(ctx, randomString(t)) - assert.Error(t, err) - }) -} - -func TestOrganizationsUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - t.Run("with valid options", func(t *testing.T) { - orgTest, orgTestCleanup := createOrganization(t, client) - - options := OrganizationUpdateOptions{ - Name: String(randomString(t)), - Email: String(randomString(t) + "@tfe.local"), - SessionTimeout: Int(3600), - SessionRemember: Int(3600), - } - - org, err := client.Organizations.Update(ctx, orgTest.Name, options) - if err != nil { - orgTestCleanup() - } - require.NoError(t, err) - - // Make sure we clean up the renamed org. - defer client.Organizations.Delete(ctx, org.Name) - - // Also get a fresh result from the API to ensure we get the - // expected values back. - refreshed, err := client.Organizations.Read(ctx, *options.Name) - require.NoError(t, err) - - for _, item := range []*Organization{ - org, - refreshed, - } { - assert.Equal(t, *options.Name, item.Name) - assert.Equal(t, *options.Email, item.Email) - assert.Equal(t, *options.SessionTimeout, item.SessionTimeout) - assert.Equal(t, *options.SessionRemember, item.SessionRemember) - } - }) - - t.Run("with invalid name", func(t *testing.T) { - org, err := client.Organizations.Update(ctx, badIdentifier, OrganizationUpdateOptions{}) - assert.Nil(t, org) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when only updating a subset of fields", func(t *testing.T) { - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - org, err := client.Organizations.Update(ctx, orgTest.Name, OrganizationUpdateOptions{}) - require.NoError(t, err) - assert.Equal(t, orgTest.Name, org.Name) - assert.Equal(t, orgTest.Email, org.Email) - }) -} - -func TestOrganizationsDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - t.Run("with valid options", func(t *testing.T) { - orgTest, _ := createOrganization(t, client) - - err := client.Organizations.Delete(ctx, orgTest.Name) - require.NoError(t, err) - - // Try fetching the org again - it should error. - _, err = client.Organizations.Read(ctx, orgTest.Name) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid name", func(t *testing.T) { - err := client.Organizations.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestOrganizationsCapacity(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest1, _ := createWorkspace(t, client, orgTest) - wTest2, _ := createWorkspace(t, client, orgTest) - wTest3, _ := createWorkspace(t, client, orgTest) - wTest4, _ := createWorkspace(t, client, orgTest) - - t.Run("without queued runs", func(t *testing.T) { - c, err := client.Organizations.Capacity(ctx, orgTest.Name) - require.NoError(t, err) - assert.Equal(t, 0, c.Pending) - assert.Equal(t, 0, c.Running) - }) - - // For this test FRQ should be enabled and have a - // limit of 2 concurrent runs per organization. - t.Run("with queued runs", func(t *testing.T) { - _, _ = createRun(t, client, wTest1) - _, _ = createRun(t, client, wTest2) - _, _ = createRun(t, client, wTest3) - _, _ = createRun(t, client, wTest4) - - c, err := client.Organizations.Capacity(ctx, orgTest.Name) - require.NoError(t, err) - assert.Equal(t, 2, c.Pending) - assert.Equal(t, 2, c.Running) - }) - - t.Run("with invalid name", func(t *testing.T) { - org, err := client.Organizations.Read(ctx, badIdentifier) - assert.Nil(t, org) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when the org does not exist", func(t *testing.T) { - _, err := client.Organizations.Read(ctx, randomString(t)) - assert.Error(t, err) - }) -} - -func TestOrganizationsEntitlements(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("when the org exists", func(t *testing.T) { - entitlements, err := client.Organizations.Entitlements(ctx, orgTest.Name) - require.NoError(t, err) - - assert.NotEmpty(t, entitlements.ID) - assert.True(t, entitlements.Operations) - assert.True(t, entitlements.PrivateModuleRegistry) - assert.True(t, entitlements.Sentinel) - assert.True(t, entitlements.StateStorage) - assert.True(t, entitlements.Teams) - assert.True(t, entitlements.VCSIntegrations) - }) - - t.Run("with invalid name", func(t *testing.T) { - entitlements, err := client.Organizations.Entitlements(ctx, badIdentifier) - assert.Nil(t, entitlements) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when the org does not exist", func(t *testing.T) { - _, err := client.Organizations.Entitlements(ctx, randomString(t)) - assert.Equal(t, ErrResourceNotFound, err) - }) -} - -func TestOrganizationsRunQueue(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest1, _ := createWorkspace(t, client, orgTest) - wTest2, _ := createWorkspace(t, client, orgTest) - wTest3, _ := createWorkspace(t, client, orgTest) - wTest4, _ := createWorkspace(t, client, orgTest) - - t.Run("without queued runs", func(t *testing.T) { - rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, len(rq.Items)) - }) - - // Create a couple or runs to fill the queue. - rTest1, _ := createRun(t, client, wTest1) - rTest2, _ := createRun(t, client, wTest2) - rTest3, _ := createRun(t, client, wTest3) - rTest4, _ := createRun(t, client, wTest4) - - // For this test FRQ should be enabled and have a - // limit of 2 concurrent runs per organization. - t.Run("with queued runs", func(t *testing.T) { - rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) - require.NoError(t, err) - - found := []string{} - for _, r := range rq.Items { - found = append(found, r.ID) - } - - assert.Contains(t, found, rTest1.ID) - assert.Contains(t, found, rTest2.ID) - assert.Contains(t, found, rTest3.ID) - assert.Contains(t, found, rTest4.ID) - }) - - t.Run("without queue options", func(t *testing.T) { - rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{}) - require.NoError(t, err) - - found := []string{} - for _, r := range rq.Items { - found = append(found, r.ID) - } - - assert.Contains(t, found, rTest1.ID) - assert.Contains(t, found, rTest2.ID) - assert.Contains(t, found, rTest3.ID) - assert.Contains(t, found, rTest4.ID) - assert.Equal(t, 1, rq.CurrentPage) - assert.Equal(t, 4, rq.TotalCount) - }) - - t.Run("with queue options", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - rq, err := client.Organizations.RunQueue(ctx, orgTest.Name, RunQueueOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - - assert.Empty(t, rq.Items) - assert.Equal(t, 999, rq.CurrentPage) - assert.Equal(t, 4, rq.TotalCount) - }) - - t.Run("with invalid name", func(t *testing.T) { - org, err := client.Organizations.Read(ctx, badIdentifier) - assert.Nil(t, org) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when the org does not exist", func(t *testing.T) { - _, err := client.Organizations.Read(ctx, randomString(t)) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/organization_token_test.go b/vendor/github.com/hashicorp/go-tfe/organization_token_test.go deleted file mode 100644 index d4b6a7dfe..000000000 --- a/vendor/github.com/hashicorp/go-tfe/organization_token_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestOrganizationTokensGenerate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - var tkToken string - t.Run("with valid options", func(t *testing.T) { - ot, err := client.OrganizationTokens.Generate(ctx, orgTest.Name) - require.NoError(t, err) - require.NotEmpty(t, ot.Token) - tkToken = ot.Token - }) - - t.Run("when a token already exists", func(t *testing.T) { - ot, err := client.OrganizationTokens.Generate(ctx, orgTest.Name) - require.NoError(t, err) - require.NotEmpty(t, ot.Token) - assert.NotEqual(t, tkToken, ot.Token) - }) - - t.Run("without valid organization", func(t *testing.T) { - ot, err := client.OrganizationTokens.Generate(ctx, badIdentifier) - assert.Nil(t, ot) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestOrganizationTokensRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - _, otTestCleanup := createOrganizationToken(t, client, orgTest) - - ot, err := client.OrganizationTokens.Read(ctx, orgTest.Name) - assert.NoError(t, err) - assert.NotEmpty(t, ot) - - otTestCleanup() - }) - - t.Run("when a token doesn't exists", func(t *testing.T) { - ot, err := client.OrganizationTokens.Read(ctx, orgTest.Name) - assert.Equal(t, ErrResourceNotFound, err) - assert.Nil(t, ot) - }) - - t.Run("without valid organization", func(t *testing.T) { - ot, err := client.OrganizationTokens.Read(ctx, badIdentifier) - assert.Nil(t, ot) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestOrganizationTokensDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - createOrganizationToken(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.OrganizationTokens.Delete(ctx, orgTest.Name) - assert.NoError(t, err) - }) - - t.Run("when a token does not exist", func(t *testing.T) { - err := client.OrganizationTokens.Delete(ctx, orgTest.Name) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without valid organization", func(t *testing.T) { - err := client.OrganizationTokens.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for organization") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/plan_test.go b/vendor/github.com/hashicorp/go-tfe/plan_test.go deleted file mode 100644 index 2d92e1a84..000000000 --- a/vendor/github.com/hashicorp/go-tfe/plan_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package tfe - -import ( - "context" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPlansRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createPlannedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the plan exists", func(t *testing.T) { - p, err := client.Plans.Read(ctx, rTest.Plan.ID) - require.NoError(t, err) - assert.True(t, p.HasChanges) - assert.NotEmpty(t, p.LogReadURL) - assert.Equal(t, p.Status, PlanFinished) - assert.NotEmpty(t, p.StatusTimestamps) - }) - - t.Run("when the plan does not exist", func(t *testing.T) { - p, err := client.Plans.Read(ctx, "nonexisting") - assert.Nil(t, p) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid plan ID", func(t *testing.T) { - p, err := client.Plans.Read(ctx, badIdentifier) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for plan ID") - }) -} - -func TestPlansLogs(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createPlannedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the log exists", func(t *testing.T) { - p, err := client.Plans.Read(ctx, rTest.Plan.ID) - require.NoError(t, err) - - logReader, err := client.Plans.Logs(ctx, p.ID) - require.NoError(t, err) - - logs, err := ioutil.ReadAll(logReader) - require.NoError(t, err) - - assert.Contains(t, string(logs), "1 to add, 0 to change, 0 to destroy") - }) - - t.Run("when the log does not exist", func(t *testing.T) { - logs, err := client.Plans.Logs(ctx, "nonexisting") - assert.Nil(t, logs) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_check_test.go b/vendor/github.com/hashicorp/go-tfe/policy_check_test.go deleted file mode 100644 index e9e77c84b..000000000 --- a/vendor/github.com/hashicorp/go-tfe/policy_check_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package tfe - -import ( - "context" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPolicyChecksList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest1, _ := createUploadedPolicy(t, client, true, orgTest) - pTest2, _ := createUploadedPolicy(t, client, true, orgTest) - wTest, _ := createWorkspace(t, client, orgTest) - createPolicySet(t, client, orgTest, []*Policy{pTest1, pTest2}, []*Workspace{wTest}) - - rTest, _ := createPlannedRun(t, client, wTest) - - t.Run("without list options", func(t *testing.T) { - pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) - require.NoError(t, err) - require.Equal(t, 1, len(pcl.Items)) - - t.Run("pagination is properly decoded", func(t *testing.T) { - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, pcl.CurrentPage) - assert.Equal(t, 1, pcl.TotalCount) - }) - - t.Run("permissions are properly decoded", func(t *testing.T) { - assert.NotEmpty(t, pcl.Items[0].Permissions) - }) - - t.Run("result is properly decoded", func(t *testing.T) { - require.NotEmpty(t, pcl.Items[0].Result) - assert.Equal(t, 2, pcl.Items[0].Result.Passed) - }) - - t.Run("timestamps are properly decoded", func(t *testing.T) { - assert.NotEmpty(t, pcl.Items[0].StatusTimestamps) - }) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, pcl.Items) - assert.Equal(t, 999, pcl.CurrentPage) - assert.Equal(t, 1, pcl.TotalCount) - }) - - t.Run("without a valid run ID", func(t *testing.T) { - pcl, err := client.PolicyChecks.List(ctx, badIdentifier, PolicyCheckListOptions{}) - assert.Nil(t, pcl) - assert.EqualError(t, err, "invalid value for run ID") - }) -} - -func TestPolicyChecksRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest, _ := createUploadedPolicy(t, client, true, orgTest) - wTest, _ := createWorkspace(t, client, orgTest) - createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) - - rTest, _ := createPlannedRun(t, client, wTest) - require.Equal(t, 1, len(rTest.PolicyChecks)) - - t.Run("when the policy check exists", func(t *testing.T) { - pc, err := client.PolicyChecks.Read(ctx, rTest.PolicyChecks[0].ID) - require.NoError(t, err) - - assert.NotEmpty(t, pc.Permissions) - assert.Equal(t, PolicyScopeOrganization, pc.Scope) - assert.Equal(t, PolicyPasses, pc.Status) - assert.NotEmpty(t, pc.StatusTimestamps) - - t.Run("result is properly decoded", func(t *testing.T) { - require.NotEmpty(t, pc.Result) - assert.Equal(t, 1, pc.Result.Passed) - }) - }) - - t.Run("when the policy check does not exist", func(t *testing.T) { - pc, err := client.PolicyChecks.Read(ctx, "nonexisting") - assert.Nil(t, pc) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("without a valid policy check ID", func(t *testing.T) { - pc, err := client.PolicyChecks.Read(ctx, badIdentifier) - assert.Nil(t, pc) - assert.EqualError(t, err, "invalid value for policy check ID") - }) -} - -func TestPolicyChecksOverride(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("when the policy failed", func(t *testing.T) { - pTest, pTestCleanup := createUploadedPolicy(t, client, false, orgTest) - defer pTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) - rTest, _ := createPlannedRun(t, client, wTest) - - pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) - require.NoError(t, err) - require.Equal(t, 1, len(pcl.Items)) - require.Equal(t, PolicySoftFailed, pcl.Items[0].Status) - - pc, err := client.PolicyChecks.Override(ctx, pcl.Items[0].ID) - require.NoError(t, err) - - assert.NotEmpty(t, pc.Result) - assert.Equal(t, PolicyOverridden, pc.Status) - }) - - t.Run("when the policy passed", func(t *testing.T) { - pTest, pTestCleanup := createUploadedPolicy(t, client, true, orgTest) - defer pTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) - rTest, _ := createPlannedRun(t, client, wTest) - - pcl, err := client.PolicyChecks.List(ctx, rTest.ID, PolicyCheckListOptions{}) - require.NoError(t, err) - require.Equal(t, 1, len(pcl.Items)) - require.Equal(t, PolicyPasses, pcl.Items[0].Status) - - _, err = client.PolicyChecks.Override(ctx, pcl.Items[0].ID) - assert.Error(t, err) - }) - - t.Run("without a valid policy check ID", func(t *testing.T) { - p, err := client.PolicyChecks.Override(ctx, badIdentifier) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for policy check ID") - }) -} - -func TestPolicyChecksLogs(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest, _ := createUploadedPolicy(t, client, true, orgTest) - wTest, _ := createWorkspace(t, client, orgTest) - createPolicySet(t, client, orgTest, []*Policy{pTest}, []*Workspace{wTest}) - - rTest, _ := createPlannedRun(t, client, wTest) - require.Equal(t, 1, len(rTest.PolicyChecks)) - - t.Run("when the log exists", func(t *testing.T) { - pc, err := client.PolicyChecks.Read(ctx, rTest.PolicyChecks[0].ID) - require.NoError(t, err) - - logReader, err := client.PolicyChecks.Logs(ctx, pc.ID) - require.NoError(t, err) - - logs, err := ioutil.ReadAll(logReader) - require.NoError(t, err) - - assert.Contains(t, string(logs), "1 policies evaluated") - }) - - t.Run("when the log does not exist", func(t *testing.T) { - logs, err := client.PolicyChecks.Logs(ctx, "nonexisting") - assert.Nil(t, logs) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_set_test.go b/vendor/github.com/hashicorp/go-tfe/policy_set_test.go deleted file mode 100644 index edd403893..000000000 --- a/vendor/github.com/hashicorp/go-tfe/policy_set_test.go +++ /dev/null @@ -1,454 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPolicySetsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - psTest1, _ := createPolicySet(t, client, orgTest, nil, nil) - psTest2, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("without list options", func(t *testing.T) { - psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{}) - require.NoError(t, err) - - assert.Contains(t, psl.Items, psTest1) - assert.Contains(t, psl.Items, psTest2) - assert.Equal(t, 1, psl.CurrentPage) - assert.Equal(t, 2, psl.TotalCount) - }) - - t.Run("with pagination", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - - assert.Empty(t, psl.Items) - assert.Equal(t, 999, psl.CurrentPage) - assert.Equal(t, 2, psl.TotalCount) - }) - - t.Run("with search", func(t *testing.T) { - // Search by one of the policy set's names; we should get only that policy - // set and pagination data should reflect the search as well - psl, err := client.PolicySets.List(ctx, orgTest.Name, PolicySetListOptions{ - Search: String(psTest1.Name), - }) - require.NoError(t, err) - - assert.Contains(t, psl.Items, psTest1) - assert.NotContains(t, psl.Items, psTest2) - assert.Equal(t, 1, psl.CurrentPage) - assert.Equal(t, 1, psl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - ps, err := client.PolicySets.List(ctx, badIdentifier, PolicySetListOptions{}) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestPolicySetsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid attributes", func(t *testing.T) { - options := PolicySetCreateOptions{ - Name: String("policy-set"), - } - - ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - assert.Equal(t, ps.Name, *options.Name) - assert.Equal(t, ps.Description, "") - assert.False(t, ps.Global) - }) - - t.Run("with all attributes provided", func(t *testing.T) { - options := PolicySetCreateOptions{ - Name: String("global"), - Description: String("Policies in this set will be checked in ALL workspaces!"), - Global: Bool(true), - } - - ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - assert.Equal(t, ps.Name, *options.Name) - assert.Equal(t, ps.Description, *options.Description) - assert.True(t, ps.Global) - }) - - t.Run("with policies and workspaces provided", func(t *testing.T) { - pTest, _ := createPolicy(t, client, orgTest) - wTest, _ := createWorkspace(t, client, orgTest) - - options := PolicySetCreateOptions{ - Name: String("populated-policy-set"), - Policies: []*Policy{pTest}, - Workspaces: []*Workspace{wTest}, - } - - ps, err := client.PolicySets.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - assert.Equal(t, ps.Name, *options.Name) - assert.Equal(t, ps.PolicyCount, 1) - assert.Equal(t, ps.Policies[0].ID, pTest.ID) - assert.Equal(t, ps.WorkspaceCount, 1) - assert.Equal(t, ps.Workspaces[0].ID, wTest.ID) - }) - - t.Run("without a name provided", func(t *testing.T) { - ps, err := client.PolicySets.Create(ctx, orgTest.Name, PolicySetCreateOptions{}) - assert.Nil(t, ps) - assert.EqualError(t, err, "name is required") - }) - - t.Run("with an invalid name provided", func(t *testing.T) { - ps, err := client.PolicySets.Create(ctx, orgTest.Name, PolicySetCreateOptions{ - Name: String("nope!"), - }) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for name") - }) - - t.Run("without a valid organization", func(t *testing.T) { - ps, err := client.PolicySets.Create(ctx, badIdentifier, PolicySetCreateOptions{ - Name: String("policy-set"), - }) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestPolicySetsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - psTest, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("with a valid ID", func(t *testing.T) { - ps, err := client.PolicySets.Read(ctx, psTest.ID) - require.NoError(t, err) - - assert.Equal(t, ps.ID, psTest.ID) - }) - - t.Run("without a valid ID", func(t *testing.T) { - ps, err := client.PolicySets.Read(ctx, badIdentifier) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - psTest, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("with valid attributes", func(t *testing.T) { - options := PolicySetUpdateOptions{ - Name: String("global"), - Description: String("Policies in this set will be checked in ALL workspaces!"), - Global: Bool(true), - } - - ps, err := client.PolicySets.Update(ctx, psTest.ID, options) - require.NoError(t, err) - - assert.Equal(t, ps.Name, *options.Name) - assert.Equal(t, ps.Description, *options.Description) - assert.True(t, ps.Global) - }) - - t.Run("with invalid attributes", func(t *testing.T) { - ps, err := client.PolicySets.Update(ctx, psTest.ID, PolicySetUpdateOptions{ - Name: String("nope!"), - }) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for name") - }) - - t.Run("without a valid ID", func(t *testing.T) { - ps, err := client.PolicySets.Update(ctx, badIdentifier, PolicySetUpdateOptions{ - Name: String("policy-set"), - }) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsAddPolicies(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest1, _ := createPolicy(t, client, orgTest) - pTest2, _ := createPolicy(t, client, orgTest) - psTest, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("with policies provided", func(t *testing.T) { - err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{ - Policies: []*Policy{pTest1, pTest2}, - }) - require.NoError(t, err) - - ps, err := client.PolicySets.Read(ctx, psTest.ID) - require.NoError(t, err) - assert.Equal(t, ps.PolicyCount, 2) - - ids := []string{} - for _, policy := range ps.Policies { - ids = append(ids, policy.ID) - } - - assert.Contains(t, ids, pTest1.ID) - assert.Contains(t, ids, pTest2.ID) - }) - - t.Run("without policies provided", func(t *testing.T) { - err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{}) - assert.EqualError(t, err, "policies is required") - }) - - t.Run("with empty policies slice", func(t *testing.T) { - err := client.PolicySets.AddPolicies(ctx, psTest.ID, PolicySetAddPoliciesOptions{ - Policies: []*Policy{}, - }) - assert.EqualError(t, err, "must provide at least one policy") - }) - - t.Run("without a valid ID", func(t *testing.T) { - err := client.PolicySets.AddPolicies(ctx, badIdentifier, PolicySetAddPoliciesOptions{ - Policies: []*Policy{pTest1, pTest2}, - }) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsRemovePolicies(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest1, _ := createPolicy(t, client, orgTest) - pTest2, _ := createPolicy(t, client, orgTest) - psTest, _ := createPolicySet(t, client, orgTest, []*Policy{pTest1, pTest2}, nil) - - t.Run("with policies provided", func(t *testing.T) { - err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{ - Policies: []*Policy{pTest1, pTest2}, - }) - require.NoError(t, err) - - ps, err := client.PolicySets.Read(ctx, psTest.ID) - require.NoError(t, err) - - assert.Equal(t, 0, ps.PolicyCount) - assert.Empty(t, ps.Policies) - }) - - t.Run("without policies provided", func(t *testing.T) { - err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{}) - assert.EqualError(t, err, "policies is required") - }) - - t.Run("with empty policies slice", func(t *testing.T) { - err := client.PolicySets.RemovePolicies(ctx, psTest.ID, PolicySetRemovePoliciesOptions{ - Policies: []*Policy{}, - }) - assert.EqualError(t, err, "must provide at least one policy") - }) - - t.Run("without a valid ID", func(t *testing.T) { - err := client.PolicySets.RemovePolicies(ctx, badIdentifier, PolicySetRemovePoliciesOptions{ - Policies: []*Policy{pTest1, pTest2}, - }) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsAddWorkspaces(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest1, _ := createWorkspace(t, client, orgTest) - wTest2, _ := createWorkspace(t, client, orgTest) - psTest, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("with workspaces provided", func(t *testing.T) { - err := client.PolicySets.AddWorkspaces( - ctx, - psTest.ID, - PolicySetAddWorkspacesOptions{ - Workspaces: []*Workspace{wTest1, wTest2}, - }, - ) - require.NoError(t, err) - - ps, err := client.PolicySets.Read(ctx, psTest.ID) - require.NoError(t, err) - assert.Equal(t, 2, ps.WorkspaceCount) - - ids := []string{} - for _, ws := range ps.Workspaces { - ids = append(ids, ws.ID) - } - - assert.Contains(t, ids, wTest1.ID) - assert.Contains(t, ids, wTest2.ID) - }) - - t.Run("without workspaces provided", func(t *testing.T) { - err := client.PolicySets.AddWorkspaces( - ctx, - psTest.ID, - PolicySetAddWorkspacesOptions{}, - ) - assert.EqualError(t, err, "workspaces is required") - }) - - t.Run("with empty workspaces slice", func(t *testing.T) { - err := client.PolicySets.AddWorkspaces( - ctx, - psTest.ID, - PolicySetAddWorkspacesOptions{Workspaces: []*Workspace{}}, - ) - assert.EqualError(t, err, "must provide at least one workspace") - }) - - t.Run("without a valid ID", func(t *testing.T) { - err := client.PolicySets.AddWorkspaces( - ctx, - badIdentifier, - PolicySetAddWorkspacesOptions{ - Workspaces: []*Workspace{wTest1, wTest2}, - }, - ) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsRemoveWorkspaces(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest1, _ := createWorkspace(t, client, orgTest) - wTest2, _ := createWorkspace(t, client, orgTest) - psTest, _ := createPolicySet(t, client, orgTest, nil, []*Workspace{wTest1, wTest2}) - - t.Run("with workspaces provided", func(t *testing.T) { - err := client.PolicySets.RemoveWorkspaces( - ctx, - psTest.ID, - PolicySetRemoveWorkspacesOptions{ - Workspaces: []*Workspace{wTest1, wTest2}, - }, - ) - require.NoError(t, err) - - ps, err := client.PolicySets.Read(ctx, psTest.ID) - require.NoError(t, err) - - assert.Equal(t, 0, ps.WorkspaceCount) - assert.Empty(t, ps.Workspaces) - }) - - t.Run("without workspaces provided", func(t *testing.T) { - err := client.PolicySets.RemoveWorkspaces( - ctx, - psTest.ID, - PolicySetRemoveWorkspacesOptions{}, - ) - assert.EqualError(t, err, "workspaces is required") - }) - - t.Run("with empty workspaces slice", func(t *testing.T) { - err := client.PolicySets.RemoveWorkspaces( - ctx, - psTest.ID, - PolicySetRemoveWorkspacesOptions{Workspaces: []*Workspace{}}, - ) - assert.EqualError(t, err, "must provide at least one workspace") - }) - - t.Run("without a valid ID", func(t *testing.T) { - err := client.PolicySets.RemoveWorkspaces( - ctx, - badIdentifier, - PolicySetRemoveWorkspacesOptions{ - Workspaces: []*Workspace{wTest1, wTest2}, - }, - ) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} - -func TestPolicySetsDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - psTest, _ := createPolicySet(t, client, orgTest, nil, nil) - - t.Run("with valid options", func(t *testing.T) { - err := client.PolicySets.Delete(ctx, psTest.ID) - require.NoError(t, err) - - // Try loading the policy - it should fail. - _, err = client.PolicySets.Read(ctx, psTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the policy does not exist", func(t *testing.T) { - err := client.PolicySets.Delete(ctx, psTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the policy ID is invalid", func(t *testing.T) { - err := client.PolicySets.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for policy set ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/policy_test.go b/vendor/github.com/hashicorp/go-tfe/policy_test.go deleted file mode 100644 index 888ca482d..000000000 --- a/vendor/github.com/hashicorp/go-tfe/policy_test.go +++ /dev/null @@ -1,393 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPoliciesList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest1, _ := createPolicy(t, client, orgTest) - pTest2, _ := createPolicy(t, client, orgTest) - - t.Run("without list options", func(t *testing.T) { - pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{}) - require.NoError(t, err) - assert.Contains(t, pl.Items, pTest1) - assert.Contains(t, pl.Items, pTest2) - - assert.Equal(t, 1, pl.CurrentPage) - assert.Equal(t, 2, pl.TotalCount) - }) - - t.Run("with pagination", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - - assert.Empty(t, pl.Items) - assert.Equal(t, 999, pl.CurrentPage) - assert.Equal(t, 2, pl.TotalCount) - }) - - t.Run("with search", func(t *testing.T) { - // Search by one of the policy's names; we should get only that policy - // and pagination data should reflect the search as well - pl, err := client.Policies.List(ctx, orgTest.Name, PolicyListOptions{ - Search: &pTest1.Name, - }) - require.NoError(t, err) - - assert.Contains(t, pl.Items, pTest1) - assert.NotContains(t, pl.Items, pTest2) - assert.Equal(t, 1, pl.CurrentPage) - assert.Equal(t, 1, pl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - ps, err := client.Policies.List(ctx, badIdentifier, PolicyListOptions{}) - assert.Nil(t, ps) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestPoliciesCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - name := randomString(t) - options := PolicyCreateOptions{ - Name: String(name), - Description: String("A sample policy"), - Enforce: []*EnforcementOptions{ - { - Path: String(name + ".sentinel"), - Mode: EnforcementMode(EnforcementSoft), - }, - }, - } - - p, err := client.Policies.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - // Get a refreshed view from the API. - refreshed, err := client.Policies.Read(ctx, p.ID) - require.NoError(t, err) - - for _, item := range []*Policy{ - p, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, *options.Name, item.Name) - assert.Equal(t, *options.Description, item.Description) - } - }) - - t.Run("when options has an invalid name", func(t *testing.T) { - p, err := client.Policies.Create(ctx, orgTest.Name, PolicyCreateOptions{ - Name: String(badIdentifier), - Enforce: []*EnforcementOptions{ - { - Path: String(badIdentifier + ".sentinel"), - Mode: EnforcementMode(EnforcementSoft), - }, - }, - }) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for name") - }) - - t.Run("when options is missing name", func(t *testing.T) { - p, err := client.Policies.Create(ctx, orgTest.Name, PolicyCreateOptions{ - Enforce: []*EnforcementOptions{ - { - Path: String(randomString(t) + ".sentinel"), - Mode: EnforcementMode(EnforcementSoft), - }, - }, - }) - assert.Nil(t, p) - assert.EqualError(t, err, "name is required") - }) - - t.Run("when options is missing an enforcement", func(t *testing.T) { - options := PolicyCreateOptions{ - Name: String(randomString(t)), - } - - p, err := client.Policies.Create(ctx, orgTest.Name, options) - assert.Nil(t, p) - assert.EqualError(t, err, "enforce is required") - }) - - t.Run("when options is missing enforcement path", func(t *testing.T) { - options := PolicyCreateOptions{ - Name: String(randomString(t)), - Enforce: []*EnforcementOptions{ - { - Mode: EnforcementMode(EnforcementSoft), - }, - }, - } - - p, err := client.Policies.Create(ctx, orgTest.Name, options) - assert.Nil(t, p) - assert.EqualError(t, err, "enforcement path is required") - }) - - t.Run("when options is missing enforcement path", func(t *testing.T) { - name := randomString(t) - options := PolicyCreateOptions{ - Name: String(name), - Enforce: []*EnforcementOptions{ - { - Path: String(name + ".sentinel"), - }, - }, - } - - p, err := client.Policies.Create(ctx, orgTest.Name, options) - assert.Nil(t, p) - assert.EqualError(t, err, "enforcement mode is required") - }) - - t.Run("when options has an invalid organization", func(t *testing.T) { - p, err := client.Policies.Create(ctx, badIdentifier, PolicyCreateOptions{ - Name: String("foo"), - }) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestPoliciesRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest, pTestCleanup := createPolicy(t, client, orgTest) - defer pTestCleanup() - - t.Run("when the policy exists without content", func(t *testing.T) { - p, err := client.Policies.Read(ctx, pTest.ID) - require.NoError(t, err) - - assert.Equal(t, pTest.ID, p.ID) - assert.Equal(t, pTest.Name, p.Name) - assert.Equal(t, pTest.PolicySetCount, p.PolicySetCount) - assert.Empty(t, p.Enforce) - assert.Equal(t, pTest.Organization.Name, p.Organization.Name) - }) - - err := client.Policies.Upload(ctx, pTest.ID, []byte(`main = rule { true }`)) - require.NoError(t, err) - - t.Run("when the policy exists with content", func(t *testing.T) { - p, err := client.Policies.Read(ctx, pTest.ID) - require.NoError(t, err) - - assert.Equal(t, pTest.ID, p.ID) - assert.Equal(t, pTest.Name, p.Name) - assert.Equal(t, pTest.Description, p.Description) - assert.Equal(t, pTest.PolicySetCount, p.PolicySetCount) - assert.NotEmpty(t, p.Enforce) - assert.Equal(t, pTest.Organization.Name, p.Organization.Name) - }) - - t.Run("when the policy does not exist", func(t *testing.T) { - p, err := client.Policies.Read(ctx, "nonexisting") - assert.Nil(t, p) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("without a valid policy ID", func(t *testing.T) { - p, err := client.Policies.Read(ctx, badIdentifier) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for policy ID") - }) -} - -func TestPoliciesUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("when updating with an existing path", func(t *testing.T) { - pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) - defer pBeforeCleanup() - - require.Equal(t, 1, len(pBefore.Enforce)) - - pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ - Enforce: []*EnforcementOptions{ - { - Path: String(pBefore.Enforce[0].Path), - Mode: EnforcementMode(EnforcementAdvisory), - }, - }, - }) - require.NoError(t, err) - require.Equal(t, 1, len(pAfter.Enforce)) - - assert.Equal(t, pBefore.ID, pAfter.ID) - assert.Equal(t, pBefore.Name, pAfter.Name) - assert.Equal(t, pBefore.Description, pAfter.Description) - assert.Equal(t, pBefore.Enforce[0].Path, pAfter.Enforce[0].Path) - assert.Equal(t, EnforcementAdvisory, pAfter.Enforce[0].Mode) - }) - - t.Run("when updating with a nonexisting path", func(t *testing.T) { - pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) - defer pBeforeCleanup() - - require.Equal(t, 1, len(pBefore.Enforce)) - - pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ - Enforce: []*EnforcementOptions{ - { - Path: String("nonexisting"), - Mode: EnforcementMode(EnforcementAdvisory), - }, - }, - }) - require.NoError(t, err) - - // Weirdly enough this is not equal as updating a nonexisting path - // causes the enforce mode to reset to the default hard-mandatory - t.Skip("see comment...") - assert.Equal(t, pBefore, pAfter) - }) - - t.Run("with a new description", func(t *testing.T) { - pBefore, pBeforeCleanup := createUploadedPolicy(t, client, true, orgTest) - defer pBeforeCleanup() - - pAfter, err := client.Policies.Update(ctx, pBefore.ID, PolicyUpdateOptions{ - Description: String("A brand new description"), - }) - require.NoError(t, err) - - assert.Equal(t, pBefore.Name, pAfter.Name) - assert.Equal(t, pBefore.Enforce, pAfter.Enforce) - assert.NotEqual(t, pBefore.Description, pAfter.Description) - assert.Equal(t, "A brand new description", pAfter.Description) - }) - - t.Run("without a valid policy ID", func(t *testing.T) { - p, err := client.Policies.Update(ctx, badIdentifier, PolicyUpdateOptions{}) - assert.Nil(t, p) - assert.EqualError(t, err, "invalid value for policy ID") - }) -} - -func TestPoliciesDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - pTest, _ := createPolicy(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.Policies.Delete(ctx, pTest.ID) - require.NoError(t, err) - - // Try loading the policy - it should fail. - _, err = client.Policies.Read(ctx, pTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the policy does not exist", func(t *testing.T) { - err := client.Policies.Delete(ctx, pTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the policy ID is invalid", func(t *testing.T) { - err := client.Policies.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for policy ID") - }) -} - -func TestPoliciesUpload(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - pTest, pTestCleanup := createPolicy(t, client, nil) - defer pTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - err := client.Policies.Upload(ctx, pTest.ID, []byte(`main = rule { true }`)) - assert.NoError(t, err) - }) - - t.Run("with empty content", func(t *testing.T) { - err := client.Policies.Upload(ctx, pTest.ID, []byte{}) - assert.NoError(t, err) - }) - - t.Run("without any content", func(t *testing.T) { - err := client.Policies.Upload(ctx, pTest.ID, nil) - assert.NoError(t, err) - }) - - t.Run("without a valid policy ID", func(t *testing.T) { - err := client.Policies.Upload(ctx, badIdentifier, []byte(`main = rule { true }`)) - assert.EqualError(t, err, "invalid value for policy ID") - }) -} - -func TestPoliciesDownload(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - pTest, pTestCleanup := createPolicy(t, client, nil) - defer pTestCleanup() - - testContent := []byte(`main = rule { true }`) - - t.Run("without existing content", func(t *testing.T) { - content, err := client.Policies.Download(ctx, pTest.ID) - assert.Equal(t, ErrResourceNotFound, err) - assert.Nil(t, content) - }) - - t.Run("with valid options", func(t *testing.T) { - err := client.Policies.Upload(ctx, pTest.ID, testContent) - require.NoError(t, err) - - content, err := client.Policies.Download(ctx, pTest.ID) - assert.NoError(t, err) - assert.Equal(t, testContent, content) - }) - - t.Run("without a valid policy ID", func(t *testing.T) { - content, err := client.Policies.Download(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for policy ID") - assert.Nil(t, content) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/run_test.go b/vendor/github.com/hashicorp/go-tfe/run_test.go deleted file mode 100644 index a23e8dd86..000000000 --- a/vendor/github.com/hashicorp/go-tfe/run_test.go +++ /dev/null @@ -1,277 +0,0 @@ -package tfe - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRunsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - rTest1, _ := createRun(t, client, wTest) - rTest2, _ := createRun(t, client, wTest) - - t.Run("without list options", func(t *testing.T) { - rl, err := client.Runs.List(ctx, wTest.ID, RunListOptions{}) - require.NoError(t, err) - - found := []string{} - for _, r := range rl.Items { - found = append(found, r.ID) - } - - assert.Contains(t, found, rTest1.ID) - assert.Contains(t, found, rTest2.ID) - assert.Equal(t, 1, rl.CurrentPage) - assert.Equal(t, 2, rl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - rl, err := client.Runs.List(ctx, wTest.ID, RunListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, rl.Items) - assert.Equal(t, 999, rl.CurrentPage) - assert.Equal(t, 2, rl.TotalCount) - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - rl, err := client.Runs.List(ctx, badIdentifier, RunListOptions{}) - assert.Nil(t, rl) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestRunsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - cvTest, _ := createUploadedConfigurationVersion(t, client, wTest) - - t.Run("without a configuration version", func(t *testing.T) { - options := RunCreateOptions{ - Workspace: wTest, - } - - _, err := client.Runs.Create(ctx, options) - assert.NoError(t, err) - }) - - t.Run("with a configuration version", func(t *testing.T) { - options := RunCreateOptions{ - ConfigurationVersion: cvTest, - Workspace: wTest, - } - - r, err := client.Runs.Create(ctx, options) - require.NoError(t, err) - assert.Equal(t, cvTest.ID, r.ConfigurationVersion.ID) - }) - - t.Run("without a workspace", func(t *testing.T) { - r, err := client.Runs.Create(ctx, RunCreateOptions{}) - assert.Nil(t, r) - assert.EqualError(t, err, "workspace is required") - }) - - t.Run("with additional attributes", func(t *testing.T) { - options := RunCreateOptions{ - Message: String("yo"), - Workspace: wTest, - } - - r, err := client.Runs.Create(ctx, options) - require.NoError(t, err) - assert.Equal(t, *options.Message, r.Message) - }) -} - -func TestRunsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createPlannedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the run exists", func(t *testing.T) { - r, err := client.Runs.Read(ctx, rTest.ID) - assert.NoError(t, err) - assert.Equal(t, rTest, r) - }) - - t.Run("when the run does not exist", func(t *testing.T) { - r, err := client.Runs.Read(ctx, "nonexisting") - assert.Nil(t, r) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid run ID", func(t *testing.T) { - r, err := client.Runs.Read(ctx, badIdentifier) - assert.Nil(t, r) - assert.EqualError(t, err, "invalid value for run ID") - }) -} - -func TestRunsApply(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createPlannedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the run exists", func(t *testing.T) { - err := client.Runs.Apply(ctx, rTest.ID, RunApplyOptions{}) - assert.NoError(t, err) - }) - - t.Run("when the run does not exist", func(t *testing.T) { - err := client.Runs.Apply(ctx, "nonexisting", RunApplyOptions{}) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid run ID", func(t *testing.T) { - err := client.Runs.Apply(ctx, badIdentifier, RunApplyOptions{}) - assert.EqualError(t, err, "invalid value for run ID") - }) -} - -func TestRunsCancel(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - // We need to create 2 runs here. The first run will automatically - // be planned so that one cannot be cancelled. The second one will - // be pending until the first one is confirmed or discarded, so we - // can cancel that one. - _, _ = createRun(t, client, wTest) - rTest2, _ := createRun(t, client, wTest) - - t.Run("when the run exists", func(t *testing.T) { - err := client.Runs.Cancel(ctx, rTest2.ID, RunCancelOptions{}) - assert.NoError(t, err) - }) - - t.Run("when the run does not exist", func(t *testing.T) { - err := client.Runs.Cancel(ctx, "nonexisting", RunCancelOptions{}) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid run ID", func(t *testing.T) { - err := client.Runs.Cancel(ctx, badIdentifier, RunCancelOptions{}) - assert.EqualError(t, err, "invalid value for run ID") - }) -} - -func TestRunsForceCancel(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - // We need to create 2 runs here. The first run will automatically - // be planned so that one cannot be cancelled. The second one will - // be pending until the first one is confirmed or discarded, so we - // can cancel that one. - _, _ = createRun(t, client, wTest) - rTest, _ := createRun(t, client, wTest) - - t.Run("run is not force-cancelable", func(t *testing.T) { - assert.False(t, rTest.Actions.IsForceCancelable) - }) - - t.Run("user is allowed to force-cancel", func(t *testing.T) { - assert.True(t, rTest.Permissions.CanForceCancel) - }) - - t.Run("after a normal cancel", func(t *testing.T) { - // Request the normal cancel - err := client.Runs.Cancel(ctx, rTest.ID, RunCancelOptions{}) - require.NoError(t, err) - - for i := 1; ; i++ { - // Refresh the view of the run - rTest, err = client.Runs.Read(ctx, rTest.ID) - require.NoError(t, err) - - // Check if the timestamp is present. - if !rTest.ForceCancelAvailableAt.IsZero() { - break - } - - if i > 30 { - t.Fatal("Timeout waiting for run to be canceled") - } - - time.Sleep(time.Second) - } - - t.Run("force-cancel-available-at timestamp is present", func(t *testing.T) { - assert.True(t, rTest.ForceCancelAvailableAt.After(time.Now())) - }) - - // This test case is minimal because a force-cancel is not needed in - // any normal circumstance. Only if Terraform encounters unexpected - // errors or behaves abnormally should this functionality be required. - // Force-cancel only becomes available if a normal cancel is performed - // first, and the desired canceled state is not reached within a pre- - // determined amount of time (see - // https://www.terraform.io/docs/enterprise/api/run.html#forcefully-cancel-a-run). - }) - - t.Run("when the run does not exist", func(t *testing.T) { - err := client.Runs.ForceCancel(ctx, "nonexisting", RunForceCancelOptions{}) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid run ID", func(t *testing.T) { - err := client.Runs.ForceCancel(ctx, badIdentifier, RunForceCancelOptions{}) - assert.EqualError(t, err, "invalid value for run ID") - }) -} - -func TestRunsDiscard(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - rTest, rTestCleanup := createPlannedRun(t, client, nil) - defer rTestCleanup() - - t.Run("when the run exists", func(t *testing.T) { - err := client.Runs.Discard(ctx, rTest.ID, RunDiscardOptions{}) - assert.NoError(t, err) - }) - - t.Run("when the run does not exist", func(t *testing.T) { - err := client.Runs.Discard(ctx, "nonexisting", RunDiscardOptions{}) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid run ID", func(t *testing.T) { - err := client.Runs.Discard(ctx, badIdentifier, RunDiscardOptions{}) - assert.EqualError(t, err, "invalid value for run ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go b/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go deleted file mode 100644 index c34adc0a6..000000000 --- a/vendor/github.com/hashicorp/go-tfe/ssh_key_test.go +++ /dev/null @@ -1,219 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSSHKeysList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - kTest1, _ := createSSHKey(t, client, orgTest) - kTest2, _ := createSSHKey(t, client, orgTest) - - t.Run("without list options", func(t *testing.T) { - kl, err := client.SSHKeys.List(ctx, orgTest.Name, SSHKeyListOptions{}) - require.NoError(t, err) - assert.Contains(t, kl.Items, kTest1) - assert.Contains(t, kl.Items, kTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, kl.CurrentPage) - assert.Equal(t, 2, kl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - kl, err := client.SSHKeys.List(ctx, orgTest.Name, SSHKeyListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, kl.Items) - assert.Equal(t, 999, kl.CurrentPage) - assert.Equal(t, 2, kl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - kl, err := client.SSHKeys.List(ctx, badIdentifier, SSHKeyListOptions{}) - assert.Nil(t, kl) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestSSHKeysCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := SSHKeyCreateOptions{ - Name: String(randomString(t)), - Value: String(randomString(t)), - } - - k, err := client.SSHKeys.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - // Get a refreshed view from the API. - refreshed, err := client.SSHKeys.Read(ctx, k.ID) - require.NoError(t, err) - - for _, item := range []*SSHKey{ - k, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, *options.Name, item.Name) - } - }) - - t.Run("when options is missing name", func(t *testing.T) { - k, err := client.SSHKeys.Create(ctx, "foo", SSHKeyCreateOptions{ - Value: String(randomString(t)), - }) - assert.Nil(t, k) - assert.EqualError(t, err, "name is required") - }) - - t.Run("when options is missing value", func(t *testing.T) { - k, err := client.SSHKeys.Create(ctx, "foo", SSHKeyCreateOptions{ - Name: String(randomString(t)), - }) - assert.Nil(t, k) - assert.EqualError(t, err, "value is required") - }) - - t.Run("when options has an invalid organization", func(t *testing.T) { - k, err := client.SSHKeys.Create(ctx, badIdentifier, SSHKeyCreateOptions{ - Name: String("foo"), - }) - assert.Nil(t, k) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestSSHKeysRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - kTest, _ := createSSHKey(t, client, orgTest) - - t.Run("when the SSH key exists", func(t *testing.T) { - k, err := client.SSHKeys.Read(ctx, kTest.ID) - require.NoError(t, err) - assert.Equal(t, kTest, k) - }) - - t.Run("when the SSH key does not exist", func(t *testing.T) { - k, err := client.SSHKeys.Read(ctx, "nonexisting") - assert.Nil(t, k) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without a valid SSH key ID", func(t *testing.T) { - k, err := client.SSHKeys.Read(ctx, badIdentifier) - assert.Nil(t, k) - assert.EqualError(t, err, "invalid value for SSH key ID") - }) -} - -func TestSSHKeysUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - kBefore, kTestCleanup := createSSHKey(t, client, orgTest) - defer kTestCleanup() - - kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ - Name: String(randomString(t)), - Value: String(randomString(t)), - }) - require.NoError(t, err) - - assert.Equal(t, kBefore.ID, kAfter.ID) - assert.NotEqual(t, kBefore.Name, kAfter.Name) - }) - - t.Run("when updating the name", func(t *testing.T) { - kBefore, kTestCleanup := createSSHKey(t, client, orgTest) - defer kTestCleanup() - - kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ - Name: String("updated-key-name"), - }) - require.NoError(t, err) - - assert.Equal(t, kBefore.ID, kAfter.ID) - assert.Equal(t, "updated-key-name", kAfter.Name) - }) - - t.Run("when updating the value", func(t *testing.T) { - kBefore, kTestCleanup := createSSHKey(t, client, orgTest) - defer kTestCleanup() - - kAfter, err := client.SSHKeys.Update(ctx, kBefore.ID, SSHKeyUpdateOptions{ - Value: String("updated-key-value"), - }) - require.NoError(t, err) - - assert.Equal(t, kBefore.ID, kAfter.ID) - assert.Equal(t, kBefore.Name, kAfter.Name) - }) - - t.Run("without a valid SSH key ID", func(t *testing.T) { - w, err := client.SSHKeys.Update(ctx, badIdentifier, SSHKeyUpdateOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for SSH key ID") - }) -} - -func TestSSHKeysDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - kTest, _ := createSSHKey(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.SSHKeys.Delete(ctx, kTest.ID) - require.NoError(t, err) - - // Try loading the SSH key - it should fail. - _, err = client.SSHKeys.Read(ctx, kTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the SSH key does not exist", func(t *testing.T) { - err := client.SSHKeys.Delete(ctx, kTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the SSH key ID is invalid", func(t *testing.T) { - err := client.SSHKeys.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for SSH key ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/state_version_test.go b/vendor/github.com/hashicorp/go-tfe/state_version_test.go deleted file mode 100644 index a6b77dd27..000000000 --- a/vendor/github.com/hashicorp/go-tfe/state_version_test.go +++ /dev/null @@ -1,350 +0,0 @@ -package tfe - -import ( - "context" - "crypto/md5" - "encoding/base64" - "fmt" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestStateVersionsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, wTestCleanup := createWorkspace(t, client, orgTest) - defer wTestCleanup() - - svTest1, _ := createStateVersion(t, client, 0, wTest) - svTest2, _ := createStateVersion(t, client, 1, wTest) - - t.Run("without list options", func(t *testing.T) { - options := StateVersionListOptions{ - Organization: String(orgTest.Name), - Workspace: String(wTest.Name), - } - - svl, err := client.StateVersions.List(ctx, options) - require.NoError(t, err) - - // We need to strip the upload URL as that is a dynamic link. - svTest1.DownloadURL = "" - svTest2.DownloadURL = "" - - // And for the retrieved configuration versions as well. - for _, sv := range svl.Items { - sv.DownloadURL = "" - } - - assert.Contains(t, svl.Items, svTest1) - assert.Contains(t, svl.Items, svTest2) - assert.Equal(t, 1, svl.CurrentPage) - assert.Equal(t, 2, svl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - options := StateVersionListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - Organization: String(orgTest.Name), - Workspace: String(wTest.Name), - } - - svl, err := client.StateVersions.List(ctx, options) - require.NoError(t, err) - assert.Empty(t, svl.Items) - assert.Equal(t, 999, svl.CurrentPage) - assert.Equal(t, 2, svl.TotalCount) - }) - - t.Run("without an organization", func(t *testing.T) { - options := StateVersionListOptions{ - Workspace: String(wTest.Name), - } - - svl, err := client.StateVersions.List(ctx, options) - assert.Nil(t, svl) - assert.EqualError(t, err, "organization is required") - }) - - t.Run("without a workspace", func(t *testing.T) { - options := StateVersionListOptions{ - Organization: String(orgTest.Name), - } - - svl, err := client.StateVersions.List(ctx, options) - assert.Nil(t, svl) - assert.EqualError(t, err, "workspace is required") - }) -} - -func TestStateVersionsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - state, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") - if err != nil { - t.Fatal(err) - } - - t.Run("with valid options", func(t *testing.T) { - ctx := context.Background() - _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - if err != nil { - t.Fatal(err) - } - - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - Lineage: String("741c4949-60b9-5bb1-5bf8-b14f4bb14af3"), - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(1), - State: String(base64.StdEncoding.EncodeToString(state)), - }) - require.NoError(t, err) - - // Get a refreshed view of the configuration version. - refreshed, err := client.StateVersions.Read(ctx, sv.ID) - require.NoError(t, err) - - _, err = client.Workspaces.Unlock(ctx, wTest.ID) - if err != nil { - t.Fatal(err) - } - - for _, item := range []*StateVersion{ - sv, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, int64(1), item.Serial) - assert.NotEmpty(t, item.CreatedAt) - assert.NotEmpty(t, item.DownloadURL) - } - }) - - t.Run("with the force flag set", func(t *testing.T) { - ctx := context.Background() - _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - if err != nil { - t.Fatal(err) - } - - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - Lineage: String("741c4949-60b9-5bb1-5bf8-b14f4bb14af3"), - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(1), - State: String(base64.StdEncoding.EncodeToString(state)), - }) - require.NoError(t, err) - - sv, err = client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - Lineage: String("821c4747-a0b9-3bd1-8bf3-c14f4bb14be7"), - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(2), - State: String(base64.StdEncoding.EncodeToString(state)), - Force: Bool(true), - }) - require.NoError(t, err) - - // Get a refreshed view of the configuration version. - refreshed, err := client.StateVersions.Read(ctx, sv.ID) - require.NoError(t, err) - - _, err = client.Workspaces.Unlock(ctx, wTest.ID) - if err != nil { - t.Fatal(err) - } - - for _, item := range []*StateVersion{ - sv, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, int64(2), item.Serial) - assert.NotEmpty(t, item.CreatedAt) - assert.NotEmpty(t, item.DownloadURL) - } - }) - - t.Run("with a run to associate with", func(t *testing.T) { - t.Skip("This can only be tested with the run specific token") - - rTest, _ := createRun(t, client, wTest) - - ctx := context.Background() - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(0), - State: String(base64.StdEncoding.EncodeToString(state)), - Run: rTest, - }) - require.NoError(t, err) - require.NotEmpty(t, sv.Run) - - // Get a refreshed view of the configuration version. - refreshed, err := client.StateVersions.Read(ctx, sv.ID) - require.NoError(t, err) - require.NotEmpty(t, refreshed.Run) - - for _, item := range []*StateVersion{ - sv, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, int64(0), item.Serial) - assert.NotEmpty(t, item.CreatedAt) - assert.NotEmpty(t, item.DownloadURL) - assert.Equal(t, rTest.ID, item.Run.ID) - } - }) - - t.Run("without md5 hash", func(t *testing.T) { - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - Serial: Int64(0), - State: String(base64.StdEncoding.EncodeToString(state)), - }) - assert.Nil(t, sv) - assert.EqualError(t, err, "MD5 is required") - }) - - t.Run("withous serial", func(t *testing.T) { - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - State: String(base64.StdEncoding.EncodeToString(state)), - }) - assert.Nil(t, sv) - assert.EqualError(t, err, "serial is required") - }) - - t.Run("without state", func(t *testing.T) { - sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ - MD5: String(fmt.Sprintf("%x", md5.Sum(state))), - Serial: Int64(0), - }) - assert.Nil(t, sv) - assert.EqualError(t, err, "state is required") - }) - - t.Run("with invalid workspace id", func(t *testing.T) { - sv, err := client.StateVersions.Create(ctx, badIdentifier, StateVersionCreateOptions{}) - assert.Nil(t, sv) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestStateVersionsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - svTest, svTestCleanup := createStateVersion(t, client, 0, nil) - defer svTestCleanup() - - t.Run("when the state version exists", func(t *testing.T) { - sv, err := client.StateVersions.Read(ctx, svTest.ID) - require.NoError(t, err) - - // Don't compare the DownloadURL because it will be generated twice - // in this test - once at creation of the configuration version, and - // again during the GET. - svTest.DownloadURL, sv.DownloadURL = "", "" - - assert.Equal(t, svTest, sv) - }) - - t.Run("when the state version does not exist", func(t *testing.T) { - sv, err := client.StateVersions.Read(ctx, "nonexisting") - assert.Nil(t, sv) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("with invalid state version id", func(t *testing.T) { - sv, err := client.StateVersions.Read(ctx, badIdentifier) - assert.Nil(t, sv) - assert.EqualError(t, err, "invalid value for state version ID") - }) -} - -func TestStateVersionsCurrent(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest1, wTest1Cleanup := createWorkspace(t, client, nil) - defer wTest1Cleanup() - - wTest2, wTest2Cleanup := createWorkspace(t, client, nil) - defer wTest2Cleanup() - - svTest, svTestCleanup := createStateVersion(t, client, 0, wTest1) - defer svTestCleanup() - - t.Run("when a state version exists", func(t *testing.T) { - sv, err := client.StateVersions.Current(ctx, wTest1.ID) - require.NoError(t, err) - - // Don't compare the DownloadURL because it will be generated twice - // in this test - once at creation of the configuration version, and - // again during the GET. - svTest.DownloadURL, sv.DownloadURL = "", "" - - assert.Equal(t, svTest, sv) - }) - - t.Run("when a state version does not exist", func(t *testing.T) { - sv, err := client.StateVersions.Current(ctx, wTest2.ID) - assert.Nil(t, sv) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("with invalid workspace id", func(t *testing.T) { - sv, err := client.StateVersions.Current(ctx, badIdentifier) - assert.Nil(t, sv) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestStateVersionsDownload(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - svTest, svTestCleanup := createStateVersion(t, client, 0, nil) - defer svTestCleanup() - - stateTest, err := ioutil.ReadFile("test-fixtures/state-version/terraform.tfstate") - require.NoError(t, err) - - t.Run("when the state version exists", func(t *testing.T) { - state, err := client.StateVersions.Download(ctx, svTest.DownloadURL) - require.NoError(t, err) - assert.Equal(t, stateTest, state) - }) - - t.Run("when the state version does not exist", func(t *testing.T) { - state, err := client.StateVersions.Download( - ctx, - svTest.DownloadURL[:len(svTest.DownloadURL)-10]+"nonexisting", - ) - assert.Nil(t, state) - assert.Error(t, err) - }) - - t.Run("with an invalid url", func(t *testing.T) { - state, err := client.StateVersions.Download(ctx, badIdentifier) - assert.Nil(t, state) - assert.Equal(t, ErrResourceNotFound, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/team_access_test.go b/vendor/github.com/hashicorp/go-tfe/team_access_test.go deleted file mode 100644 index fea595e98..000000000 --- a/vendor/github.com/hashicorp/go-tfe/team_access_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTeamAccessesList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - tmTest1, tmTest1Cleanup := createTeam(t, client, orgTest) - defer tmTest1Cleanup() - tmTest2, tmTest2Cleanup := createTeam(t, client, orgTest) - defer tmTest2Cleanup() - - taTest1, taTest1Cleanup := createTeamAccess(t, client, tmTest1, wTest, orgTest) - defer taTest1Cleanup() - taTest2, taTest2Cleanup := createTeamAccess(t, client, tmTest2, wTest, orgTest) - defer taTest2Cleanup() - - t.Run("with valid options", func(t *testing.T) { - tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ - WorkspaceID: String(wTest.ID), - }) - require.NoError(t, err) - assert.Contains(t, tal.Items, taTest1) - assert.Contains(t, tal.Items, taTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, tal.CurrentPage) - assert.Equal(t, 2, tal.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, tal.Items) - assert.Equal(t, 999, tal.CurrentPage) - assert.Equal(t, 2, tal.TotalCount) - }) - - t.Run("without list options", func(t *testing.T) { - tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{}) - assert.Nil(t, tal) - assert.EqualError(t, err, "workspace ID is required") - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - tal, err := client.TeamAccess.List(ctx, TeamAccessListOptions{ - WorkspaceID: String(badIdentifier), - }) - assert.Nil(t, tal) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestTeamAccessesAdd(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - tmTest, tmTestCleanup := createTeam(t, client, orgTest) - defer tmTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := TeamAccessAddOptions{ - Access: Access(AccessAdmin), - Team: tmTest, - Workspace: wTest, - } - - ta, err := client.TeamAccess.Add(ctx, options) - require.NoError(t, err) - - // Get a refreshed view from the API. - refreshed, err := client.TeamAccess.Read(ctx, ta.ID) - require.NoError(t, err) - - for _, item := range []*TeamAccess{ - ta, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, *options.Access, item.Access) - } - }) - - t.Run("when the team already has access", func(t *testing.T) { - options := TeamAccessAddOptions{ - Access: Access(AccessAdmin), - Team: tmTest, - Workspace: wTest, - } - - _, err := client.TeamAccess.Add(ctx, options) - assert.Error(t, err) - }) - - t.Run("when options is missing access", func(t *testing.T) { - ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ - Team: tmTest, - Workspace: wTest, - }) - assert.Nil(t, ta) - assert.EqualError(t, err, "access is required") - }) - - t.Run("when options is missing team", func(t *testing.T) { - ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ - Access: Access(AccessAdmin), - Workspace: wTest, - }) - assert.Nil(t, ta) - assert.EqualError(t, err, "team is required") - }) - - t.Run("when options is missing workspace", func(t *testing.T) { - ta, err := client.TeamAccess.Add(ctx, TeamAccessAddOptions{ - Access: Access(AccessAdmin), - Team: tmTest, - }) - assert.Nil(t, ta) - assert.EqualError(t, err, "workspace is required") - }) -} - -func TestTeamAccessesRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - taTest, taTestCleanup := createTeamAccess(t, client, nil, nil, nil) - defer taTestCleanup() - - t.Run("when the team access exists", func(t *testing.T) { - ta, err := client.TeamAccess.Read(ctx, taTest.ID) - require.NoError(t, err) - - assert.Equal(t, AccessAdmin, ta.Access) - - t.Run("team relationship is decoded", func(t *testing.T) { - assert.NotEmpty(t, ta.Team) - }) - - t.Run("workspace relationship is decoded", func(t *testing.T) { - assert.NotEmpty(t, ta.Workspace) - }) - }) - - t.Run("when the team access does not exist", func(t *testing.T) { - ta, err := client.TeamAccess.Read(ctx, "nonexisting") - assert.Nil(t, ta) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without a valid team access ID", func(t *testing.T) { - ta, err := client.TeamAccess.Read(ctx, badIdentifier) - assert.Nil(t, ta) - assert.EqualError(t, err, "invalid value for team access ID") - }) -} - -func TestTeamAccessesRemove(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - tmTest, tmTestCleanup := createTeam(t, client, orgTest) - defer tmTestCleanup() - - taTest, _ := createTeamAccess(t, client, tmTest, nil, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.TeamAccess.Remove(ctx, taTest.ID) - require.NoError(t, err) - - // Try loading the workspace - it should fail. - _, err = client.TeamAccess.Read(ctx, taTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the team access does not exist", func(t *testing.T) { - err := client.TeamAccess.Remove(ctx, taTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("when the team access ID is invalid", func(t *testing.T) { - err := client.TeamAccess.Remove(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for team access ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/team_member_test.go b/vendor/github.com/hashicorp/go-tfe/team_member_test.go deleted file mode 100644 index 1182bac5d..000000000 --- a/vendor/github.com/hashicorp/go-tfe/team_member_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTeamMembersList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - options := TeamMemberAddOptions{ - Usernames: []string{"admin"}, - } - err := client.TeamMembers.Add(ctx, tmTest.ID, options) - require.NoError(t, err) - - t.Run("with valid options", func(t *testing.T) { - users, err := client.TeamMembers.List(ctx, tmTest.ID) - require.NoError(t, err) - require.Equal(t, 1, len(users)) - - found := false - for _, user := range users { - if user.Username == "admin" { - found = true - break - } - } - - assert.True(t, found) - }) - - t.Run("when the team ID is invalid", func(t *testing.T) { - users, err := client.TeamMembers.List(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for team ID") - assert.Nil(t, users) - }) -} - -func TestTeamMembersAdd(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := TeamMemberAddOptions{ - Usernames: []string{"admin"}, - } - - err := client.TeamMembers.Add(ctx, tmTest.ID, options) - require.NoError(t, err) - - users, err := client.TeamMembers.List(ctx, tmTest.ID) - require.NoError(t, err) - - found := false - for _, user := range users { - if user.Username == "admin" { - found = true - break - } - } - - assert.True(t, found) - }) - - t.Run("when options is missing usernames", func(t *testing.T) { - err := client.TeamMembers.Add(ctx, tmTest.ID, TeamMemberAddOptions{}) - assert.EqualError(t, err, "usernames is required") - }) - - t.Run("when usernames is empty", func(t *testing.T) { - err := client.TeamMembers.Add(ctx, tmTest.ID, TeamMemberAddOptions{ - Usernames: []string{}, - }) - assert.EqualError(t, err, "invalid value for usernames") - }) - - t.Run("when the team ID is invalid", func(t *testing.T) { - err := client.TeamMembers.Add(ctx, badIdentifier, TeamMemberAddOptions{ - Usernames: []string{"user1"}, - }) - assert.EqualError(t, err, "invalid value for team ID") - }) -} - -func TestTeamMembersRemove(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - options := TeamMemberAddOptions{ - Usernames: []string{"admin"}, - } - err := client.TeamMembers.Add(ctx, tmTest.ID, options) - require.NoError(t, err) - - t.Run("with valid options", func(t *testing.T) { - options := TeamMemberRemoveOptions{ - Usernames: []string{"admin"}, - } - - err := client.TeamMembers.Remove(ctx, tmTest.ID, options) - assert.NoError(t, err) - }) - - t.Run("when options is missing usernames", func(t *testing.T) { - err := client.TeamMembers.Remove(ctx, tmTest.ID, TeamMemberRemoveOptions{}) - assert.EqualError(t, err, "usernames is required") - }) - - t.Run("when usernames is empty", func(t *testing.T) { - err := client.TeamMembers.Remove(ctx, tmTest.ID, TeamMemberRemoveOptions{ - Usernames: []string{}, - }) - assert.EqualError(t, err, "invalid value for usernames") - }) - - t.Run("when the team ID is invalid", func(t *testing.T) { - err := client.TeamMembers.Remove(ctx, badIdentifier, TeamMemberRemoveOptions{ - Usernames: []string{"user1"}, - }) - assert.EqualError(t, err, "invalid value for team ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/team_test.go b/vendor/github.com/hashicorp/go-tfe/team_test.go deleted file mode 100644 index 81cf0cb6d..000000000 --- a/vendor/github.com/hashicorp/go-tfe/team_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTeamsList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - tmTest1, tmTest1Cleanup := createTeam(t, client, orgTest) - defer tmTest1Cleanup() - tmTest2, tmTest2Cleanup := createTeam(t, client, orgTest) - defer tmTest2Cleanup() - - t.Run("without list options", func(t *testing.T) { - tl, err := client.Teams.List(ctx, orgTest.Name, TeamListOptions{}) - require.NoError(t, err) - assert.Contains(t, tl.Items, tmTest1) - assert.Contains(t, tl.Items, tmTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, tl.CurrentPage) - assert.Equal(t, 2, tl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - tl, err := client.Teams.List(ctx, orgTest.Name, TeamListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, tl.Items) - assert.Equal(t, 999, tl.CurrentPage) - assert.Equal(t, 2, tl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - tl, err := client.Teams.List(ctx, badIdentifier, TeamListOptions{}) - assert.Nil(t, tl) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestTeamsCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := TeamCreateOptions{ - Name: String("foo"), - } - - tm, err := client.Teams.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - // Get a refreshed view from the API. - refreshed, err := client.Teams.Read(ctx, tm.ID) - require.NoError(t, err) - - for _, item := range []*Team{ - tm, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, *options.Name, item.Name) - } - }) - - t.Run("when options is missing name", func(t *testing.T) { - tm, err := client.Teams.Create(ctx, "foo", TeamCreateOptions{}) - assert.Nil(t, tm) - assert.EqualError(t, err, "name is required") - }) - - t.Run("when options has an invalid organization", func(t *testing.T) { - tm, err := client.Teams.Create(ctx, badIdentifier, TeamCreateOptions{ - Name: String("foo"), - }) - assert.Nil(t, tm) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestTeamsRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - tmTest, tmTestCleanup := createTeam(t, client, orgTest) - defer tmTestCleanup() - - t.Run("when the team exists", func(t *testing.T) { - tm, err := client.Teams.Read(ctx, tmTest.ID) - require.NoError(t, err) - assert.Equal(t, tmTest, tm) - - t.Run("permissions are properly decoded", func(t *testing.T) { - assert.True(t, tm.Permissions.CanDestroy) - }) - - t.Run("organization access is properly decoded", func(t *testing.T) { - assert.True(t, tm.OrganizationAccess.ManagePolicies) - assert.False(t, tm.OrganizationAccess.ManageWorkspaces) - }) - }) - - t.Run("when the team does not exist", func(t *testing.T) { - tm, err := client.Teams.Read(ctx, "nonexisting") - assert.Nil(t, tm) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without a valid team ID", func(t *testing.T) { - tm, err := client.Teams.Read(ctx, badIdentifier) - assert.Nil(t, tm) - assert.EqualError(t, err, "invalid value for team ID") - }) -} - -func TestTeamsUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - tmTest, tmTestCleanup := createTeam(t, client, orgTest) - defer tmTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := TeamUpdateOptions{ - Name: String("foo bar"), - OrganizationAccess: &OrganizationAccessOptions{ - ManagePolicies: Bool(false), - ManageVCSSettings: Bool(true)}, - } - - tm, err := client.Teams.Update(ctx, tmTest.ID, options) - require.NoError(t, err) - - refreshed, err := client.Teams.Read(ctx, tmTest.ID) - require.NoError(t, err) - - for _, item := range []*Team{ - tm, - refreshed, - } { - assert.Equal(t, *options.Name, item.Name) - assert.Equal(t, - *options.OrganizationAccess.ManagePolicies, - item.OrganizationAccess.ManagePolicies, - ) - assert.Equal(t, - *options.OrganizationAccess.ManageVCSSettings, - item.OrganizationAccess.ManageVCSSettings, - ) - } - }) - - t.Run("when the team does not exist", func(t *testing.T) { - tm, err := client.Teams.Update(ctx, "nonexisting", TeamUpdateOptions{ - Name: String("foo bar"), - }) - assert.Nil(t, tm) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without a valid team ID", func(t *testing.T) { - tm, err := client.Teams.Update(ctx, badIdentifier, TeamUpdateOptions{}) - assert.Nil(t, tm) - assert.EqualError(t, err, "invalid value for team ID") - }) -} - -func TestTeamsDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - tmTest, _ := createTeam(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.Teams.Delete(ctx, tmTest.ID) - require.NoError(t, err) - - // Try loading the workspace - it should fail. - _, err = client.Teams.Read(ctx, tmTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without valid team ID", func(t *testing.T) { - err := client.Teams.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for team ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/team_token_test.go b/vendor/github.com/hashicorp/go-tfe/team_token_test.go deleted file mode 100644 index abbc69c13..000000000 --- a/vendor/github.com/hashicorp/go-tfe/team_token_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTeamTokensGenerate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - var tmToken string - t.Run("with valid options", func(t *testing.T) { - tt, err := client.TeamTokens.Generate(ctx, tmTest.ID) - require.NoError(t, err) - require.NotEmpty(t, tt.Token) - tmToken = tt.Token - }) - - t.Run("when a token already exists", func(t *testing.T) { - tt, err := client.TeamTokens.Generate(ctx, tmTest.ID) - require.NoError(t, err) - require.NotEmpty(t, tt.Token) - assert.NotEqual(t, tmToken, tt.Token) - }) - - t.Run("without valid team ID", func(t *testing.T) { - tt, err := client.TeamTokens.Generate(ctx, badIdentifier) - assert.Nil(t, tt) - assert.EqualError(t, err, "invalid value for team ID") - }) -} -func TestTeamTokensRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - _, ttTestCleanup := createTeamToken(t, client, tmTest) - - tt, err := client.TeamTokens.Read(ctx, tmTest.ID) - assert.NoError(t, err) - assert.NotEmpty(t, tt) - - ttTestCleanup() - }) - - t.Run("when a token doesn't exists", func(t *testing.T) { - tt, err := client.TeamTokens.Read(ctx, tmTest.ID) - assert.Equal(t, ErrResourceNotFound, err) - assert.Nil(t, tt) - }) - - t.Run("without valid organization", func(t *testing.T) { - tt, err := client.OrganizationTokens.Read(ctx, badIdentifier) - assert.Nil(t, tt) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestTeamTokensDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - tmTest, tmTestCleanup := createTeam(t, client, nil) - defer tmTestCleanup() - - createTeamToken(t, client, tmTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.TeamTokens.Delete(ctx, tmTest.ID) - assert.NoError(t, err) - }) - - t.Run("when a token does not exist", func(t *testing.T) { - err := client.TeamTokens.Delete(ctx, tmTest.ID) - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("without valid team ID", func(t *testing.T) { - err := client.TeamTokens.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for team ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt deleted file mode 100644 index 5716ca598..000000000 --- a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/bar.txt +++ /dev/null @@ -1 +0,0 @@ -bar diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/exe deleted file mode 100755 index e69de29bb..000000000 diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt deleted file mode 100644 index 257cc5642..000000000 --- a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/foo.txt +++ /dev/null @@ -1 +0,0 @@ -foo diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt deleted file mode 120000 index 416e3bd9e..000000000 --- a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/foo.txt +++ /dev/null @@ -1 +0,0 @@ -../foo.txt \ No newline at end of file diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt b/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt deleted file mode 100644 index d0513b233..000000000 --- a/vendor/github.com/hashicorp/go-tfe/test-fixtures/archive-dir/sub/zip.txt +++ /dev/null @@ -1 +0,0 @@ -zip diff --git a/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf b/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf deleted file mode 100644 index 3911a2a9b..000000000 --- a/vendor/github.com/hashicorp/go-tfe/test-fixtures/config-version/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "null_resource" "foo" {} diff --git a/vendor/github.com/hashicorp/go-tfe/tfe_test.go b/vendor/github.com/hashicorp/go-tfe/tfe_test.go deleted file mode 100644 index bb23af2e8..000000000 --- a/vendor/github.com/hashicorp/go-tfe/tfe_test.go +++ /dev/null @@ -1,398 +0,0 @@ -package tfe - -import ( - "context" - "errors" - "net/http" - "net/http/httptest" - "os" - "testing" - "time" - - "golang.org/x/time/rate" -) - -func TestClient_newClient(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", "30") - w.WriteHeader(404) // We query the configured base URL which should return a 404. - })) - defer ts.Close() - - cfg := &Config{ - HTTPClient: ts.Client(), - } - - t.Run("uses env vars if values are missing", func(t *testing.T) { - defer setupEnvVars("abcd1234", ts.URL)() - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - if client.token != "abcd1234" { - t.Fatalf("unexpected token: %q", client.token) - } - if client.baseURL.String() != ts.URL+DefaultBasePath { - t.Fatalf("unexpected address: %q", client.baseURL.String()) - } - }) - - t.Run("fails if token is empty", func(t *testing.T) { - defer setupEnvVars("", "")() - - _, err := NewClient(cfg) - if err == nil || err.Error() != "missing API token" { - t.Fatalf("unexpected error: %v", err) - } - }) - - t.Run("makes a new client with good settings", func(t *testing.T) { - config := &Config{ - Address: ts.URL, - Token: "abcd1234", - HTTPClient: ts.Client(), - } - - client, err := NewClient(config) - if err != nil { - t.Fatal(err) - } - - if config.Address+DefaultBasePath != client.baseURL.String() { - t.Fatalf("unexpected client address %q", client.baseURL.String()) - } - if config.Token != client.token { - t.Fatalf("unexpected client token %q", client.token) - } - if ts.Client() != client.http.HTTPClient { - t.Fatal("unexpected HTTP client value") - } - }) -} - -func TestClient_defaultConfig(t *testing.T) { - t.Run("with no environment variables", func(t *testing.T) { - defer setupEnvVars("", "")() - - config := DefaultConfig() - - if config.Address != DefaultAddress { - t.Fatalf("expected %q, got %q", DefaultAddress, config.Address) - } - if config.Token != "" { - t.Fatalf("expected empty token, got %q", config.Token) - } - if config.HTTPClient == nil { - t.Fatalf("expected default http client, got %v", config.HTTPClient) - } - }) - - t.Run("with environment variables", func(t *testing.T) { - defer setupEnvVars("abcd1234", "https://mytfe.local")() - }) -} - -func TestClient_headers(t *testing.T) { - testedCalls := 0 - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - testedCalls++ - - if testedCalls == 1 { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", "30") - w.WriteHeader(404) // We query the configured base URL which should return a 404. - return - } - - if r.Header.Get("Accept") != "application/vnd.api+json" { - t.Fatalf("unexpected accept header: %q", r.Header.Get("Accept")) - } - if r.Header.Get("Authorization") != "Bearer dummy-token" { - t.Fatalf("unexpected authorization header: %q", r.Header.Get("Authorization")) - } - if r.Header.Get("My-Custom-Header") != "foobar" { - t.Fatalf("unexpected custom header: %q", r.Header.Get("My-Custom-Header")) - } - if r.Header.Get("Terraform-Version") != "0.11.9" { - t.Fatalf("unexpected Terraform version header: %q", r.Header.Get("Terraform-Version")) - } - if r.Header.Get("User-Agent") != "go-tfe" { - t.Fatalf("unexpected user agent header: %q", r.Header.Get("User-Agent")) - } - })) - defer ts.Close() - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - Headers: make(http.Header), - HTTPClient: ts.Client(), - } - - // Set some custom header. - cfg.Headers.Set("My-Custom-Header", "foobar") - cfg.Headers.Set("Terraform-Version", "0.11.9") - - // This one should be overridden! - cfg.Headers.Set("Authorization", "bad-token") - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - ctx := context.Background() - - // Make a few calls so we can check they all send the expected headers. - _, _ = client.Organizations.List(ctx, OrganizationListOptions{}) - _, _ = client.Plans.Logs(ctx, "plan-123456789") - _ = client.Runs.Apply(ctx, "run-123456789", RunApplyOptions{}) - _, _ = client.Workspaces.Lock(ctx, "ws-123456789", WorkspaceLockOptions{}) - _, _ = client.Workspaces.Read(ctx, "organization", "workspace") - - if testedCalls != 6 { - t.Fatalf("expected 6 tested calls, got: %d", testedCalls) - } -} - -func TestClient_userAgent(t *testing.T) { - testedCalls := 0 - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - testedCalls++ - - if testedCalls == 1 { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", "30") - w.WriteHeader(404) // We query the configured base URL which should return a 404. - return - } - - if r.Header.Get("User-Agent") != "hashicorp" { - t.Fatalf("unexpected user agent header: %q", r.Header.Get("User-Agent")) - } - })) - defer ts.Close() - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - Headers: make(http.Header), - HTTPClient: ts.Client(), - } - - // Set a custom user agent. - cfg.Headers.Set("User-Agent", "hashicorp") - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - ctx := context.Background() - - // Make a few calls so we can check they all send the expected headers. - _, _ = client.Organizations.List(ctx, OrganizationListOptions{}) - _, _ = client.Plans.Logs(ctx, "plan-123456789") - _ = client.Runs.Apply(ctx, "run-123456789", RunApplyOptions{}) - _, _ = client.Workspaces.Lock(ctx, "ws-123456789", WorkspaceLockOptions{}) - _, _ = client.Workspaces.Read(ctx, "organization", "workspace") - - if testedCalls != 6 { - t.Fatalf("expected 6 tested calls, got: %d", testedCalls) - } -} - -func TestClient_configureLimiter(t *testing.T) { - rateLimit := "" - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", rateLimit) - w.WriteHeader(404) // We query the configured base URL which should return a 404. - })) - defer ts.Close() - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - HTTPClient: ts.Client(), - } - - cases := map[string]struct { - rate string - limit rate.Limit - burst int - }{ - "no-value": { - rate: "", - limit: rate.Inf, - burst: 0, - }, - "limit-0": { - rate: "0", - limit: rate.Inf, - burst: 0, - }, - "limit-30": { - rate: "30", - limit: rate.Limit(19.8), - burst: 9, - }, - "limit-100": { - rate: "100", - limit: rate.Limit(66), - burst: 33, - }, - } - - for name, tc := range cases { - // First set the test rate limit. - rateLimit = tc.rate - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - if client.limiter.Limit() != tc.limit { - t.Fatalf("test %s expected limit %f, got: %f", name, tc.limit, client.limiter.Limit()) - } - - if client.limiter.Burst() != tc.burst { - t.Fatalf("test %s expected burst %d, got: %d", name, tc.burst, client.limiter.Burst()) - } - } -} - -func TestClient_retryHTTPCheck(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", "30") - w.WriteHeader(404) // We query the configured base URL which should return a 404. - })) - defer ts.Close() - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - HTTPClient: ts.Client(), - } - - connErr := errors.New("connection error") - - cases := map[string]struct { - resp *http.Response - err error - retryServerErrors bool - checkOK bool - checkErr error - }{ - "429-no-server-errors": { - resp: &http.Response{StatusCode: 429}, - err: nil, - checkOK: true, - checkErr: nil, - }, - "429-with-server-errors": { - resp: &http.Response{StatusCode: 429}, - err: nil, - retryServerErrors: true, - checkOK: true, - checkErr: nil, - }, - "500-no-server-errors": { - resp: &http.Response{StatusCode: 500}, - err: nil, - checkOK: false, - checkErr: nil, - }, - "500-with-server-errors": { - resp: &http.Response{StatusCode: 500}, - err: nil, - retryServerErrors: true, - checkOK: true, - checkErr: nil, - }, - "err-no-server-errors": { - err: connErr, - checkOK: false, - checkErr: connErr, - }, - "err-with-server-errors": { - err: connErr, - retryServerErrors: true, - checkOK: true, - checkErr: connErr, - }, - } - - ctx := context.Background() - - for name, tc := range cases { - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - client.RetryServerErrors(tc.retryServerErrors) - - checkOK, checkErr := client.retryHTTPCheck(ctx, tc.resp, tc.err) - if checkOK != tc.checkOK { - t.Fatalf("test %s expected checkOK %t, got: %t", name, tc.checkOK, checkOK) - } - if checkErr != tc.checkErr { - t.Fatalf("test %s expected checkErr %v, got: %v", name, tc.checkErr, checkErr) - } - } -} - -func TestClient_retryHTTPBackoff(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.api+json") - w.Header().Set("X-RateLimit-Limit", "30") - w.WriteHeader(404) // We query the configured base URL which should return a 404. - })) - defer ts.Close() - - var attempts int - retryLogHook := func(attemptNum int, resp *http.Response) { - attempts++ - } - - cfg := &Config{ - Address: ts.URL, - Token: "dummy-token", - HTTPClient: ts.Client(), - RetryLogHook: retryLogHook, - } - - client, err := NewClient(cfg) - if err != nil { - t.Fatal(err) - } - - retries := 3 - resp := &http.Response{StatusCode: 500} - - for i := 0; i < retries; i++ { - client.retryHTTPBackoff(time.Second, time.Second, i, resp) - } - - if attempts != retries { - t.Fatalf("expected %d log hook callbacks, got: %d callbacks", retries, attempts) - } -} - -func setupEnvVars(token, address string) func() { - origToken := os.Getenv("TFE_TOKEN") - origAddress := os.Getenv("TFE_ADDRESS") - - os.Setenv("TFE_TOKEN", token) - os.Setenv("TFE_ADDRESS", address) - - return func() { - os.Setenv("TFE_TOKEN", origToken) - os.Setenv("TFE_ADDRESS", origAddress) - } -} diff --git a/vendor/github.com/hashicorp/go-tfe/user_test.go b/vendor/github.com/hashicorp/go-tfe/user_test.go deleted file mode 100644 index 2fd6c7503..000000000 --- a/vendor/github.com/hashicorp/go-tfe/user_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestUsersReadCurrent(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - u, err := client.Users.ReadCurrent(ctx) - assert.NoError(t, err) - assert.NotEmpty(t, u.ID) - assert.NotEmpty(t, u.AvatarURL) - assert.NotEmpty(t, u.Username) - - t.Run("two factor options are decoded", func(t *testing.T) { - assert.NotNil(t, u.TwoFactor) - }) -} - -func TestUsersUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - uTest, err := client.Users.ReadCurrent(ctx) - require.NoError(t, err) - - // Make sure we reset the current user when were done. - defer func() { - client.Users.Update(ctx, UserUpdateOptions{ - Email: String(uTest.Email), - Username: String(uTest.Username), - }) - }() - - t.Run("without any options", func(t *testing.T) { - _, err := client.Users.Update(ctx, UserUpdateOptions{}) - require.NoError(t, err) - - u, err := client.Users.ReadCurrent(ctx) - assert.NoError(t, err) - assert.Equal(t, u, uTest) - }) - - t.Run("with a new username", func(t *testing.T) { - _, err := client.Users.Update(ctx, UserUpdateOptions{ - Username: String("NewTestUsername"), - }) - require.NoError(t, err) - - u, err := client.Users.ReadCurrent(ctx) - assert.NoError(t, err) - assert.Equal(t, "NewTestUsername", u.Username) - }) - - t.Run("with a new email address", func(t *testing.T) { - _, err := client.Users.Update(ctx, UserUpdateOptions{ - Email: String("newtestemail@hashicorp.com"), - }) - require.NoError(t, err) - - u, err := client.Users.ReadCurrent(ctx) - assert.NoError(t, err) - assert.Equal(t, "newtestemail@hashicorp.com", u.UnconfirmedEmail) - }) - - t.Run("with invalid email address", func(t *testing.T) { - u, err := client.Users.Update(ctx, UserUpdateOptions{ - Email: String("notamailaddress"), - }) - assert.Nil(t, u) - assert.Error(t, err) - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/variable_test.go b/vendor/github.com/hashicorp/go-tfe/variable_test.go deleted file mode 100644 index 3fe85d988..000000000 --- a/vendor/github.com/hashicorp/go-tfe/variable_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestVariablesList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - vTest1, _ := createVariable(t, client, wTest) - vTest2, _ := createVariable(t, client, wTest) - - t.Run("without list options", func(t *testing.T) { - vl, err := client.Variables.List(ctx, VariableListOptions{ - Organization: String(orgTest.Name), - Workspace: String(wTest.Name), - }) - require.NoError(t, err) - assert.Contains(t, vl.Items, vTest1) - assert.Contains(t, vl.Items, vTest2) - - t.Skip("paging not supported yet in API") - assert.Equal(t, 1, vl.CurrentPage) - assert.Equal(t, 2, vl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - t.Skip("paging not supported yet in API") - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - vl, err := client.Variables.List(ctx, VariableListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - Organization: String(orgTest.Name), - Workspace: String(wTest.Name), - }) - require.NoError(t, err) - assert.Empty(t, vl.Items) - assert.Equal(t, 999, vl.CurrentPage) - assert.Equal(t, 2, vl.TotalCount) - }) - - t.Run("when options is missing an organization", func(t *testing.T) { - vl, err := client.Variables.List(ctx, VariableListOptions{ - Workspace: String(wTest.Name), - }) - assert.Nil(t, vl) - assert.EqualError(t, err, "organization is required") - }) - - t.Run("when options is missing an workspace", func(t *testing.T) { - vl, err := client.Variables.List(ctx, VariableListOptions{ - Organization: String(orgTest.Name), - }) - assert.Nil(t, vl) - assert.EqualError(t, err, "workspace is required") - }) -} - -func TestVariablesCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - Workspace: wTest, - } - - v, err := client.Variables.Create(ctx, options) - require.NoError(t, err) - - assert.NotEmpty(t, v.ID) - assert.Equal(t, *options.Key, v.Key) - assert.Equal(t, *options.Value, v.Value) - assert.Equal(t, *options.Category, v.Category) - // The workspace isn't returned correcly by the API. - // assert.Equal(t, *options.Workspace, v.Workspace) - }) - - t.Run("when options has an empty string value", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(""), - Category: Category(CategoryTerraform), - Workspace: wTest, - } - - v, err := client.Variables.Create(ctx, options) - require.NoError(t, err) - - assert.NotEmpty(t, v.ID) - assert.Equal(t, *options.Key, v.Key) - assert.Equal(t, *options.Value, v.Value) - assert.Equal(t, *options.Category, v.Category) - }) - - t.Run("when options is missing value", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(randomString(t)), - Category: Category(CategoryTerraform), - Workspace: wTest, - } - - v, err := client.Variables.Create(ctx, options) - require.NoError(t, err) - - assert.NotEmpty(t, v.ID) - assert.Equal(t, *options.Key, v.Key) - assert.Equal(t, "", v.Value) - assert.Equal(t, *options.Category, v.Category) - }) - - t.Run("when options is missing key", func(t *testing.T) { - options := VariableCreateOptions{ - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - Workspace: wTest, - } - - _, err := client.Variables.Create(ctx, options) - assert.EqualError(t, err, "key is required") - }) - - t.Run("when options has an empty key", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(""), - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - Workspace: wTest, - } - - _, err := client.Variables.Create(ctx, options) - assert.EqualError(t, err, "key is required") - }) - - t.Run("when options is missing category", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Workspace: wTest, - } - - _, err := client.Variables.Create(ctx, options) - assert.EqualError(t, err, "category is required") - }) - - t.Run("when options is missing workspace", func(t *testing.T) { - options := VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - } - - _, err := client.Variables.Create(ctx, options) - assert.EqualError(t, err, "workspace is required") - }) -} - -func TestVariablesRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - vTest, vTestCleanup := createVariable(t, client, nil) - defer vTestCleanup() - - t.Run("when the variable exists", func(t *testing.T) { - v, err := client.Variables.Read(ctx, vTest.ID) - require.NoError(t, err) - assert.Equal(t, vTest.ID, v.ID) - assert.Equal(t, vTest.Category, v.Category) - assert.Equal(t, vTest.HCL, v.HCL) - assert.Equal(t, vTest.Key, v.Key) - assert.Equal(t, vTest.Sensitive, v.Sensitive) - assert.Equal(t, vTest.Value, v.Value) - }) - - t.Run("when the variable does not exist", func(t *testing.T) { - v, err := client.Variables.Read(ctx, "nonexisting") - assert.Nil(t, v) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("without a valid variable ID", func(t *testing.T) { - v, err := client.Variables.Read(ctx, badIdentifier) - assert.Nil(t, v) - assert.EqualError(t, err, "invalid value for variable ID") - }) -} - -func TestVariablesUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - vTest, vTestCleanup := createVariable(t, client, nil) - defer vTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := VariableUpdateOptions{ - Key: String("newname"), - Value: String("newvalue"), - HCL: Bool(true), - } - - v, err := client.Variables.Update(ctx, vTest.ID, options) - require.NoError(t, err) - - assert.Equal(t, *options.Key, v.Key) - assert.Equal(t, *options.HCL, v.HCL) - assert.Equal(t, *options.Value, v.Value) - }) - - t.Run("when updating a subset of values", func(t *testing.T) { - options := VariableUpdateOptions{ - Key: String("someothername"), - HCL: Bool(false), - } - - v, err := client.Variables.Update(ctx, vTest.ID, options) - require.NoError(t, err) - - assert.Equal(t, *options.Key, v.Key) - assert.Equal(t, *options.HCL, v.HCL) - }) - - t.Run("with sensitive set", func(t *testing.T) { - options := VariableUpdateOptions{ - Sensitive: Bool(true), - } - - v, err := client.Variables.Update(ctx, vTest.ID, options) - require.NoError(t, err) - - assert.Equal(t, *options.Sensitive, v.Sensitive) - assert.Empty(t, v.Value) // Because its now sensitive - }) - - t.Run("without any changes", func(t *testing.T) { - vTest, vTestCleanup := createVariable(t, client, nil) - defer vTestCleanup() - - v, err := client.Variables.Update(ctx, vTest.ID, VariableUpdateOptions{}) - require.NoError(t, err) - - assert.Equal(t, vTest, v) - }) - - t.Run("with invalid variable ID", func(t *testing.T) { - _, err := client.Variables.Update(ctx, badIdentifier, VariableUpdateOptions{}) - assert.EqualError(t, err, "invalid value for variable ID") - }) -} - -func TestVariablesDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - wTest, wTestCleanup := createWorkspace(t, client, nil) - defer wTestCleanup() - - vTest, _ := createVariable(t, client, wTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.Variables.Delete(ctx, vTest.ID) - assert.NoError(t, err) - }) - - t.Run("with non existing variable ID", func(t *testing.T) { - err := client.Variables.Delete(ctx, "nonexisting") - assert.Equal(t, err, ErrResourceNotFound) - }) - - t.Run("with invalid variable ID", func(t *testing.T) { - err := client.Variables.Delete(ctx, badIdentifier) - assert.EqualError(t, err, "invalid value for variable ID") - }) -} diff --git a/vendor/github.com/hashicorp/go-tfe/workspace_test.go b/vendor/github.com/hashicorp/go-tfe/workspace_test.go deleted file mode 100644 index fc853ecac..000000000 --- a/vendor/github.com/hashicorp/go-tfe/workspace_test.go +++ /dev/null @@ -1,496 +0,0 @@ -package tfe - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestWorkspacesList(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest1, wTest1Cleanup := createWorkspace(t, client, orgTest) - defer wTest1Cleanup() - wTest2, wTest2Cleanup := createWorkspace(t, client, orgTest) - defer wTest2Cleanup() - - t.Run("without list options", func(t *testing.T) { - wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{}) - require.NoError(t, err) - assert.Contains(t, wl.Items, wTest1) - assert.Contains(t, wl.Items, wTest2) - assert.Equal(t, 1, wl.CurrentPage) - assert.Equal(t, 2, wl.TotalCount) - }) - - t.Run("with list options", func(t *testing.T) { - // Request a page number which is out of range. The result should - // be successful, but return no results if the paging options are - // properly passed along. - wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ - ListOptions: ListOptions{ - PageNumber: 999, - PageSize: 100, - }, - }) - require.NoError(t, err) - assert.Empty(t, wl.Items) - assert.Equal(t, 999, wl.CurrentPage) - assert.Equal(t, 2, wl.TotalCount) - }) - - t.Run("when searching a known workspace", func(t *testing.T) { - // Use a known workspace prefix as search attribute. The result - // should be successful and only contain the matching workspace. - wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ - Search: String(wTest1.Name[:len(wTest1.Name)-5]), - }) - require.NoError(t, err) - assert.Contains(t, wl.Items, wTest1) - assert.NotContains(t, wl.Items, wTest2) - assert.Equal(t, 1, wl.CurrentPage) - assert.Equal(t, 1, wl.TotalCount) - }) - - t.Run("when searching an unknown workspace", func(t *testing.T) { - // Use a nonexisting workspace name as search attribute. The result - // should be successful, but return no results. - wl, err := client.Workspaces.List(ctx, orgTest.Name, WorkspaceListOptions{ - Search: String("nonexisting"), - }) - require.NoError(t, err) - assert.Empty(t, wl.Items) - assert.Equal(t, 1, wl.CurrentPage) - assert.Equal(t, 0, wl.TotalCount) - }) - - t.Run("without a valid organization", func(t *testing.T) { - wl, err := client.Workspaces.List(ctx, badIdentifier, WorkspaceListOptions{}) - assert.Nil(t, wl) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestWorkspacesCreate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - options := WorkspaceCreateOptions{ - Name: String("foo"), - AutoApply: Bool(true), - QueueAllRuns: Bool(true), - TerraformVersion: String("0.11.0"), - WorkingDirectory: String("bar/"), - } - - w, err := client.Workspaces.Create(ctx, orgTest.Name, options) - require.NoError(t, err) - - // Get a refreshed view from the API. - refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) - require.NoError(t, err) - - for _, item := range []*Workspace{ - w, - refreshed, - } { - assert.NotEmpty(t, item.ID) - assert.Equal(t, *options.Name, item.Name) - assert.Equal(t, *options.AutoApply, item.AutoApply) - assert.Equal(t, *options.QueueAllRuns, item.QueueAllRuns) - assert.Equal(t, *options.TerraformVersion, item.TerraformVersion) - assert.Equal(t, *options.WorkingDirectory, item.WorkingDirectory) - } - }) - - t.Run("when options is missing name", func(t *testing.T) { - w, err := client.Workspaces.Create(ctx, "foo", WorkspaceCreateOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "name is required") - }) - - t.Run("when options has an invalid name", func(t *testing.T) { - w, err := client.Workspaces.Create(ctx, "foo", WorkspaceCreateOptions{ - Name: String(badIdentifier), - }) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for name") - }) - - t.Run("when options has an invalid organization", func(t *testing.T) { - w, err := client.Workspaces.Create(ctx, badIdentifier, WorkspaceCreateOptions{ - Name: String("foo"), - }) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when an error is returned from the api", func(t *testing.T) { - w, err := client.Workspaces.Create(ctx, "bar", WorkspaceCreateOptions{ - Name: String("bar"), - TerraformVersion: String("nonexisting"), - }) - assert.Nil(t, w) - assert.Error(t, err) - }) -} - -func TestWorkspacesRead(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, wTestCleanup := createWorkspace(t, client, orgTest) - defer wTestCleanup() - - t.Run("when the workspace exists", func(t *testing.T) { - w, err := client.Workspaces.Read(ctx, orgTest.Name, wTest.Name) - require.NoError(t, err) - assert.Equal(t, wTest, w) - - t.Run("permissions are properly decoded", func(t *testing.T) { - assert.True(t, w.Permissions.CanDestroy) - }) - - t.Run("relationships are properly decoded", func(t *testing.T) { - assert.Equal(t, orgTest.Name, w.Organization.Name) - }) - - t.Run("timestamps are properly decoded", func(t *testing.T) { - assert.NotEmpty(t, w.CreatedAt) - }) - }) - - t.Run("when the workspace does not exist", func(t *testing.T) { - w, err := client.Workspaces.Read(ctx, orgTest.Name, "nonexisting") - assert.Nil(t, w) - assert.Error(t, err) - }) - - t.Run("when the organization does not exist", func(t *testing.T) { - w, err := client.Workspaces.Read(ctx, "nonexisting", "nonexisting") - assert.Nil(t, w) - assert.Error(t, err) - }) - - t.Run("without a valid organization", func(t *testing.T) { - w, err := client.Workspaces.Read(ctx, badIdentifier, wTest.Name) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("without a valid workspace", func(t *testing.T) { - w, err := client.Workspaces.Read(ctx, orgTest.Name, badIdentifier) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace") - }) -} - -func TestWorkspacesUpdate(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - t.Run("when updating a subset of values", func(t *testing.T) { - options := WorkspaceUpdateOptions{ - Name: String(wTest.Name), - AutoApply: Bool(true), - QueueAllRuns: Bool(true), - TerraformVersion: String("0.10.0"), - } - - wAfter, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) - require.NoError(t, err) - - assert.Equal(t, wTest.Name, wAfter.Name) - assert.NotEqual(t, wTest.AutoApply, wAfter.AutoApply) - assert.NotEqual(t, wTest.QueueAllRuns, wAfter.QueueAllRuns) - assert.NotEqual(t, wTest.TerraformVersion, wAfter.TerraformVersion) - assert.Equal(t, wTest.WorkingDirectory, wAfter.WorkingDirectory) - }) - - t.Run("with valid options", func(t *testing.T) { - options := WorkspaceUpdateOptions{ - Name: String(randomString(t)), - AutoApply: Bool(false), - QueueAllRuns: Bool(false), - TerraformVersion: String("0.11.1"), - WorkingDirectory: String("baz/"), - } - - w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) - require.NoError(t, err) - - // Get a refreshed view of the workspace from the API - refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) - require.NoError(t, err) - - for _, item := range []*Workspace{ - w, - refreshed, - } { - assert.Equal(t, *options.Name, item.Name) - assert.Equal(t, *options.AutoApply, item.AutoApply) - assert.Equal(t, *options.QueueAllRuns, item.QueueAllRuns) - assert.Equal(t, *options.TerraformVersion, item.TerraformVersion) - assert.Equal(t, *options.WorkingDirectory, item.WorkingDirectory) - } - }) - - t.Run("when an error is returned from the api", func(t *testing.T) { - w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, WorkspaceUpdateOptions{ - TerraformVersion: String("nonexisting"), - }) - assert.Nil(t, w) - assert.Error(t, err) - }) - - t.Run("when options has an invalid name", func(t *testing.T) { - w, err := client.Workspaces.Update(ctx, orgTest.Name, badIdentifier, WorkspaceUpdateOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace") - }) - - t.Run("when options has an invalid organization", func(t *testing.T) { - w, err := client.Workspaces.Update(ctx, badIdentifier, wTest.Name, WorkspaceUpdateOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for organization") - }) -} - -func TestWorkspacesDelete(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - err := client.Workspaces.Delete(ctx, orgTest.Name, wTest.Name) - require.NoError(t, err) - - // Try loading the workspace - it should fail. - _, err = client.Workspaces.Read(ctx, orgTest.Name, wTest.Name) - assert.Equal(t, ErrResourceNotFound, err) - }) - - t.Run("when organization is invalid", func(t *testing.T) { - err := client.Workspaces.Delete(ctx, badIdentifier, wTest.Name) - assert.EqualError(t, err, "invalid value for organization") - }) - - t.Run("when workspace is invalid", func(t *testing.T) { - err := client.Workspaces.Delete(ctx, orgTest.Name, badIdentifier) - assert.EqualError(t, err, "invalid value for workspace") - }) -} - -func TestWorkspacesRemoveVCSConnection(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspaceWithVCS(t, client, orgTest) - - t.Run("remove vcs integration", func(t *testing.T) { - w, err := client.Workspaces.RemoveVCSConnection(ctx, orgTest.Name, wTest.Name) - require.NoError(t, err) - assert.Equal(t, (*VCSRepo)(nil), w.VCSRepo) - }) -} - -func TestWorkspacesLock(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - t.Run("with valid options", func(t *testing.T) { - w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - require.NoError(t, err) - assert.True(t, w.Locked) - }) - - t.Run("when workspace is already locked", func(t *testing.T) { - _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - assert.Equal(t, ErrWorkspaceLocked, err) - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - w, err := client.Workspaces.Lock(ctx, badIdentifier, WorkspaceLockOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestWorkspacesUnlock(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - if err != nil { - orgTestCleanup() - } - require.NoError(t, err) - require.True(t, w.Locked) - - t.Run("with valid options", func(t *testing.T) { - w, err := client.Workspaces.Unlock(ctx, wTest.ID) - require.NoError(t, err) - assert.False(t, w.Locked) - }) - - t.Run("when workspace is already unlocked", func(t *testing.T) { - _, err := client.Workspaces.Unlock(ctx, wTest.ID) - assert.Equal(t, ErrWorkspaceNotLocked, err) - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - w, err := client.Workspaces.Unlock(ctx, badIdentifier) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestWorkspacesForceUnlock(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, _ := createWorkspace(t, client, orgTest) - - w, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) - if err != nil { - orgTestCleanup() - } - require.NoError(t, err) - require.True(t, w.Locked) - - t.Run("with valid options", func(t *testing.T) { - w, err := client.Workspaces.ForceUnlock(ctx, wTest.ID) - require.NoError(t, err) - assert.False(t, w.Locked) - }) - - t.Run("when workspace is already unlocked", func(t *testing.T) { - _, err := client.Workspaces.ForceUnlock(ctx, wTest.ID) - assert.Equal(t, ErrWorkspaceNotLocked, err) - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - w, err := client.Workspaces.ForceUnlock(ctx, badIdentifier) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestWorkspacesAssignSSHKey(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, wTestCleanup := createWorkspace(t, client, orgTest) - defer wTestCleanup() - - sshKeyTest, sshKeyTestCleanup := createSSHKey(t, client, orgTest) - defer sshKeyTestCleanup() - - t.Run("with valid options", func(t *testing.T) { - w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ - SSHKeyID: String(sshKeyTest.ID), - }) - require.NoError(t, err) - require.NotNil(t, w.SSHKey) - assert.Equal(t, w.SSHKey.ID, sshKeyTest.ID) - }) - - t.Run("without an SSH key ID", func(t *testing.T) { - w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{}) - assert.Nil(t, w) - assert.EqualError(t, err, "SSH key ID is required") - }) - - t.Run("without a valid SSH key ID", func(t *testing.T) { - w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ - SSHKeyID: String(badIdentifier), - }) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for SSH key ID") - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - w, err := client.Workspaces.AssignSSHKey(ctx, badIdentifier, WorkspaceAssignSSHKeyOptions{ - SSHKeyID: String(sshKeyTest.ID), - }) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} - -func TestWorkspacesUnassignSSHKey(t *testing.T) { - client := testClient(t) - ctx := context.Background() - - orgTest, orgTestCleanup := createOrganization(t, client) - defer orgTestCleanup() - - wTest, wTestCleanup := createWorkspace(t, client, orgTest) - defer wTestCleanup() - - sshKeyTest, sshKeyTestCleanup := createSSHKey(t, client, orgTest) - defer sshKeyTestCleanup() - - w, err := client.Workspaces.AssignSSHKey(ctx, wTest.ID, WorkspaceAssignSSHKeyOptions{ - SSHKeyID: String(sshKeyTest.ID), - }) - if err != nil { - orgTestCleanup() - } - require.NoError(t, err) - require.NotNil(t, w.SSHKey) - require.Equal(t, w.SSHKey.ID, sshKeyTest.ID) - - t.Run("with valid options", func(t *testing.T) { - w, err := client.Workspaces.UnassignSSHKey(ctx, wTest.ID) - assert.Nil(t, err) - assert.Nil(t, w.SSHKey) - }) - - t.Run("without a valid workspace ID", func(t *testing.T) { - w, err := client.Workspaces.UnassignSSHKey(ctx, badIdentifier) - assert.Nil(t, w) - assert.EqualError(t, err, "invalid value for workspace ID") - }) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e27ec1637..7f760f940 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -290,7 +290,7 @@ github.com/hashicorp/go-rootcerts github.com/hashicorp/go-safetemp # github.com/hashicorp/go-slug v0.3.0 github.com/hashicorp/go-slug -# github.com/hashicorp/go-tfe v0.3.14 +# github.com/hashicorp/go-tfe v0.3.16 github.com/hashicorp/go-tfe # github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/go-uuid From 7cf744241a048c24956bf930ce7e40194cfa5cb1 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Thu, 25 Apr 2019 09:51:52 +0200 Subject: [PATCH 6/9] Do not use a scanner to read the logs Using a scanner can cause issues when reading long lines. Also make sure we return the error correctly while planning. --- backend/remote/backend_common.go | 38 ++++++++++++++++++++++---------- backend/remote/backend_plan.go | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index d53b0a791..836ce85b3 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -228,37 +228,51 @@ func (b *Remote) parseVariableValues(op *backend.Operation) (terraform.InputValu } func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { + if r.CostEstimation != nil { + return nil + } + if b.CLI != nil { b.CLI.Output("\n------------------------------------------------------------------------\n") } - var ce = r.CostEstimation - logs, err := b.client.CostEstimations.Logs(stopCtx, ce.ID) + logs, err := b.client.CostEstimations.Logs(stopCtx, r.CostEstimation.ID) if err != nil { return generalError("Failed to retrieve cost estimation logs", err) } - scanner := bufio.NewScanner(logs) + reader := bufio.NewReaderSize(logs, 64*1024) // Retrieve the cost estimation to get its current status. - ce, err = b.client.CostEstimations.Read(stopCtx, ce.ID) + ce, err := b.client.CostEstimations.Read(stopCtx, r.CostEstimation.ID) if err != nil { return generalError("Failed to retrieve cost estimation", err) } - var msgPrefix = "Cost estimation" - + msgPrefix := "Cost estimation" if b.CLI != nil { b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) } - for scanner.Scan() { - if b.CLI != nil { - b.CLI.Output(b.Colorize().Color(scanner.Text())) + if b.CLI != nil { + for next := true; next; { + var l, line []byte + + for isPrefix := true; isPrefix; { + l, isPrefix, err = reader.ReadLine() + if err != nil { + if err != io.EOF { + return generalError("Failed to read logs", err) + } + next = false + } + line = append(line, l...) + } + + if next || len(line) > 0 { + b.CLI.Output(b.Colorize().Color(string(line))) + } } } - if err := scanner.Err(); err != nil { - return generalError("Failed to read logs", err) - } switch ce.Status { case tfe.CostEstimationFinished: diff --git a/backend/remote/backend_plan.go b/backend/remote/backend_plan.go index 662e28ef3..72508c639 100644 --- a/backend/remote/backend_plan.go +++ b/backend/remote/backend_plan.go @@ -294,7 +294,7 @@ func (b *Remote) plan(stopCtx, cancelCtx context.Context, op *backend.Operation, if r.CostEstimation != nil { err = b.costEstimation(stopCtx, cancelCtx, op, r) if err != nil { - generalError("Cost Estimation error", err) + return r, err } } From bb12206bcaa2a8751c9611dbdcc6a2e769adfee0 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Thu, 25 Apr 2019 09:53:12 +0200 Subject: [PATCH 7/9] Fixup the tests --- backend/remote/backend_common.go | 2 +- backend/remote/backend_mock.go | 37 ++----------------- backend/remote/backend_plan.go | 2 +- backend/remote/backend_plan_test.go | 34 +++++++++-------- .../ce.log | 2 +- .../main.tf | 0 .../plan.log | 0 7 files changed, 25 insertions(+), 52 deletions(-) rename backend/remote/test-fixtures/{cost-estimation => plan-cost-estimation}/ce.log (83%) rename backend/remote/test-fixtures/{cost-estimation => plan-cost-estimation}/main.tf (100%) rename backend/remote/test-fixtures/{cost-estimation => plan-cost-estimation}/plan.log (100%) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 836ce85b3..34c851467 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -228,7 +228,7 @@ func (b *Remote) parseVariableValues(op *backend.Operation) (terraform.InputValu } func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { - if r.CostEstimation != nil { + if r.CostEstimation == nil { return nil } diff --git a/backend/remote/backend_mock.go b/backend/remote/backend_mock.go index 59a1a1393..31b63131b 100644 --- a/backend/remote/backend_mock.go +++ b/backend/remote/backend_mock.go @@ -228,8 +228,8 @@ func newMockCostEstimations(client *mockClient) *mockCostEstimations { } } -// create is a helper function to create a mock plan that uses the configured -// working directory to find the logfile. +// create is a helper function to create a mock cost estimation that uses the +// configured working directory to find the logfile. func (m *mockCostEstimations) create(cvID, workspaceID string) (*tfe.CostEstimation, error) { id := generateID("ce-") @@ -264,28 +264,6 @@ func (m *mockCostEstimations) Read(ctx context.Context, costEstimationID string) if !ok { return nil, tfe.ErrResourceNotFound } - - logfile, ok := m.logs[ce.ID] - if !ok { - return nil, tfe.ErrResourceNotFound - } - - if _, err := os.Stat(logfile); os.IsNotExist(err) { - return nil, fmt.Errorf("logfile does not exist") - } - - logs, err := ioutil.ReadFile(logfile) - if err != nil { - return nil, err - } - - if bytes.Contains(logs, []byte("SKU")) { - ce.Status = tfe.CostEstimationFinished - } else { - // As this is an unexpected state, we say the estimation errored. - ce.Status = tfe.CostEstimationErrored - } - return ce, nil } @@ -309,12 +287,7 @@ func (m *mockCostEstimations) Logs(ctx context.Context, costEstimationID string) return nil, err } - if bytes.Contains(logs, []byte("SKU")) { - ce.Status = tfe.CostEstimationFinished - } else { - // As this is an unexpected state, we say the estimation errored. - ce.Status = tfe.CostEstimationErrored - } + ce.Status = tfe.CostEstimationFinished return bytes.NewBuffer(logs), nil } @@ -1151,10 +1124,6 @@ func (m *mockWorkspaces) UnassignSSHKey(ctx context.Context, workspaceID string) panic("not implemented") } -func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organizationID string, workspaceID string) (*tfe.Workspace, error) { - panic("not implemented") -} - const alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func generateID(s string) string { diff --git a/backend/remote/backend_plan.go b/backend/remote/backend_plan.go index 72508c639..db5495b16 100644 --- a/backend/remote/backend_plan.go +++ b/backend/remote/backend_plan.go @@ -290,7 +290,7 @@ func (b *Remote) plan(stopCtx, cancelCtx context.Context, op *backend.Operation, return r, nil } - // Show Cost Estimation + // Show any cost estimation output. if r.CostEstimation != nil { err = b.costEstimation(stopCtx, cancelCtx, op, r) if err != nil { diff --git a/backend/remote/backend_plan_test.go b/backend/remote/backend_plan_test.go index d89ef8685..acf3c4abb 100644 --- a/backend/remote/backend_plan_test.go +++ b/backend/remote/backend_plan_test.go @@ -655,10 +655,11 @@ func TestRemote_planWithWorkingDirectory(t *testing.T) { } } -func TestRemote_costEstimationFinish(t *testing.T) { - b := testBackendDefault(t) +func TestRemote_costEstimation(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() - op, configCleanup := testOperationPlan(t, "./test-fixtures/cost-estimation") + op, configCleanup := testOperationPlan(t, "./test-fixtures/plan-cost-estimation") defer configCleanup() op.Workspace = backend.DefaultStateName @@ -672,17 +673,20 @@ func TestRemote_costEstimationFinish(t *testing.T) { if run.Result != backend.OperationSuccess { t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) } + if run.PlanEmpty { + t.Fatalf("expected a non-empty plan") + } output := b.CLI.(*cli.MockUi).OutputWriter.String() if !strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("expected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summary in output: %s", output) - } if !strings.Contains(output, "SKU") { t.Fatalf("expected cost estimation result in output: %s", output) } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summary in output: %s", output) + } } func TestRemote_planPolicyPass(t *testing.T) { @@ -711,12 +715,12 @@ func TestRemote_planPolicyPass(t *testing.T) { if !strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("expected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) - } if !strings.Contains(output, "Sentinel Result: true") { t.Fatalf("expected policy check result in output: %s", output) } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summery in output: %s", output) + } } func TestRemote_planPolicyHardFail(t *testing.T) { @@ -750,12 +754,12 @@ func TestRemote_planPolicyHardFail(t *testing.T) { if !strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("expected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) - } if !strings.Contains(output, "Sentinel Result: false") { t.Fatalf("expected policy check result in output: %s", output) } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summery in output: %s", output) + } } func TestRemote_planPolicySoftFail(t *testing.T) { @@ -789,12 +793,12 @@ func TestRemote_planPolicySoftFail(t *testing.T) { if !strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("expected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) - } if !strings.Contains(output, "Sentinel Result: false") { t.Fatalf("expected policy check result in output: %s", output) } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summery in output: %s", output) + } } func TestRemote_planWithRemoteError(t *testing.T) { diff --git a/backend/remote/test-fixtures/cost-estimation/ce.log b/backend/remote/test-fixtures/plan-cost-estimation/ce.log similarity index 83% rename from backend/remote/test-fixtures/cost-estimation/ce.log rename to backend/remote/test-fixtures/plan-cost-estimation/ce.log index d73c8915b..e51fef1ed 100644 --- a/backend/remote/test-fixtures/cost-estimation/ce.log +++ b/backend/remote/test-fixtures/plan-cost-estimation/ce.log @@ -3,4 +3,4 @@ +---------+------+-----+-------------+----------------------+ +---------+------+-----+-------------+----------------------+ | TOTAL | $0.000 USD / 720 HRS | -+---------+------+-----+-------------+----------------------+ \ No newline at end of file ++---------+------+-----+-------------+----------------------+ diff --git a/backend/remote/test-fixtures/cost-estimation/main.tf b/backend/remote/test-fixtures/plan-cost-estimation/main.tf similarity index 100% rename from backend/remote/test-fixtures/cost-estimation/main.tf rename to backend/remote/test-fixtures/plan-cost-estimation/main.tf diff --git a/backend/remote/test-fixtures/cost-estimation/plan.log b/backend/remote/test-fixtures/plan-cost-estimation/plan.log similarity index 100% rename from backend/remote/test-fixtures/cost-estimation/plan.log rename to backend/remote/test-fixtures/plan-cost-estimation/plan.log From 0e27a8862fcc4a8306ffbaf9a0a3abc6e23bd98e Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Thu, 25 Apr 2019 10:52:19 -0700 Subject: [PATCH 8/9] remove duplicate remote output line Signed-off-by: Paul Thrasher --- backend/remote/backend_common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 34c851467..2f9399323 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -276,7 +276,7 @@ func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend. switch ce.Status { case tfe.CostEstimationFinished: - if r.HasChanges && op.Type == backend.OperationTypeApply || b.CLI != nil { + if len(r.PolicyChecks) == 0 && r.HasChanges && op.Type == backend.OperationTypeApply && b.CLI != nil { b.CLI.Output("\n------------------------------------------------------------------------") } return nil From 151c91ffdae99e14d45883df5e4ce2f84cfc6e17 Mon Sep 17 00:00:00 2001 From: Paul Thrasher Date: Thu, 25 Apr 2019 11:17:08 -0700 Subject: [PATCH 9/9] use scanner for reading logs Signed-off-by: Paul Thrasher --- backend/remote/backend_common.go | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/backend/remote/backend_common.go b/backend/remote/backend_common.go index 2f9399323..15f59ac47 100644 --- a/backend/remote/backend_common.go +++ b/backend/remote/backend_common.go @@ -240,7 +240,7 @@ func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend. if err != nil { return generalError("Failed to retrieve cost estimation logs", err) } - reader := bufio.NewReaderSize(logs, 64*1024) + scanner := bufio.NewScanner(logs) // Retrieve the cost estimation to get its current status. ce, err := b.client.CostEstimations.Read(stopCtx, r.CostEstimation.ID) @@ -253,27 +253,16 @@ func (b *Remote) costEstimation(stopCtx, cancelCtx context.Context, op *backend. b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) } - if b.CLI != nil { - for next := true; next; { - var l, line []byte - - for isPrefix := true; isPrefix; { - l, isPrefix, err = reader.ReadLine() - if err != nil { - if err != io.EOF { - return generalError("Failed to read logs", err) - } - next = false - } - line = append(line, l...) - } - - if next || len(line) > 0 { - b.CLI.Output(b.Colorize().Color(string(line))) - } + for scanner.Scan() { + if b.CLI != nil { + b.CLI.Output(b.Colorize().Color(scanner.Text())) } } + if err := scanner.Err(); err != nil { + return generalError("Failed to read logs", err) + } + switch ce.Status { case tfe.CostEstimationFinished: if len(r.PolicyChecks) == 0 && r.HasChanges && op.Type == backend.OperationTypeApply && b.CLI != nil {