Skip to content

Commit

Permalink
Merge pull request #865 from hashicorp/gs/add-global-run-tasks
Browse files Browse the repository at this point in the history
Update Run Tasks for global config and stages
  • Loading branch information
glennsarti committed Apr 8, 2024
2 parents 146ad87 + 3cbccb0 commit 5e21f37
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 108 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## Enhancements
* Adds Bitbucket Data Center as a new `ServiceProviderType` and ensures similar validation as Bitbucket Server by @zainq11 [#879](https://github.com/hashicorp/go-tfe/pull/879)
* Add `GlobalRunTasks` field to `Entitlements`. by @glennsarti [#865](https://github.com/hashicorp/go-tfe/pull/865)
* Add `Global` field to `RunTask`. by @glennsarti [#865](https://github.com/hashicorp/go-tfe/pull/865)
* Add `Stages` field to `WorkspaceRunTask`. by @glennsarti [#865](https://github.com/hashicorp/go-tfe/pull/865)

# v1.49.0
## Deprecations
* The `Stage` field has been deprecated on `WorkspaceRunTask`. Instead, use `Stages`. by @glennsarti [#865](https://github.com/hashicorp/go-tfe/pull/865)

# v1.49.0
## Enhancements
* Adds `post_apply` to list of possible `stages` for Run Tasks by @glennsarti [#878](https://github.com/hashicorp/go-tfe/pull/878)

Expand Down
74 changes: 4 additions & 70 deletions helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"io"
"math/rand"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
Expand All @@ -38,31 +37,6 @@ const agentVersion = "1.3.0"

var _testAccountDetails *TestAccountDetails

type featureSet struct {
ID string `jsonapi:"primary,feature-sets"`
}

type featureSetList struct {
Items []*featureSet
*Pagination
}

type featureSetListOptions struct {
Q string `url:"q,omitempty"`
}

type retryableFn func() (interface{}, error)

type updateFeatureSetOptions struct {
Type string `jsonapi:"primary,subscription"`
RunsCeiling int `jsonapi:"attr,runs-ceiling"`
ContractStartAt time.Time `jsonapi:"attr,contract-start-at,iso8601"`
ContractUserLimit int `jsonapi:"attr,contract-user-limit"`
ContractApplyLimit int `jsonapi:"attr,contract-apply-limit"`

FeatureSet *featureSet `jsonapi:"relation,feature-set"`
}

func testClient(t *testing.T) *Client {
client, err := NewClient(&Config{
RetryServerErrors: true,
Expand Down Expand Up @@ -959,7 +933,7 @@ func createOrganizationWithOptions(t *testing.T, client *Client, options Organiz
ctx := context.Background()
org, err := client.Organizations.Create(ctx, options)
if err != nil {
t.Fatal(err)
t.Fatalf("Failed to create organization: %s", err)
}

return org, func() {
Expand Down Expand Up @@ -2526,49 +2500,9 @@ func createVariableSetVariable(t *testing.T, client *Client, vs *VariableSet, op
}

// Attempts to upgrade an organization to the business plan. Requires a user token with admin access.
func upgradeOrganizationSubscription(t *testing.T, client *Client, organization *Organization) {
if enterpriseEnabled() {
t.Skip("Cannot upgrade an organization's subscription when enterprise is enabled. Set ENABLE_TFE=0 to run.")
}

adminClient := testAdminClient(t, provisionLicensesAdmin)
req, err := adminClient.NewRequest("GET", "admin/feature-sets", featureSetListOptions{
Q: "Business",
})
if err != nil {
t.Fatal(err)
return
}

fsl := &featureSetList{}
err = req.Do(context.Background(), fsl)
if err != nil {
t.Fatalf("failed to enumerate feature sets: %v", err)
return
} else if len(fsl.Items) == 0 {
t.Fatalf("feature set response was empty")
return
}

opts := updateFeatureSetOptions{
RunsCeiling: 10,
ContractStartAt: time.Now(),
ContractUserLimit: 1000,
ContractApplyLimit: 5000,
FeatureSet: fsl.Items[0],
}

u := fmt.Sprintf("admin/organizations/%s/subscription", url.QueryEscape(organization.Name))
req, err = adminClient.NewRequest("POST", u, &opts)
if err != nil {
t.Fatalf("Failed to create request: %v", err)
return
}

err = req.Do(context.Background(), nil)
if err != nil {
t.Fatalf("Failed to upgrade subscription: %v", err)
}
// DEPRECATED : Please use the newSubscriptionUpdater instead.
func upgradeOrganizationSubscription(t *testing.T, _ *Client, organization *Organization) {
newSubscriptionUpdater(organization).WithBusinessPlan().Update(t)
}

func createProject(t *testing.T, client *Client, org *Organization) (*Project, func()) {
Expand Down
103 changes: 103 additions & 0 deletions internal_run_task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfe

// A private struct we need for unmarshalling
type internalRunTask struct {
ID string `jsonapi:"primary,tasks"`
Name string `jsonapi:"attr,name"`
URL string `jsonapi:"attr,url"`
Description string `jsonapi:"attr,description"`
Category string `jsonapi:"attr,category"`
HMACKey *string `jsonapi:"attr,hmac-key,omitempty"`
Enabled bool `jsonapi:"attr,enabled"`
RawGlobal map[string]interface{} `jsonapi:"attr,global-configuration,omitempty"`

Organization *Organization `jsonapi:"relation,organization"`
WorkspaceRunTasks []*internalWorkspaceRunTask `jsonapi:"relation,workspace-tasks"`
}

// Due to https://github.com/google/jsonapi/issues/74 we must first unmarshall using map[string]interface{}
// and then perform our own conversion from the map into a GlobalRunTask struct
func (irt internalRunTask) ToRunTask() *RunTask {
obj := RunTask{
ID: irt.ID,
Name: irt.Name,
URL: irt.URL,
Description: irt.Description,
Category: irt.Category,
HMACKey: irt.HMACKey,
Enabled: irt.Enabled,

Organization: irt.Organization,
}

// Convert the WorkspaceRunTasks
workspaceTasks := make([]*WorkspaceRunTask, len(irt.WorkspaceRunTasks))
for idx, rawTask := range irt.WorkspaceRunTasks {
if rawTask != nil {
workspaceTasks[idx] = rawTask.ToWorkspaceRunTask()
}
}
obj.WorkspaceRunTasks = workspaceTasks

// Check if the global configuration exists
if val, ok := irt.RawGlobal["enabled"]; !ok {
// The enabled property is required so we can assume now that the
// global configuration was not supplied
return &obj
} else if boolVal, ok := val.(bool); !ok {
// The enabled property exists but it is invalid (Couldn't cast to boolean)
// so assume the global configuration was not supplied
return &obj
} else {
obj.Global = &GlobalRunTask{
Enabled: boolVal,
}
}

// Global Enforcement Level
if val, ok := irt.RawGlobal["enforcement-level"]; ok {
if stringVal, ok := val.(string); ok {
obj.Global.EnforcementLevel = TaskEnforcementLevel(stringVal)
}
}

// Global Stages
if val, ok := irt.RawGlobal["stages"]; ok {
if stringsVal, ok := val.([]interface{}); ok {
obj.Global.Stages = make([]Stage, len(stringsVal))
for idx, stageName := range stringsVal {
if stringVal, ok := stageName.(string); ok {
obj.Global.Stages[idx] = Stage(stringVal)
}
}
}
}

return &obj
}

// A private struct we need for unmarshalling
type internalRunTaskList struct {
*Pagination
Items []*internalRunTask
}

// Due to https://github.com/google/jsonapi/issues/74 we must first unmarshall using
// the internal RunTask struct and convert that a RunTask
func (irt internalRunTaskList) ToRunTaskList() *RunTaskList {
obj := RunTaskList{
Pagination: irt.Pagination,
Items: make([]*RunTask, len(irt.Items)),
}

for idx, src := range irt.Items {
if src != nil {
obj.Items[idx] = src.ToRunTask()
}
}

return &obj
}
57 changes: 57 additions & 0 deletions internal_workspace_run_task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfe

// A private struct we need for unmarshalling
type internalWorkspaceRunTask struct {
ID string `jsonapi:"primary,workspace-tasks"`
EnforcementLevel TaskEnforcementLevel `jsonapi:"attr,enforcement-level"`
Stage Stage `jsonapi:"attr,stage"`
Stages []string `jsonapi:"attr,stages"`

RunTask *RunTask `jsonapi:"relation,task"`
Workspace *Workspace `jsonapi:"relation,workspace"`
}

// Due to https://github.com/google/jsonapi/issues/74 we must first unmarshall using map[string]interface{}
// and then perform our own conversion for the Stages
func (irt internalWorkspaceRunTask) ToWorkspaceRunTask() *WorkspaceRunTask {
obj := WorkspaceRunTask{
ID: irt.ID,
EnforcementLevel: irt.EnforcementLevel,
Stage: irt.Stage,
Stages: make([]Stage, len(irt.Stages)),
RunTask: irt.RunTask,
Workspace: irt.Workspace,
}

for idx, val := range irt.Stages {
obj.Stages[idx] = Stage(val)
}

return &obj
}

// A private struct we need for unmarshalling
type internalWorkspaceRunTaskList struct {
*Pagination
Items []*internalWorkspaceRunTask
}

// Due to https://github.com/google/jsonapi/issues/74 we must first unmarshall using
// the internal WorkspaceRunTask struct and convert that a WorkspaceRunTask
func (irt internalWorkspaceRunTaskList) ToWorkspaceRunTaskList() *WorkspaceRunTaskList {
obj := WorkspaceRunTaskList{
Pagination: irt.Pagination,
Items: make([]*WorkspaceRunTask, len(irt.Items)),
}

for idx, src := range irt.Items {
if src != nil {
obj.Items[idx] = src.ToWorkspaceRunTask()
}
}

return &obj
}
1 change: 1 addition & 0 deletions organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ type Entitlements struct {
Agents bool `jsonapi:"attr,agents"`
AuditLogging bool `jsonapi:"attr,audit-logging"`
CostEstimation bool `jsonapi:"attr,cost-estimation"`
GlobalRunTasks bool `jsonapi:"attr,global-run-tasks"`
Operations bool `jsonapi:"attr,operations"`
PrivateModuleRegistry bool `jsonapi:"attr,private-module-registry"`
RunTasks bool `jsonapi:"attr,run-tasks"`
Expand Down

0 comments on commit 5e21f37

Please sign in to comment.