From 158da4442a580bd4c8e4997b46cb74fb1bac4489 Mon Sep 17 00:00:00 2001 From: Glenn Sarti Date: Thu, 1 Aug 2024 10:49:28 +0800 Subject: [PATCH] Update Org and Run Tasks for Private Run Tasks This commit updates the Org. entitlements for the new Private Run Tasks feature. This commit also updates the Run Task options struct for CRUD operations to pass through the agent pool relationship --- CHANGELOG.md | 7 ++++ organization.go | 1 + run_task.go | 9 ++++ run_task_integration_test.go | 79 +++++++++++++++++++++++++++++++++--- task_result.go | 1 + 5 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae9391d52..7c3b91c5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Unreleased * Add support for HCP Terraform `/api/v2/workspaces/{external_id}/all-vars` API endpoint to fetch the list of all variables available to a workspace (include inherited variables from varsets) by @debrin-hc [#1105](https://github.com/hashicorp/go-tfe/pull/1105) +## Enhancements + +* 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.82.0 ## Enhancements @@ -195,6 +200,8 @@ In the last release, Runs interface method `ListForOrganization` included pagina ## 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 5c0c9e004..d1bee4233 100644 --- a/organization.go +++ b/organization.go @@ -165,6 +165,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 b769dd1ff..78a75a16b 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"`