Skip to content

Commit c75a3d1

Browse files
authored
Merge pull request #1209 from hashicorp/hashimoon/TF-27882-agent-pool
[TF-27882] Add support for running Registry Module tests on custom Agents
2 parents d4d0527 + 87e4e97 commit c75a3d1

File tree

7 files changed

+482
-8
lines changed

7 files changed

+482
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
* Fixes arch validation on Terraform, OPA, and Sentinel Tool Versions when providing top level `url` and `sha` with multiple `archs` by @kelsi-hoyle [#1212](https://github.com/hashicorp/go-tfe/pull/1212)
66

7+
## Enhancements
8+
9+
* Adds BETA support for performing Registry Module test runs on custom Agents by @hashimooon [#1209](httpsLhttps://github.com/hashicorp/go-tfe/pull/1209)
10+
711
# v1.91.1
812

913
## Bug Fixes

errors.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,12 @@ var (
247247

248248
ErrRequiredAgentPoolID = errors.New("'agent' execution mode requires an agent pool ID to be specified")
249249

250-
ErrRequiredAgentMode = errors.New("specifying an agent pool ID requires 'agent' execution mode")
251-
ErrRequiredBranchWhenTestsEnabled = errors.New("VCS branch is required when enabling tests")
252-
ErrBranchMustBeEmptyWhenTagsEnabled = errors.New("VCS branch must be empty to enable tags")
253-
ErrRequiredCategory = errors.New("category is required")
254-
255-
ErrRequiredDestinationType = errors.New("destination type is required")
250+
ErrRequiredAgentMode = errors.New("specifying an agent pool ID requires 'agent' execution mode")
251+
ErrRequiredBranchWhenTestsEnabled = errors.New("VCS branch is required when enabling tests")
252+
ErrBranchMustBeEmptyWhenTagsEnabled = errors.New("VCS branch must be empty to enable tags")
253+
ErrRequiredCategory = errors.New("category is required")
254+
ErrAgentPoolNotRequiredForRemoteExecution = errors.New("'remote' execution mode does not support agent pool IDs")
255+
ErrRequiredDestinationType = errors.New("destination type is required")
256256

257257
ErrRequiredDataType = errors.New("data type is required")
258258

registry_module.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ import (
1414
"strings"
1515
)
1616

17+
type AgentExecutionMode string
18+
19+
const (
20+
AgentExecutionModeAgent AgentExecutionMode = "agent"
21+
AgentExecutionModeRemote AgentExecutionMode = "remote"
22+
)
23+
24+
func (a *AgentExecutionMode) UnmarshalText(text []byte) error {
25+
*a = AgentExecutionMode(string(text))
26+
return nil
27+
}
28+
29+
func (a AgentExecutionMode) MarshalText() ([]byte, error) {
30+
return []byte(string(a)), nil
31+
}
32+
1733
// Compile-time proof of interface implementation.
1834
var _ RegistryModules = (*registryModules)(nil)
1935

@@ -366,7 +382,9 @@ type RegistryModuleUpdateOptions struct {
366382
}
367383

368384
type RegistryModuleTestConfigOptions struct {
369-
TestsEnabled *bool `jsonapi:"attr,tests-enabled,omitempty"`
385+
TestsEnabled *bool `jsonapi:"attr,tests-enabled,omitempty"`
386+
AgentExecutionMode *AgentExecutionMode `jsonapi:"attr,agent-execution-mode,omitempty"`
387+
AgentPoolID *string `jsonapi:"attr,agent-pool-id,omitempty"`
370388
}
371389

372390
type RegistryModuleVCSRepoOptions struct {
@@ -533,6 +551,12 @@ func (r *registryModules) Update(ctx context.Context, moduleID RegistryModuleID,
533551
}
534552
}
535553

554+
if options.TestConfig != nil && options.TestConfig.AgentExecutionMode != nil {
555+
if *options.TestConfig.AgentExecutionMode == AgentExecutionModeRemote && options.TestConfig.AgentPoolID != nil {
556+
return nil, ErrAgentPoolNotRequiredForRemoteExecution
557+
}
558+
}
559+
536560
org := url.PathEscape(moduleID.Organization)
537561
registryName := url.PathEscape(string(moduleID.RegistryName))
538562
namespace := url.PathEscape(moduleID.Namespace)
@@ -597,6 +621,13 @@ func (r *registryModules) CreateWithVCSConnection(ctx context.Context, options R
597621
url.PathEscape(*options.VCSRepo.OrganizationName),
598622
)
599623
}
624+
625+
if options.TestConfig != nil && options.TestConfig.AgentExecutionMode != nil {
626+
if *options.TestConfig.AgentExecutionMode == AgentExecutionModeRemote && options.TestConfig.AgentPoolID != nil {
627+
return nil, ErrAgentPoolNotRequiredForRemoteExecution
628+
}
629+
}
630+
600631
req, err := r.client.NewRequest("POST", u, &options)
601632
if err != nil {
602633
return nil, err

registry_module_integration_test.go

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,3 +2221,234 @@ func TestRegistryCreateWithVCSOptions_Marshal(t *testing.T) {
22212221
`
22222222
assert.Equal(t, expectedBody, string(bodyBytes))
22232223
}
2224+
2225+
func TestRegistryModulesUpdate_AgentExecutionValidation(t *testing.T) {
2226+
skipUnlessBeta(t)
2227+
2228+
githubIdentifier := os.Getenv("GITHUB_REGISTRY_MODULE_IDENTIFIER")
2229+
if githubIdentifier == "" {
2230+
t.Skip("Export a valid GITHUB_REGISTRY_MODULE_IDENTIFIER before running this test")
2231+
}
2232+
2233+
githubBranch := os.Getenv("GITHUB_REGISTRY_MODULE_BRANCH")
2234+
if githubBranch == "" {
2235+
githubBranch = "main"
2236+
}
2237+
2238+
client := testClient(t)
2239+
ctx := context.Background()
2240+
2241+
orgTest, orgTestCleanup := createOrganization(t, client)
2242+
defer orgTestCleanup()
2243+
2244+
agentPool, agentPoolCleanup := createAgentPool(t, client, orgTest)
2245+
defer agentPoolCleanup()
2246+
2247+
oauthTokenTest, oauthTokenTestCleanup := createOAuthToken(t, client, orgTest)
2248+
defer oauthTokenTestCleanup()
2249+
2250+
// Create a VCS-connected registry module with tests enabled for testing updates
2251+
createOptions := RegistryModuleCreateWithVCSConnectionOptions{
2252+
VCSRepo: &RegistryModuleVCSRepoOptions{
2253+
OrganizationName: String(orgTest.Name),
2254+
Identifier: String(githubIdentifier),
2255+
OAuthTokenID: String(oauthTokenTest.ID),
2256+
DisplayIdentifier: String(githubIdentifier),
2257+
Branch: String(githubBranch),
2258+
},
2259+
TestConfig: &RegistryModuleTestConfigOptions{
2260+
TestsEnabled: Bool(true),
2261+
},
2262+
}
2263+
rm, err := client.RegistryModules.CreateWithVCSConnection(ctx, createOptions)
2264+
require.NoError(t, err)
2265+
assert.NotEmpty(t, rm.ID)
2266+
2267+
moduleID := RegistryModuleID{
2268+
Organization: orgTest.Name,
2269+
Name: rm.Name,
2270+
Provider: rm.Provider,
2271+
Namespace: rm.Namespace,
2272+
RegistryName: rm.RegistryName,
2273+
}
2274+
2275+
// Cleanup the created module
2276+
defer func() {
2277+
if err := client.RegistryModules.Delete(ctx, orgTest.Name, rm.Name); err != nil {
2278+
t.Logf("Error deleting registry module: %v", err)
2279+
}
2280+
}()
2281+
2282+
t.Run("errors when remote execution mode has agent pool ID", func(t *testing.T) {
2283+
updateOptions := RegistryModuleUpdateOptions{
2284+
TestConfig: &RegistryModuleTestConfigOptions{
2285+
TestsEnabled: Bool(true),
2286+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeRemote),
2287+
AgentPoolID: String(agentPool.ID),
2288+
},
2289+
}
2290+
2291+
_, err := client.RegistryModules.Update(ctx, moduleID, updateOptions)
2292+
assert.Error(t, err)
2293+
assert.Equal(t, ErrAgentPoolNotRequiredForRemoteExecution, err)
2294+
})
2295+
2296+
t.Run("succeeds when agent execution mode has agent pool ID", func(t *testing.T) {
2297+
updateOptions := RegistryModuleUpdateOptions{
2298+
TestConfig: &RegistryModuleTestConfigOptions{
2299+
TestsEnabled: Bool(true),
2300+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeAgent),
2301+
AgentPoolID: String(agentPool.ID),
2302+
},
2303+
}
2304+
2305+
updatedRM, err := client.RegistryModules.Update(ctx, moduleID, updateOptions)
2306+
require.NoError(t, err)
2307+
assert.NotNil(t, updatedRM)
2308+
assert.NotNil(t, updatedRM.TestConfig)
2309+
assert.True(t, updatedRM.TestConfig.TestsEnabled)
2310+
2311+
// Verify that AgentExecutionMode and AgentPoolID are returned correctly
2312+
assert.NotNil(t, updatedRM.TestConfig.AgentExecutionMode)
2313+
assert.Equal(t, string(AgentExecutionModeAgent), *updatedRM.TestConfig.AgentExecutionMode)
2314+
assert.NotNil(t, updatedRM.TestConfig.AgentPoolID)
2315+
assert.Equal(t, agentPool.ID, *updatedRM.TestConfig.AgentPoolID)
2316+
})
2317+
2318+
t.Run("succeeds when remote execution mode has no agent pool ID", func(t *testing.T) {
2319+
updateOptions := RegistryModuleUpdateOptions{
2320+
TestConfig: &RegistryModuleTestConfigOptions{
2321+
TestsEnabled: Bool(true),
2322+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeRemote),
2323+
},
2324+
}
2325+
2326+
updatedRM, err := client.RegistryModules.Update(ctx, moduleID, updateOptions)
2327+
require.NoError(t, err)
2328+
assert.NotNil(t, updatedRM)
2329+
assert.NotNil(t, updatedRM.TestConfig)
2330+
assert.True(t, updatedRM.TestConfig.TestsEnabled)
2331+
2332+
// Verify that AgentExecutionMode is returned correctly and AgentPoolID is nil
2333+
assert.NotNil(t, updatedRM.TestConfig.AgentExecutionMode)
2334+
assert.Equal(t, string(AgentExecutionModeRemote), *updatedRM.TestConfig.AgentExecutionMode)
2335+
assert.Nil(t, updatedRM.TestConfig.AgentPoolID)
2336+
})
2337+
}
2338+
2339+
func TestRegistryModulesCreateWithVCSConnection_AgentExecutionValidation(t *testing.T) {
2340+
skipUnlessBeta(t)
2341+
2342+
githubIdentifier := os.Getenv("GITHUB_REGISTRY_MODULE_IDENTIFIER")
2343+
if githubIdentifier == "" {
2344+
t.Skip("Export a valid GITHUB_REGISTRY_MODULE_IDENTIFIER before running this test")
2345+
}
2346+
2347+
githubBranch := os.Getenv("GITHUB_REGISTRY_MODULE_BRANCH")
2348+
if githubBranch == "" {
2349+
githubBranch = "main"
2350+
}
2351+
2352+
client := testClient(t)
2353+
ctx := context.Background()
2354+
2355+
orgTest, orgTestCleanup := createOrganization(t, client)
2356+
defer orgTestCleanup()
2357+
2358+
agentPool, agentPoolCleanup := createAgentPool(t, client, orgTest)
2359+
defer agentPoolCleanup()
2360+
2361+
oauthTokenTest, oauthTokenTestCleanup := createOAuthToken(t, client, orgTest)
2362+
defer oauthTokenTestCleanup()
2363+
2364+
t.Run("errors when remote execution mode has agent pool ID", func(t *testing.T) {
2365+
options := RegistryModuleCreateWithVCSConnectionOptions{
2366+
VCSRepo: &RegistryModuleVCSRepoOptions{
2367+
OrganizationName: String(orgTest.Name),
2368+
Identifier: String(githubIdentifier),
2369+
OAuthTokenID: String(oauthTokenTest.ID),
2370+
DisplayIdentifier: String(githubIdentifier),
2371+
Branch: String(githubBranch),
2372+
},
2373+
TestConfig: &RegistryModuleTestConfigOptions{
2374+
TestsEnabled: Bool(true),
2375+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeRemote),
2376+
AgentPoolID: String(agentPool.ID),
2377+
},
2378+
}
2379+
2380+
_, err := client.RegistryModules.CreateWithVCSConnection(ctx, options)
2381+
assert.Error(t, err)
2382+
assert.Equal(t, ErrAgentPoolNotRequiredForRemoteExecution, err)
2383+
})
2384+
2385+
t.Run("succeeds when agent execution mode has agent pool ID", func(t *testing.T) {
2386+
options := RegistryModuleCreateWithVCSConnectionOptions{
2387+
VCSRepo: &RegistryModuleVCSRepoOptions{
2388+
OrganizationName: String(orgTest.Name),
2389+
Identifier: String(githubIdentifier),
2390+
OAuthTokenID: String(oauthTokenTest.ID),
2391+
DisplayIdentifier: String(githubIdentifier),
2392+
Branch: String(githubBranch),
2393+
},
2394+
TestConfig: &RegistryModuleTestConfigOptions{
2395+
TestsEnabled: Bool(true),
2396+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeAgent),
2397+
AgentPoolID: String(agentPool.ID),
2398+
},
2399+
}
2400+
2401+
rm, err := client.RegistryModules.CreateWithVCSConnection(ctx, options)
2402+
require.NoError(t, err)
2403+
assert.NotEmpty(t, rm.ID)
2404+
assert.NotNil(t, rm.TestConfig)
2405+
assert.True(t, rm.TestConfig.TestsEnabled)
2406+
2407+
// Verify that AgentExecutionMode and AgentPoolID are returned correctly
2408+
assert.NotNil(t, rm.TestConfig.AgentExecutionMode)
2409+
assert.Equal(t, string(AgentExecutionModeAgent), *rm.TestConfig.AgentExecutionMode)
2410+
assert.NotNil(t, rm.TestConfig.AgentPoolID)
2411+
assert.Equal(t, agentPool.ID, *rm.TestConfig.AgentPoolID)
2412+
2413+
// Cleanup the created module
2414+
defer func() {
2415+
if err := client.RegistryModules.Delete(ctx, orgTest.Name, rm.Name); err != nil {
2416+
t.Logf("Error deleting registry module: %v", err)
2417+
}
2418+
}()
2419+
})
2420+
2421+
t.Run("succeeds when remote execution mode has no agent pool ID", func(t *testing.T) {
2422+
options := RegistryModuleCreateWithVCSConnectionOptions{
2423+
VCSRepo: &RegistryModuleVCSRepoOptions{
2424+
OrganizationName: String(orgTest.Name),
2425+
Identifier: String(githubIdentifier),
2426+
OAuthTokenID: String(oauthTokenTest.ID),
2427+
DisplayIdentifier: String(githubIdentifier),
2428+
Branch: String(githubBranch),
2429+
},
2430+
TestConfig: &RegistryModuleTestConfigOptions{
2431+
TestsEnabled: Bool(true),
2432+
AgentExecutionMode: AgentExecutionModePtr(AgentExecutionModeRemote),
2433+
},
2434+
}
2435+
2436+
rm, err := client.RegistryModules.CreateWithVCSConnection(ctx, options)
2437+
require.NoError(t, err)
2438+
assert.NotEmpty(t, rm.ID)
2439+
assert.NotNil(t, rm.TestConfig)
2440+
assert.True(t, rm.TestConfig.TestsEnabled)
2441+
2442+
// Verify that AgentExecutionMode is returned correctly and AgentPoolID is nil
2443+
assert.NotNil(t, rm.TestConfig.AgentExecutionMode)
2444+
assert.Equal(t, string(AgentExecutionModeRemote), *rm.TestConfig.AgentExecutionMode)
2445+
assert.Nil(t, rm.TestConfig.AgentPoolID)
2446+
2447+
// Cleanup the created module
2448+
defer func() {
2449+
if err := client.RegistryModules.Delete(ctx, orgTest.Name, rm.Name); err != nil {
2450+
t.Logf("Error deleting registry module: %v", err)
2451+
}
2452+
}()
2453+
})
2454+
}

0 commit comments

Comments
 (0)