diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a508b3d1..1cd3ad285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # UNRELEASED + +## Enhancements + * Adds `AllowMemberTokenManagement` permission to `Team` by @juliannatetreault [#922](https://github.com/hashicorp/go-tfe/pull/922) +* Adds `PrivateRunTasks` field to Entitlements by @glennsarti [#944](https://github.com/hashicorp/go-tfe/pull/944) +* Adds `AgentPool` relationship to options when creating and updating Run Tasks by @glennsarti [#944](https://github.com/hashicorp/go-tfe/pull/944) # v1.61.0 diff --git a/organization.go b/organization.go index 86a97515e..e25fa0cee 100644 --- a/organization.go +++ b/organization.go @@ -164,6 +164,7 @@ type Entitlements struct { GlobalRunTasks bool `jsonapi:"attr,global-run-tasks"` Operations bool `jsonapi:"attr,operations"` PrivateModuleRegistry bool `jsonapi:"attr,private-module-registry"` + PrivateRunTasks bool `jsonapi:"attr,private-run-tasks"` RunTasks bool `jsonapi:"attr,run-tasks"` SSO bool `jsonapi:"attr,sso"` Sentinel bool `jsonapi:"attr,sentinel"` diff --git a/run_task.go b/run_task.go index 3f7edb0a4..22fb79d1a 100644 --- a/run_task.go +++ b/run_task.go @@ -54,6 +54,7 @@ type RunTask struct { Enabled bool `jsonapi:"attr,enabled"` Global *GlobalRunTask `jsonapi:"attr,global-configuration,omitempty"` + AgentPool *AgentPool `jsonapi:"relation,agent-pool"` Organization *Organization `jsonapi:"relation,organization"` WorkspaceRunTasks []*WorkspaceRunTask `jsonapi:"relation,workspace-tasks"` } @@ -130,6 +131,10 @@ type RunTaskCreateOptions struct { // Optional: Whether the task contains global configuration Global *GlobalRunTaskOptions `jsonapi:"attr,global-configuration,omitempty"` + + // Optional: Whether the task will be executed using an Agent Pool + // Requires the PrivateRunTasks entitlement + AgentPool *AgentPool `jsonapi:"relation,agent-pool,omitempty"` } // RunTaskUpdateOptions represents the set of options for updating an organization's run task @@ -160,6 +165,10 @@ type RunTaskUpdateOptions struct { // Optional: Whether the task contains global configuration Global *GlobalRunTaskOptions `jsonapi:"attr,global-configuration,omitempty"` + + // Optional: Whether the task will be executed using an Agent Pool + // Requires the PrivateRunTasks entitlement + AgentPool *AgentPool `jsonapi:"relation,agent-pool,omitempty"` } // Create is used to create a new run task for an organization diff --git a/run_task_integration_test.go b/run_task_integration_test.go index ba1811cfb..26c878e91 100644 --- a/run_task_integration_test.go +++ b/run_task_integration_test.go @@ -13,15 +13,32 @@ import ( "github.com/stretchr/testify/require" ) -func hasGlobalRunTasks(client *Client, organizationName string) (bool, error) { +func getOrgEntitlements(client *Client, organizationName string) (*Entitlements, error) { ctx := context.Background() - if orgEntitlements, err := client.Organizations.ReadEntitlements(ctx, organizationName); err != nil { + orgEntitlements, err := client.Organizations.ReadEntitlements(ctx, organizationName) + if err != nil { + return nil, err + } + if orgEntitlements == nil { + return nil, errors.New("The organization entitlements are empty.") + } + return orgEntitlements, nil +} + +func hasGlobalRunTasks(client *Client, organizationName string) (bool, error) { + oe, err := getOrgEntitlements(client, organizationName) + if err != nil { return false, err - } else if orgEntitlements == nil { - return false, errors.New("The organization entitlements are empty.") - } else { - return orgEntitlements.GlobalRunTasks, nil } + return oe.GlobalRunTasks, nil +} + +func hasPrivateRunTasks(client *Client, organizationName string) (bool, error) { + oe, err := getOrgEntitlements(client, organizationName) + if err != nil { + return false, err + } + return oe.PrivateRunTasks, nil } func TestRunTasksCreate(t *testing.T) { @@ -54,6 +71,33 @@ func TestRunTasksCreate(t *testing.T) { } globalEnforce := Mandatory + t.Run("with an agent pool", func(t *testing.T) { + // We can only test if the org, supports private run tasks. For now this isn't + // a fatal error and we just skip the test. + if v, err := hasPrivateRunTasks(client, orgTest.Name); err != nil { + t.Fatalf("Could not retrieve the entitlements for the test organization.: %s", err) + } else if !v { + t.Skip("The test organization requires the private-run-tasks entitlement but is not entitled.") + return + } + + // Unfortunately when we create a Run Task it automatically verifies that the URL by sending a test payload. But + // this means with an agent pool, we need an agent pool to exist, and an agent created with request forwarding enabled. + // This is too much to create for this one test suite. So instead, we really only need to assert that; when the options include an + // agent pool, then we expect HCP Terraform to process the agent pool. So, if we send it a nonsense agent pool ID, then we + // expect an error to be returned saying that the ID was nonsense. + _, err := client.RunTasks.Create(ctx, orgTest.Name, RunTaskCreateOptions{ + Name: runTaskName, + URL: runTaskServerURL, + Description: &runTaskDescription, + Category: "task", + AgentPool: &AgentPool{ + ID: "apool-this-pool-id-will-never-exist-so-we-expect-http-error-response", + }, + }) + require.ErrorContains(t, err, "The provided agent pool does not exist") + }) + t.Run("add run task to organization", func(t *testing.T) { r, err := client.RunTasks.Create(ctx, orgTest.Name, RunTaskCreateOptions{ Name: runTaskName, @@ -262,6 +306,29 @@ func TestRunTasksUpdate(t *testing.T) { assert.Equal(t, newDescription, r.Description) }) + + t.Run("with an agent pool", func(t *testing.T) { + // We can only test if the org, supports private run tasks. For now this isn't + // a fatal error and we just skip the test. + if v, err := hasPrivateRunTasks(client, orgTest.Name); err != nil { + t.Fatalf("Could not retrieve the entitlements for the test organization.: %s", err) + } else if !v { + t.Skip("The test organization requires the private-run-tasks entitlement but is not entitled.") + return + } + + // Unfortunately when we update a Run Task it automatically verifies that the URL by sending a test payload. But + // this means with an agent pool, we need an agent pool to exist, and an agent created with request forwarding enabled. + // This is too much to create for this one test suite. So instead, we really only need to assert that; when the options include an + // agent pool, then we expect HCP Terraform to process the agent pool. So, if we send it a nonsense agent pool ID, then we + // expect an error to be returned saying that the ID was nonsense. + _, err := client.RunTasks.Update(ctx, runTaskTest.ID, RunTaskUpdateOptions{ + AgentPool: &AgentPool{ + ID: "apool-this-pool-id-will-never-exist-so-we-expect-http-error-response", + }, + }) + require.ErrorContains(t, err, "The provided agent pool does not exist") + }) } func TestRunTasksDelete(t *testing.T) { diff --git a/task_result.go b/task_result.go index f60bacd9b..6cb7d874a 100644 --- a/task_result.go +++ b/task_result.go @@ -66,6 +66,7 @@ type TaskResult struct { TaskURL string `jsonapi:"attr,task-url"` WorkspaceTaskID string `jsonapi:"attr,workspace-task-id"` WorkspaceTaskEnforcementLevel TaskEnforcementLevel `jsonapi:"attr,workspace-task-enforcement-level"` + AgentPoolID *string `jsonapi:"attr,agent-pool-id,omitempty"` // The task stage this result belongs to TaskStage *TaskStage `jsonapi:"relation,task_stage"`