From 3dd8ec403521ccf303e7cfbb49fdffd61f5bf3f8 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 15:12:42 -0700 Subject: [PATCH 01/19] draft golangci-lint workflow --- .github/workflows/golangci-lint.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 000000000..af5f40570 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,18 @@ +name: golangci-lint +on: + push: + branches: + - main + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci/golangci-lint-action@v2 + with: + version: 1.42.1 + args: --timeout=5m + only-new-issues: true + \ No newline at end of file From 222fe1520a2e02c2355b73b470e5bf726fe81ab1 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 15:44:36 -0700 Subject: [PATCH 02/19] fix gh action error that says every step must define a or key --- .github/workflows/golangci-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index af5f40570..d22b1d757 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: golangci/golangci-lint-action@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 with: version: 1.42.1 args: --timeout=5m only-new-issues: true - \ No newline at end of file From e8279f2fca57474b7e705a61328b89a4427be5c9 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 15:46:00 -0700 Subject: [PATCH 03/19] 1.42.1 is an invalid version --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index d22b1d757..e01987491 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -13,6 +13,6 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: 1.42.1 + version: latest args: --timeout=5m only-new-issues: true From 6bfaa45de92d3f8fe8aba3343f9f27929a6e05ea Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 15:47:58 -0700 Subject: [PATCH 04/19] temporarily commenting the option only-new-issues to correct old issues --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index e01987491..c60094340 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -15,4 +15,4 @@ jobs: with: version: latest args: --timeout=5m - only-new-issues: true + #only-new-issues: true From e14e0681bee0e7385482902b2144785f9f5317f6 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 16:04:27 -0700 Subject: [PATCH 05/19] apply golangci-lint recommendations: remove unused code which is everything that was inside helper_test.go and remove unnecesary use of formatting --- admin_user.go | 2 +- helper_test.go | 1039 ------------------------------------------------ 2 files changed, 1 insertion(+), 1040 deletions(-) delete mode 100644 helper_test.go diff --git a/admin_user.go b/admin_user.go index b0500a71b..75009ce6e 100644 --- a/admin_user.go +++ b/admin_user.go @@ -85,7 +85,7 @@ type AdminUserListOptions struct { // List all user accounts in the Terraform Enterprise installation func (a *adminUsers) List(ctx context.Context, options AdminUserListOptions) (*AdminUserList, error) { - u := fmt.Sprintf("admin/users") + u := "admin/users" req, err := a.client.newRequest("GET", u, &options) if err != nil { return nil, err diff --git a/helper_test.go b/helper_test.go deleted file mode 100644 index ebff5d2cf..000000000 --- a/helper_test.go +++ /dev/null @@ -1,1039 +0,0 @@ -package tfe - -import ( - "context" - "crypto/hmac" - "crypto/md5" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "fmt" - "io/ioutil" - "os" - "testing" - "time" - - "github.com/hashicorp/go-uuid" -) - -const badIdentifier = "! / nope" - -// Memoize test account details -var _testAccountDetails *TestAccountDetails - -func testClient(t *testing.T) *Client { - client, err := NewClient(nil) - if err != nil { - t.Fatal(err) - } - - return client -} - -func fetchTestAccountDetails(t *testing.T, client *Client) *TestAccountDetails { - if _testAccountDetails == nil { - _testAccountDetails = FetchTestAccountDetails(t, client) - } - return _testAccountDetails -} - -func createAgentPool(t *testing.T, client *Client, org *Organization) (*AgentPool, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - pool, err := client.AgentPools.Create(ctx, org.Name, AgentPoolCreateOptions{ - Name: String(randomString(t)), - }) - if err != nil { - t.Fatal(err) - } - - return pool, func() { - if err := client.AgentPools.Delete(ctx, pool.ID); err != nil { - t.Errorf("Error destroying agent pool! WARNING: Dangling resources "+ - "may exist! The full error is shown below.\n\n"+ - "Agent pool ID: %s\nError: %s", pool.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createAgentToken(t *testing.T, client *Client, ap *AgentPool) (*AgentToken, func()) { - var apCleanup func() - - if ap == nil { - ap, apCleanup = createAgentPool(t, client, nil) - } - - ctx := context.Background() - at, err := client.AgentTokens.Generate(ctx, ap.ID, AgentTokenGenerateOptions{ - Description: String(randomString(t)), - }) - if err != nil { - t.Fatal(err) - } - - return at, func() { - if err := client.AgentTokens.Delete(ctx, at.ID); err != nil { - t.Errorf("Error destroying agent token! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "AgentToken: %s\nError: %s", at.ID, err) - } - - if apCleanup != nil { - apCleanup() - } - } -} - -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, options *NotificationConfigurationCreateOptions) (*NotificationConfiguration, func()) { - var wCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - if options == nil { - options = &NotificationConfigurationCreateOptions{ - DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), - Enabled: Bool(false), - Name: String(randomString(t)), - Token: String(randomString(t)), - URL: String("http://example.com"), - Triggers: []string{NotificationTriggerCreated}, - } - } - - ctx := context.Background() - nc, err := client.NotificationConfigurations.Create( - ctx, - w.ID, - *options, - ) - 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 createPolicySetParameter(t *testing.T, client *Client, ps *PolicySet) (*PolicySetParameter, func()) { - var psCleanup func() - - if ps == nil { - ps, psCleanup = createPolicySet(t, client, nil, nil, nil) - } - - ctx := context.Background() - v, err := client.PolicySetParameters.Create(ctx, ps.ID, PolicySetParameterCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Category: Category(CategoryPolicySet), - }) - if err != nil { - t.Fatal(err) - } - - return v, func() { - if err := client.PolicySetParameters.Delete(ctx, ps.ID, v.ID); err != nil { - t.Errorf("Error destroying variable! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Parameter: %s\nError: %s", v.Key, err) - } - - if psCleanup != nil { - psCleanup() - } - } -} - -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 createPolicySetVersion(t *testing.T, client *Client, ps *PolicySet) (*PolicySetVersion, func()) { - var psCleanup func() - - if ps == nil { - ps, psCleanup = createPolicySet(t, client, nil, nil, nil) - } - - ctx := context.Background() - psv, err := client.PolicySetVersions.Create(ctx, ps.ID) - if err != nil { - t.Fatal(err) - } - - return psv, func() { - // Deleting a Policy Set Version is done through deleting a Policy Set. - if psCleanup != nil { - psCleanup() - } - } -} - -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.Skip("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 createOrganizationMembership(t *testing.T, client *Client, org *Organization) (*OrganizationMembership, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - mem, err := client.OrganizationMemberships.Create(ctx, org.Name, OrganizationMembershipCreateOptions{ - Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), - }) - if err != nil { - t.Fatal(err) - } - - return mem, func() { - if err := client.OrganizationMemberships.Delete(ctx, mem.ID); err != nil { - t.Errorf("Error destroying membership! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Membership: %s\nError: %s", mem.ID, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -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 createRunTrigger(t *testing.T, client *Client, w *Workspace, sourceable *Workspace) (*RunTrigger, func()) { - var wCleanup func() - var sourceableCleanup func() - - if w == nil { - w, wCleanup = createWorkspace(t, client, nil) - } - - if sourceable == nil { - sourceable, sourceableCleanup = createWorkspace(t, client, nil) - } - - ctx := context.Background() - rt, err := client.RunTriggers.Create( - ctx, - w.ID, - RunTriggerCreateOptions{ - Sourceable: sourceable, - }, - ) - if err != nil { - t.Fatal(err) - } - - return rt, func() { - if err := client.RunTriggers.Delete(ctx, rt.ID); err != nil { - t.Errorf("Error destroying run trigger! WARNING: Dangling\n"+ - "resources may exist! The full error is shown below.\n\n"+ - "RunTrigger: %s\nError: %s", rt.ID, err) - } - - if wCleanup != nil { - wCleanup() - } - - if sourceableCleanup != nil { - sourceableCleanup() - } - } -} - -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() { - cvCleanup() - - if wCleanup != nil { - wCleanup() - } - } -} - -func createRunWithStatus(t *testing.T, client *Client, w *Workspace, timeout int, desiredStatuses ...RunStatus) (*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) - } - - for _, desiredStatus := range desiredStatuses { - // if we're creating an applied run, we need to manually confirm the apply once the plan finishes - isApplyable := hasApplyableStatus(r) - if desiredStatus == RunApplied && isApplyable { - err := client.Runs.Apply(ctx, r.ID, RunApplyOptions{}) - if err != nil { - t.Fatal(err) - } - } else if desiredStatus == r.Status { - return r, rCleanup - } - } - - if i > timeout { - runStatus := r.Status - rCleanup() - t.Fatal(fmt.Printf("Timeout waiting for run to reach status %v, had status %s", desiredStatuses, runStatus)) - } - - time.Sleep(1 * time.Second) - } -} - -func createPlannedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - if paidFeaturesDisabled() { - return createRunWithStatus(t, client, w, 45, RunPlanned) - } else { - return createRunWithStatus(t, client, w, 45, RunCostEstimated) - } -} - -func createCostEstimatedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - return createRunWithStatus(t, client, w, 45, RunCostEstimated) -} - -func createPolicyCheckedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - return createRunWithStatus(t, client, w, 45, RunPolicyChecked, RunPolicyOverride) -} - -func createAppliedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { - return createRunWithStatus(t, client, w, 90, RunApplied) -} - -func hasApplyableStatus(r *Run) bool { - if len(r.PolicyChecks) > 0 { - return r.Status == RunPolicyChecked || r.Status == RunPolicyOverride - } else if r.CostEstimate != nil { - return r.Status == RunCostEstimated - } else { - return r.Status == RunPlanned - } -} - -func createPlanExport(t *testing.T, client *Client, r *Run) (*PlanExport, func()) { - var rCleanup func() - - if r == nil { - r, rCleanup = createPlannedRun(t, client, nil) - } - - ctx := context.Background() - pe, err := client.PlanExports.Create(ctx, PlanExportCreateOptions{ - Plan: r.Plan, - DataType: PlanExportType(PlanExportSentinelMockBundleV0), - }) - if err != nil { - t.Fatal(err) - } - - for i := 0; ; i++ { - pe, err := client.PlanExports.Read(ctx, pe.ID) - if err != nil { - t.Fatal(err) - } - - if pe.Status == PlanExportFinished { - return pe, func() { - if rCleanup != nil { - rCleanup() - } - } - } - - if i > 45 { - rCleanup() - t.Fatal("Timeout waiting for plan export to finish") - } - - time.Sleep(1 * time.Second) - } -} - -func createRegistryModule(t *testing.T, client *Client, org *Organization) (*RegistryModule, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - - options := RegistryModuleCreateOptions{ - Name: String("name"), - Provider: String("provider"), - } - rm, err := client.RegistryModules.Create(ctx, org.Name, options) - if err != nil { - t.Fatal(err) - } - - return rm, func() { - if err := client.RegistryModules.Delete(ctx, org.Name, rm.Name); err != nil { - t.Errorf("Error destroying registry module! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Registry Module: %s\nError: %s", rm.Name, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -func createRegistryModuleWithVersion(t *testing.T, client *Client, org *Organization) (*RegistryModule, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - ctx := context.Background() - - options := RegistryModuleCreateOptions{ - Name: String("name"), - Provider: String("provider"), - } - rm, err := client.RegistryModules.Create(ctx, org.Name, options) - if err != nil { - t.Fatal(err) - } - - optionsModuleVersion := RegistryModuleCreateVersionOptions{ - Version: String("1.0.0"), - } - _, err = client.RegistryModules.CreateVersion(ctx, org.Name, rm.Name, rm.Provider, optionsModuleVersion) - if err != nil { - t.Fatal(err) - } - - rm, err = client.RegistryModules.Read(ctx, org.Name, rm.Name, rm.Provider) - if err != nil { - t.Fatal(err) - } - - return rm, func() { - if err := client.RegistryModules.Delete(ctx, org.Name, rm.Name); err != nil { - t.Errorf("Error destroying registry module! WARNING: Dangling resources\n"+ - "may exist! The full error is shown below.\n\n"+ - "Registry Module: %s\nError: %s", rm.Name, err) - } - - if orgCleanup != nil { - orgCleanup() - } - } -} - -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), - ManagePolicyOverrides: 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, wCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - if tm == nil { - tm, tmCleanup = createTeam(t, client, org) - } - - if w == nil { - w, wCleanup = 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() - } - - if wCleanup != nil { - wCleanup() - } - } -} - -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, w.ID, VariableCreateOptions{ - Key: String(randomString(t)), - Value: String(randomString(t)), - Category: Category(CategoryTerraform), - Description: String(randomString(t)), - }) - if err != nil { - t.Fatal(err) - } - - return v, func() { - if err := client.Variables.Delete(ctx, w.ID, 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() - } - } -} - -// queueAllRuns: Whether runs should be queued immediately after workspace creation. When set to -// false, runs triggered by a VCS change will not be queued until at least one run is manually -// queued. If set to true, a run will be automatically started after the configuration is ingressed -// from VCS. -func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization, options WorkspaceCreateOptions) (*Workspace, func()) { - var orgCleanup func() - - if org == nil { - org, orgCleanup = createOrganization(t, client) - } - - oc, ocCleanup := createOAuthToken(t, client, org) - - githubIdentifier := os.Getenv("GITHUB_POLICY_SET_IDENTIFIER") - if githubIdentifier == "" { - t.Fatal("Export a valid GITHUB_POLICY_SET_IDENTIFIER before running this test!") - } - - if options.Name == nil { - options.Name = String(randomString(t)) - } - - if options.VCSRepo == nil { - options.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 genSha(t *testing.T, secret, data string) string { - h := hmac.New(sha256.New, []byte(secret)) - _, err := h.Write([]byte(data)) - if err != nil { - t.Fatalf("error writing hmac: %s", err) - } - sha := hex.EncodeToString(h.Sum(nil)) - return sha -} - -func randomString(t *testing.T) string { - v, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - return v -} - -// skips a test if the environment is for Terraform Cloud. -func skipIfCloud(t *testing.T) { - if !enterpriseEnabled() { - t.Skip("Skipping test related to Terraform Cloud. Set ENABLE_TFE=1 to run.") - } -} - -// skips a test if the environment is for Terraform Enterprise -func skipIfEnterprise(t *testing.T) { - if enterpriseEnabled() { - t.Skip("Skipping test related to Terraform Enterprise. Set ENABLE_TFE=0 to run.") - } -} - -// skips a test if the test requires a paid feature, and this flag -// SKIP_PAID is set. -func skipIfFreeOnly(t *testing.T) { - if paidFeaturesDisabled() { - t.Skip("Skipping test that requires a paid feature. Remove 'SKIP_PAID=1' if you want to run this test") - } -} - -// Checks to see if ENABLE_TFE is set to 1, thereby enabling enterprise tests. -func enterpriseEnabled() bool { - return os.Getenv("ENABLE_TFE") == "1" -} - -func paidFeaturesDisabled() bool { - return os.Getenv("SKIP_PAID") == "1" -} From 08332cafea7064548eb4c378c32d4a40c53f5c4e Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 16:37:23 -0700 Subject: [PATCH 06/19] add back file because its functions are being used, is just that golangci-lint does not see it --- helper_test.go | 1039 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1039 insertions(+) create mode 100644 helper_test.go diff --git a/helper_test.go b/helper_test.go new file mode 100644 index 000000000..ebff5d2cf --- /dev/null +++ b/helper_test.go @@ -0,0 +1,1039 @@ +package tfe + +import ( + "context" + "crypto/hmac" + "crypto/md5" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "testing" + "time" + + "github.com/hashicorp/go-uuid" +) + +const badIdentifier = "! / nope" + +// Memoize test account details +var _testAccountDetails *TestAccountDetails + +func testClient(t *testing.T) *Client { + client, err := NewClient(nil) + if err != nil { + t.Fatal(err) + } + + return client +} + +func fetchTestAccountDetails(t *testing.T, client *Client) *TestAccountDetails { + if _testAccountDetails == nil { + _testAccountDetails = FetchTestAccountDetails(t, client) + } + return _testAccountDetails +} + +func createAgentPool(t *testing.T, client *Client, org *Organization) (*AgentPool, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + pool, err := client.AgentPools.Create(ctx, org.Name, AgentPoolCreateOptions{ + Name: String(randomString(t)), + }) + if err != nil { + t.Fatal(err) + } + + return pool, func() { + if err := client.AgentPools.Delete(ctx, pool.ID); err != nil { + t.Errorf("Error destroying agent pool! WARNING: Dangling resources "+ + "may exist! The full error is shown below.\n\n"+ + "Agent pool ID: %s\nError: %s", pool.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createAgentToken(t *testing.T, client *Client, ap *AgentPool) (*AgentToken, func()) { + var apCleanup func() + + if ap == nil { + ap, apCleanup = createAgentPool(t, client, nil) + } + + ctx := context.Background() + at, err := client.AgentTokens.Generate(ctx, ap.ID, AgentTokenGenerateOptions{ + Description: String(randomString(t)), + }) + if err != nil { + t.Fatal(err) + } + + return at, func() { + if err := client.AgentTokens.Delete(ctx, at.ID); err != nil { + t.Errorf("Error destroying agent token! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "AgentToken: %s\nError: %s", at.ID, err) + } + + if apCleanup != nil { + apCleanup() + } + } +} + +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, options *NotificationConfigurationCreateOptions) (*NotificationConfiguration, func()) { + var wCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + if options == nil { + options = &NotificationConfigurationCreateOptions{ + DestinationType: NotificationDestination(NotificationDestinationTypeGeneric), + Enabled: Bool(false), + Name: String(randomString(t)), + Token: String(randomString(t)), + URL: String("http://example.com"), + Triggers: []string{NotificationTriggerCreated}, + } + } + + ctx := context.Background() + nc, err := client.NotificationConfigurations.Create( + ctx, + w.ID, + *options, + ) + 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 createPolicySetParameter(t *testing.T, client *Client, ps *PolicySet) (*PolicySetParameter, func()) { + var psCleanup func() + + if ps == nil { + ps, psCleanup = createPolicySet(t, client, nil, nil, nil) + } + + ctx := context.Background() + v, err := client.PolicySetParameters.Create(ctx, ps.ID, PolicySetParameterCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryPolicySet), + }) + if err != nil { + t.Fatal(err) + } + + return v, func() { + if err := client.PolicySetParameters.Delete(ctx, ps.ID, v.ID); err != nil { + t.Errorf("Error destroying variable! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Parameter: %s\nError: %s", v.Key, err) + } + + if psCleanup != nil { + psCleanup() + } + } +} + +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 createPolicySetVersion(t *testing.T, client *Client, ps *PolicySet) (*PolicySetVersion, func()) { + var psCleanup func() + + if ps == nil { + ps, psCleanup = createPolicySet(t, client, nil, nil, nil) + } + + ctx := context.Background() + psv, err := client.PolicySetVersions.Create(ctx, ps.ID) + if err != nil { + t.Fatal(err) + } + + return psv, func() { + // Deleting a Policy Set Version is done through deleting a Policy Set. + if psCleanup != nil { + psCleanup() + } + } +} + +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.Skip("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 createOrganizationMembership(t *testing.T, client *Client, org *Organization) (*OrganizationMembership, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + mem, err := client.OrganizationMemberships.Create(ctx, org.Name, OrganizationMembershipCreateOptions{ + Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), + }) + if err != nil { + t.Fatal(err) + } + + return mem, func() { + if err := client.OrganizationMemberships.Delete(ctx, mem.ID); err != nil { + t.Errorf("Error destroying membership! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Membership: %s\nError: %s", mem.ID, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +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 createRunTrigger(t *testing.T, client *Client, w *Workspace, sourceable *Workspace) (*RunTrigger, func()) { + var wCleanup func() + var sourceableCleanup func() + + if w == nil { + w, wCleanup = createWorkspace(t, client, nil) + } + + if sourceable == nil { + sourceable, sourceableCleanup = createWorkspace(t, client, nil) + } + + ctx := context.Background() + rt, err := client.RunTriggers.Create( + ctx, + w.ID, + RunTriggerCreateOptions{ + Sourceable: sourceable, + }, + ) + if err != nil { + t.Fatal(err) + } + + return rt, func() { + if err := client.RunTriggers.Delete(ctx, rt.ID); err != nil { + t.Errorf("Error destroying run trigger! WARNING: Dangling\n"+ + "resources may exist! The full error is shown below.\n\n"+ + "RunTrigger: %s\nError: %s", rt.ID, err) + } + + if wCleanup != nil { + wCleanup() + } + + if sourceableCleanup != nil { + sourceableCleanup() + } + } +} + +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() { + cvCleanup() + + if wCleanup != nil { + wCleanup() + } + } +} + +func createRunWithStatus(t *testing.T, client *Client, w *Workspace, timeout int, desiredStatuses ...RunStatus) (*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) + } + + for _, desiredStatus := range desiredStatuses { + // if we're creating an applied run, we need to manually confirm the apply once the plan finishes + isApplyable := hasApplyableStatus(r) + if desiredStatus == RunApplied && isApplyable { + err := client.Runs.Apply(ctx, r.ID, RunApplyOptions{}) + if err != nil { + t.Fatal(err) + } + } else if desiredStatus == r.Status { + return r, rCleanup + } + } + + if i > timeout { + runStatus := r.Status + rCleanup() + t.Fatal(fmt.Printf("Timeout waiting for run to reach status %v, had status %s", desiredStatuses, runStatus)) + } + + time.Sleep(1 * time.Second) + } +} + +func createPlannedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + if paidFeaturesDisabled() { + return createRunWithStatus(t, client, w, 45, RunPlanned) + } else { + return createRunWithStatus(t, client, w, 45, RunCostEstimated) + } +} + +func createCostEstimatedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + return createRunWithStatus(t, client, w, 45, RunCostEstimated) +} + +func createPolicyCheckedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + return createRunWithStatus(t, client, w, 45, RunPolicyChecked, RunPolicyOverride) +} + +func createAppliedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { + return createRunWithStatus(t, client, w, 90, RunApplied) +} + +func hasApplyableStatus(r *Run) bool { + if len(r.PolicyChecks) > 0 { + return r.Status == RunPolicyChecked || r.Status == RunPolicyOverride + } else if r.CostEstimate != nil { + return r.Status == RunCostEstimated + } else { + return r.Status == RunPlanned + } +} + +func createPlanExport(t *testing.T, client *Client, r *Run) (*PlanExport, func()) { + var rCleanup func() + + if r == nil { + r, rCleanup = createPlannedRun(t, client, nil) + } + + ctx := context.Background() + pe, err := client.PlanExports.Create(ctx, PlanExportCreateOptions{ + Plan: r.Plan, + DataType: PlanExportType(PlanExportSentinelMockBundleV0), + }) + if err != nil { + t.Fatal(err) + } + + for i := 0; ; i++ { + pe, err := client.PlanExports.Read(ctx, pe.ID) + if err != nil { + t.Fatal(err) + } + + if pe.Status == PlanExportFinished { + return pe, func() { + if rCleanup != nil { + rCleanup() + } + } + } + + if i > 45 { + rCleanup() + t.Fatal("Timeout waiting for plan export to finish") + } + + time.Sleep(1 * time.Second) + } +} + +func createRegistryModule(t *testing.T, client *Client, org *Organization) (*RegistryModule, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + + options := RegistryModuleCreateOptions{ + Name: String("name"), + Provider: String("provider"), + } + rm, err := client.RegistryModules.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + return rm, func() { + if err := client.RegistryModules.Delete(ctx, org.Name, rm.Name); err != nil { + t.Errorf("Error destroying registry module! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Registry Module: %s\nError: %s", rm.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +func createRegistryModuleWithVersion(t *testing.T, client *Client, org *Organization) (*RegistryModule, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + ctx := context.Background() + + options := RegistryModuleCreateOptions{ + Name: String("name"), + Provider: String("provider"), + } + rm, err := client.RegistryModules.Create(ctx, org.Name, options) + if err != nil { + t.Fatal(err) + } + + optionsModuleVersion := RegistryModuleCreateVersionOptions{ + Version: String("1.0.0"), + } + _, err = client.RegistryModules.CreateVersion(ctx, org.Name, rm.Name, rm.Provider, optionsModuleVersion) + if err != nil { + t.Fatal(err) + } + + rm, err = client.RegistryModules.Read(ctx, org.Name, rm.Name, rm.Provider) + if err != nil { + t.Fatal(err) + } + + return rm, func() { + if err := client.RegistryModules.Delete(ctx, org.Name, rm.Name); err != nil { + t.Errorf("Error destroying registry module! WARNING: Dangling resources\n"+ + "may exist! The full error is shown below.\n\n"+ + "Registry Module: %s\nError: %s", rm.Name, err) + } + + if orgCleanup != nil { + orgCleanup() + } + } +} + +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), + ManagePolicyOverrides: 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, wCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + if tm == nil { + tm, tmCleanup = createTeam(t, client, org) + } + + if w == nil { + w, wCleanup = 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() + } + + if wCleanup != nil { + wCleanup() + } + } +} + +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, w.ID, VariableCreateOptions{ + Key: String(randomString(t)), + Value: String(randomString(t)), + Category: Category(CategoryTerraform), + Description: String(randomString(t)), + }) + if err != nil { + t.Fatal(err) + } + + return v, func() { + if err := client.Variables.Delete(ctx, w.ID, 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() + } + } +} + +// queueAllRuns: Whether runs should be queued immediately after workspace creation. When set to +// false, runs triggered by a VCS change will not be queued until at least one run is manually +// queued. If set to true, a run will be automatically started after the configuration is ingressed +// from VCS. +func createWorkspaceWithVCS(t *testing.T, client *Client, org *Organization, options WorkspaceCreateOptions) (*Workspace, func()) { + var orgCleanup func() + + if org == nil { + org, orgCleanup = createOrganization(t, client) + } + + oc, ocCleanup := createOAuthToken(t, client, org) + + githubIdentifier := os.Getenv("GITHUB_POLICY_SET_IDENTIFIER") + if githubIdentifier == "" { + t.Fatal("Export a valid GITHUB_POLICY_SET_IDENTIFIER before running this test!") + } + + if options.Name == nil { + options.Name = String(randomString(t)) + } + + if options.VCSRepo == nil { + options.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 genSha(t *testing.T, secret, data string) string { + h := hmac.New(sha256.New, []byte(secret)) + _, err := h.Write([]byte(data)) + if err != nil { + t.Fatalf("error writing hmac: %s", err) + } + sha := hex.EncodeToString(h.Sum(nil)) + return sha +} + +func randomString(t *testing.T) string { + v, err := uuid.GenerateUUID() + if err != nil { + t.Fatal(err) + } + return v +} + +// skips a test if the environment is for Terraform Cloud. +func skipIfCloud(t *testing.T) { + if !enterpriseEnabled() { + t.Skip("Skipping test related to Terraform Cloud. Set ENABLE_TFE=1 to run.") + } +} + +// skips a test if the environment is for Terraform Enterprise +func skipIfEnterprise(t *testing.T) { + if enterpriseEnabled() { + t.Skip("Skipping test related to Terraform Enterprise. Set ENABLE_TFE=0 to run.") + } +} + +// skips a test if the test requires a paid feature, and this flag +// SKIP_PAID is set. +func skipIfFreeOnly(t *testing.T) { + if paidFeaturesDisabled() { + t.Skip("Skipping test that requires a paid feature. Remove 'SKIP_PAID=1' if you want to run this test") + } +} + +// Checks to see if ENABLE_TFE is set to 1, thereby enabling enterprise tests. +func enterpriseEnabled() bool { + return os.Getenv("ENABLE_TFE") == "1" +} + +func paidFeaturesDisabled() bool { + return os.Getenv("SKIP_PAID") == "1" +} From 9f58003752d4e3bfe362ea41d71f4bb8bb583d7d Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 22 Sep 2021 16:38:24 -0700 Subject: [PATCH 07/19] add flag to ignore test files --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index c60094340..50f65065b 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -14,5 +14,5 @@ jobs: uses: golangci/golangci-lint-action@v2 with: version: latest - args: --timeout=5m + args: --timeout=5m --tests=false #only-new-issues: true From 5b21f13ec0244773061cc8f6ec2d4db42723f7ec Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Thu, 23 Sep 2021 09:52:00 -0700 Subject: [PATCH 08/19] add yml for golangci configuration and add setting to ignore unused functions from test files --- .github/workflows/golangci-lint.yml | 1 - .golangci.yml | 12 ++++++++++++ helper_test.go | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 50f65065b..fbec730cf 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -14,5 +14,4 @@ jobs: uses: golangci/golangci-lint-action@v2 with: version: latest - args: --timeout=5m --tests=false #only-new-issues: true diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..b88a9e4b5 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,12 @@ +run: + timeout: 5m +issues: + exclude-rules: + - path: _test\.go + linters: + - unused + - deadcode + + + + diff --git a/helper_test.go b/helper_test.go index ebff5d2cf..40d54e8ab 100644 --- a/helper_test.go +++ b/helper_test.go @@ -16,7 +16,7 @@ import ( "github.com/hashicorp/go-uuid" ) -const badIdentifier = "! / nope" +const badIdentifier = "! / nope" //nolint // Memoize test account details var _testAccountDetails *TestAccountDetails From 13c20e52413f9a2c9f48d50f0cdf80aed7ac866e Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 08:21:20 -0700 Subject: [PATCH 09/19] remove extra lines --- .golangci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index b88a9e4b5..4c22504aa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,3 @@ issues: linters: - unused - deadcode - - - - From 413f623a63e5ad0e93711a9f5aa3402900bbb42e Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 14:19:29 -0700 Subject: [PATCH 10/19] pin to version 1.29 to compare behaviour with version 1.40 --- .github/workflows/golangci-lint.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index fbec730cf..252afa318 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -8,10 +8,13 @@ jobs: golangci: name: lint runs-on: ubuntu-latest + strategy: + matrix: + go-version: [1.15.x] steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: latest + version: v1.29 #only-new-issues: true From 3c44683d44accf39692e0485616e06e60f832807 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 15:39:02 -0700 Subject: [PATCH 11/19] enable some useful lint libraries and use go version 1.16 and golangci-lint 1.42 to match local environment --- .github/workflows/golangci-lint.yml | 4 ++-- .golangci.yml | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 252afa318..bd834a158 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -10,11 +10,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: [1.15.x] + go-version: [1.16.x] steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.29 + version: v1.42 #only-new-issues: true diff --git a/.golangci.yml b/.golangci.yml index 4c22504aa..7240cd422 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,8 +1,14 @@ run: timeout: 5m +linters: + enable: + - gosimple + - stylecheck + - golint #deprecated + #other deprecated lint libraries: maligned, scopelint, interfacer issues: exclude-rules: - path: _test\.go linters: - unused - - deadcode + - deadcode \ No newline at end of file From be0ed9d80d7fb1fa39e674879f1c6ce6ff480590 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 15:43:27 -0700 Subject: [PATCH 12/19] golangci-lint library recommends to stop using golint because it is deprecated and archive, and instead use revive: https://github.com/hashicorp/go-tfe/pull/262/checks?check_run_id=3738237150 --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 7240cd422..a0d9cfbfc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,7 +4,7 @@ linters: enable: - gosimple - stylecheck - - golint #deprecated + - revive #deprecated #other deprecated lint libraries: maligned, scopelint, interfacer issues: exclude-rules: From 771ab13fc78f62d5e1a56da744c3f4424d56ca6d Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 16:11:28 -0700 Subject: [PATCH 13/19] fix new issues point by golangci-lint --- admin_setting_saml.go | 6 +++--- helper_test.go | 3 +-- ip_ranges.go | 2 +- oauth_client.go | 2 +- policy_set_version.go | 4 ++-- registry_module.go | 2 +- tfe.go | 17 ++++++++--------- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/admin_setting_saml.go b/admin_setting_saml.go index 8e35dc765..3de700faa 100644 --- a/admin_setting_saml.go +++ b/admin_setting_saml.go @@ -48,14 +48,14 @@ type AdminSAMLSetting struct { } // Read returns the SAML settings. -func (s *adminSAMLSettings) Read(ctx context.Context) (*AdminSAMLSetting, error) { - req, err := s.client.newRequest("GET", "admin/saml-settings", nil) +func (a *adminSAMLSettings) Read(ctx context.Context) (*AdminSAMLSetting, error) { + req, err := a.client.newRequest("GET", "admin/saml-settings", nil) if err != nil { return nil, err } saml := &AdminSAMLSetting{} - err = s.client.do(ctx, req, saml) + err = a.client.do(ctx, req, saml) if err != nil { return nil, err } diff --git a/helper_test.go b/helper_test.go index 40d54e8ab..6f9da74b6 100644 --- a/helper_test.go +++ b/helper_test.go @@ -561,9 +561,8 @@ func createRunWithStatus(t *testing.T, client *Client, w *Workspace, timeout int func createPlannedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { if paidFeaturesDisabled() { return createRunWithStatus(t, client, w, 45, RunPlanned) - } else { - return createRunWithStatus(t, client, w, 45, RunCostEstimated) } + return createRunWithStatus(t, client, w, 45, RunCostEstimated) } func createCostEstimatedRun(t *testing.T, client *Client, w *Workspace) (*Run, func()) { diff --git a/ip_ranges.go b/ip_ranges.go index f0801532e..162a797f5 100644 --- a/ip_ranges.go +++ b/ip_ranges.go @@ -83,7 +83,7 @@ func (i *ipRanges) customDo(ctx context.Context, req *retryablehttp.Request, ir defer resp.Body.Close() if resp.StatusCode < 200 && resp.StatusCode >= 400 { - return fmt.Errorf("Error HTTP response while retrieving IP ranges: %d", resp.StatusCode) + return fmt.Errorf("error HTTP response while retrieving IP ranges: %d", resp.StatusCode) } else if resp.StatusCode == 304 { return nil } diff --git a/oauth_client.go b/oauth_client.go index 9b3ddcfca..33bb259a4 100644 --- a/oauth_client.go +++ b/oauth_client.go @@ -144,7 +144,7 @@ func (o OAuthClientCreateOptions) valid() error { return errors.New("service provider is required") } if validString(o.PrivateKey) && *o.ServiceProvider != *ServiceProvider(ServiceProviderAzureDevOpsServer) { - return errors.New("Private Key can only be present with Azure DevOps Server service provider") + return errors.New("private Key can only be present with Azure DevOps Server service provider") } return nil } diff --git a/policy_set_version.go b/policy_set_version.go index 30261e820..6c0284413 100644 --- a/policy_set_version.go +++ b/policy_set_version.go @@ -85,11 +85,11 @@ type PolicySetVersion struct { func (p PolicySetVersion) uploadURL() (string, error) { uploadURL, ok := p.Links["upload"].(string) if !ok { - return uploadURL, fmt.Errorf("The Policy Set Version does not contain an upload link.") + return uploadURL, fmt.Errorf("the Policy Set Version does not contain an upload link") } if uploadURL == "" { - return uploadURL, fmt.Errorf("The Policy Set Version upload URL is empty.") + return uploadURL, fmt.Errorf("the Policy Set Version upload URL is empty") } return uploadURL, nil diff --git a/registry_module.go b/registry_module.go index 084520b8f..45b9abae2 100644 --- a/registry_module.go +++ b/registry_module.go @@ -110,7 +110,7 @@ type RegistryModuleVersion struct { func (r *registryModules) Upload(ctx context.Context, rmv RegistryModuleVersion, path string) error { uploadURL, ok := rmv.Links["upload"].(string) if !ok { - return fmt.Errorf("Provided RegistryModuleVersion does not contain an upload link") + return fmt.Errorf("provided RegistryModuleVersion does not contain an upload link") } body, err := packContents(path) diff --git a/tfe.go b/tfe.go index 9f50071f3..a605c04b9 100644 --- a/tfe.go +++ b/tfe.go @@ -531,18 +531,18 @@ func serializeRequestBody(v interface{}) (interface{}, error) { // Infer whether the request uses jsonapi or regular json // serialization based on how the fields are tagged. - jsonApiFields := 0 + jsonAPIFields := 0 jsonFields := 0 for i := 0; i < modelType.NumField(); i++ { structField := modelType.Field(i) if structField.Tag.Get("jsonapi") != "" { - jsonApiFields++ + jsonAPIFields++ } if structField.Tag.Get("json") != "" { jsonFields++ } } - if jsonApiFields > 0 && jsonFields > 0 { + if jsonAPIFields > 0 && jsonFields > 0 { // Defining a struct with both json and jsonapi tags doesn't // make sense, because a struct can only be serialized // as one or another. If this does happen, it's a bug @@ -552,13 +552,12 @@ func serializeRequestBody(v interface{}) (interface{}, error) { if jsonFields > 0 { return json.Marshal(v) - } else { - buf := bytes.NewBuffer(nil) - if err := jsonapi.MarshalPayloadWithoutIncluded(buf, v); err != nil { - return nil, err - } - return buf, nil } + buf := bytes.NewBuffer(nil) + if err := jsonapi.MarshalPayloadWithoutIncluded(buf, v); err != nil { + return nil, err + } + return buf, nil } // do sends an API request and returns the API response. The API response From cabf350b4aeeaded3bdd62bbcebdc14f38e69fa7 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Tue, 28 Sep 2021 16:22:39 -0700 Subject: [PATCH 14/19] clarify comment on why we are not using golint --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index a0d9cfbfc..812fc078a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,7 +4,7 @@ linters: enable: - gosimple - stylecheck - - revive #deprecated + - revive #golint is deprecated and golangci-lint recommends to use revive instead #other deprecated lint libraries: maligned, scopelint, interfacer issues: exclude-rules: From 1689acd14cb9a37a92c48da9be251654df05a573 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 29 Sep 2021 09:51:16 -0700 Subject: [PATCH 15/19] enable other lint libraries that I liked their description and add additional configuration to errcheck because some of their features are disable by default --- .golangci.yml | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 812fc078a..27d59293c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,13 +2,47 @@ run: timeout: 5m linters: enable: - - gosimple - - stylecheck - - revive #golint is deprecated and golangci-lint recommends to use revive instead + - whitespace #https://github.com/ultraware/whitespace + - noctx #https://github.com/sonatard/noctx + - nilerr #https://github.com/gostaticanalysis/nilerr + - nestif #https://github.com/nakabonne/nestif + - gocritic #https://github.com/go-critic/go-critic + - goconst #https://github.com/jgautheron/goconst + - exportloopref #https://github.com/kyoh86/exportloopref + - errname #https://github.com/Antonboom/errname + - errorlint #https://github.com/polyfloyd/go-errorlint + - bodyclose #https://github.com/timakin/bodyclose + # - cyclop #https://github.com/bkielbasa/cyclop + - errcheck #https://github.com/kisielk/errcheck + - stylecheck #https://github.com/dominikh/go-tools/tree/master/stylecheck + - revive #golint is deprecated and golangci-lint recommends to use revive instead https://github.com/mgechev/revive #other deprecated lint libraries: maligned, scopelint, interfacer issues: exclude-rules: - path: _test\.go linters: - unused - - deadcode \ No newline at end of file + - deadcode +linters-settings: + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: true + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: true + # list of functions to exclude from checking, where each entry is a single function to exclude. + # see https://github.com/kisielk/errcheck#excluding-functions for details + # exclude-functions: + # - io/ioutil.ReadFile + # - io.Copy(*bytes.Buffer) + # - io.Copy(os.Stdout) + revive: + # see https://github.com/mgechev/revive#available-rules for details. + ignore-generated-header: false #recommended in their configuration + severity: warning + rules: + - name: indent-error-flow #Prevents redundant else statements + severity: warning + - name: useless-break + severity: warning \ No newline at end of file From a5730604f3777202dac06c8341c1f37040658018 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 29 Sep 2021 13:10:27 -0700 Subject: [PATCH 16/19] add configuration to golangci-lint and fix errors mentioned by enabled lint libraries --- .golangci.yml | 11 ++++++----- logreader.go | 33 ++++++++++++++++----------------- tfe.go | 27 ++++++++++++++++----------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 27d59293c..fd44e4208 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,16 +1,17 @@ run: timeout: 5m linters: +#enabled by default: deadcode, errcheck, gosimple, govet, ineffasign, staticcheck, struccheck, typecheck, unused, varcheck enable: - whitespace #https://github.com/ultraware/whitespace - - noctx #https://github.com/sonatard/noctx + # - noctx #https://github.com/sonatard/noctx - nilerr #https://github.com/gostaticanalysis/nilerr - nestif #https://github.com/nakabonne/nestif - - gocritic #https://github.com/go-critic/go-critic - - goconst #https://github.com/jgautheron/goconst + # - gocritic #https://github.com/go-critic/go-critic #TODO: reasses usefulness + # - goconst #https://github.com/jgautheron/goconst #TODO: reasses usefulness - exportloopref #https://github.com/kyoh86/exportloopref - - errname #https://github.com/Antonboom/errname - - errorlint #https://github.com/polyfloyd/go-errorlint + # - errname #https://github.com/Antonboom/errname #TODO: reasses usefulness + # - errorlint #https://github.com/polyfloyd/go-errorlint #TODO: reasses usefulness - bodyclose #https://github.com/timakin/bodyclose # - cyclop #https://github.com/bkielbasa/cyclop - errcheck #https://github.com/kisielk/errcheck diff --git a/logreader.go b/logreader.go index aee4472fe..96ffe840c 100644 --- a/logreader.go +++ b/logreader.go @@ -121,23 +121,22 @@ func (r *LogReader) read(l []byte) (int, error) { // Check if we need to continue the loop and wait 500 miliseconds // before checking if there is a new chunk available or that the // run is finished and we are done reading all chunks. - if written == 0 { - if (r.startOfText && r.endOfText) || // The logstream finished without issues. - (r.startOfText && r.reads%10 == 0) || // The logstream terminated unexpectedly. - (!r.startOfText && r.reads > 1) { // The logstream doesn't support STX/ETX. - done, err := r.done() - if err != nil { - return 0, err - } - if done { - return 0, io.EOF - } - } - return 0, io.ErrNoProgress + if written != 0 { + // Update the offset for the next read. + r.offset += int64(written) + return written, nil } - // Update the offset for the next read. - r.offset += int64(written) - - return written, nil + if (r.startOfText && r.endOfText) || // The logstream finished without issues. + (r.startOfText && r.reads%10 == 0) || // The logstream terminated unexpectedly. + (!r.startOfText && r.reads > 1) { // The logstream doesn't support STX/ETX. + done, err := r.done() + if err != nil { + return 0, err + } + if done { + return 0, io.EOF + } + } + return 0, io.ErrNoProgress } diff --git a/tfe.go b/tfe.go index a605c04b9..8c57e9e7b 100644 --- a/tfe.go +++ b/tfe.go @@ -1,6 +1,8 @@ package tfe import ( + "log" + "bytes" "context" "encoding/json" @@ -155,7 +157,7 @@ func NewClient(cfg *Config) (*Client, error) { config := DefaultConfig() // Layer in the provided config for any non-blank values. - if cfg != nil { + if cfg != nil { // nolint if cfg.Address != "" { config.Address = cfg.Address } @@ -356,14 +358,15 @@ func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Respons // First create some jitter bounded by the min and max durations. jitter := time.Duration(rnd.Float64() * float64(max-min)) - if resp != nil { - if v := resp.Header.Get(headerRateReset); v != "" { - if reset, _ := strconv.ParseFloat(v, 64); reset > 0 { - // Only update min if the given time to wait is longer. - if wait := time.Duration(reset * 1e9); wait > min { - min = wait - } - } + if resp != nil && resp.Header.Get(headerRateReset) != "" { + v := resp.Header.Get(headerRateReset) + reset, err := strconv.ParseFloat(v, 64) + if err != nil { + log.Fatal(err) + } + // Only update min if the given time to wait is longer + if reset > 0 && time.Duration(reset*1e9) > min { + min = time.Duration(reset * 1e9) } } @@ -417,13 +420,15 @@ func (c *Client) getRawAPIMetadata() (rawAPIMetadata, error) { // configureLimiter configures the rate limiter. func (c *Client) configureLimiter(rawLimit string) { - // Set default values for when rate limiting is disabled. limit := rate.Inf burst := 0 if v := rawLimit; v != "" { - if rateLimit, _ := strconv.ParseFloat(v, 64); rateLimit > 0 { + if rateLimit, err := strconv.ParseFloat(v, 64); rateLimit > 0 { + if err != nil { + log.Fatal(err) + } // Configure the limit and burst using a split of 2/3 for the limit and // 1/3 for the burst. This enables clients to burst 1/3 of the allowed // calls before the limiter kicks in. The remaining calls will then be From 2e17d84bc145d4cca6b5185941beec48588175f2 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 29 Sep 2021 13:13:53 -0700 Subject: [PATCH 17/19] remove not useful comments --- .golangci.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index fd44e4208..b6c345272 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,18 +26,10 @@ issues: - deadcode linters-settings: errcheck: - # report about not checking of errors in type assertions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. + # https://github.com/kisielk/errcheck#excluding-functions check-type-assertions: true - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. check-blank: true - # list of functions to exclude from checking, where each entry is a single function to exclude. - # see https://github.com/kisielk/errcheck#excluding-functions for details - # exclude-functions: - # - io/ioutil.ReadFile - # - io.Copy(*bytes.Buffer) - # - io.Copy(os.Stdout) + revive: # see https://github.com/mgechev/revive#available-rules for details. ignore-generated-header: false #recommended in their configuration From 1cb63d4c9a3eec2a4238511f2aa7add70d30663c Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 29 Sep 2021 13:23:11 -0700 Subject: [PATCH 18/19] match lowercase in tests --- oauth_client_integration_test.go | 2 +- policy_set_version_integration_test.go | 4 ++-- registry_module_integration_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/oauth_client_integration_test.go b/oauth_client_integration_test.go index ec077f711..39f7c4ce2 100644 --- a/oauth_client_integration_test.go +++ b/oauth_client_integration_test.go @@ -322,7 +322,7 @@ func TestOAuthClientsCreateOptionsValid(t *testing.T) { } err := options.valid() - assert.EqualError(t, err, "Private Key can only be present with Azure DevOps Server service provider") + assert.EqualError(t, err, "private Key can only be present with Azure DevOps Server service provider") }) t.Run("with valid options including private key", func(t *testing.T) { diff --git a/policy_set_version_integration_test.go b/policy_set_version_integration_test.go index 6181626f7..17746d05f 100644 --- a/policy_set_version_integration_test.go +++ b/policy_set_version_integration_test.go @@ -102,7 +102,7 @@ func TestPolicySetVersionsUpload(t *testing.T) { *psv, "test-fixtures/policy-set-version", ) - assert.EqualError(t, err, "The Policy Set Version does not contain an upload link.") + assert.EqualError(t, err, "the Policy Set Version does not contain an upload link") }) } @@ -130,7 +130,7 @@ func TestPolicySetVersionsUploadURL(t *testing.T) { } _, err := psv.uploadURL() - assert.EqualError(t, err, "The Policy Set Version does not contain an upload link.") + assert.EqualError(t, err, "the Policy Set Version does not contain an upload link") }) t.Run("errors when the upload link is empty", func(t *testing.T) { diff --git a/registry_module_integration_test.go b/registry_module_integration_test.go index 522d516a3..8da04b9e4 100644 --- a/registry_module_integration_test.go +++ b/registry_module_integration_test.go @@ -571,7 +571,7 @@ func TestRegistryModulesUpload(t *testing.T) { *rmv, "test-fixtures/config-version", ) - assert.EqualError(t, err, "Provided RegistryModuleVersion does not contain an upload link") + assert.EqualError(t, err, "provided RegistryModuleVersion does not contain an upload link") }) } From 8b804937911e7d7e7162f89092080da98d62a474 Mon Sep 17 00:00:00 2001 From: uturunku1 Date: Wed, 29 Sep 2021 13:57:58 -0700 Subject: [PATCH 19/19] missing test --- policy_set_version_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policy_set_version_integration_test.go b/policy_set_version_integration_test.go index 17746d05f..c3039bff2 100644 --- a/policy_set_version_integration_test.go +++ b/policy_set_version_integration_test.go @@ -142,6 +142,6 @@ func TestPolicySetVersionsUploadURL(t *testing.T) { } _, err := psv.uploadURL() - assert.EqualError(t, err, "The Policy Set Version upload URL is empty.") + assert.EqualError(t, err, "the Policy Set Version upload URL is empty") }) }