Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for organization-level default execution mode and agent pool #1137

Merged
merged 19 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c819ce6
support org-level default agent pool and execution mode
SwiftEngineer Aug 30, 2023
e09d1b9
horrific first attempt at allowing users to set organization default …
SwiftEngineer Sep 6, 2023
1e3c62c
use seperate model to support default execution mode to avoid circula…
SwiftEngineer Sep 7, 2023
7d6056e
merge main
SwiftEngineer Nov 9, 2023
7e14b3a
fix erroneous drift when toggling operations from true to false when …
SwiftEngineer Nov 14, 2023
c25d26c
Merge branch 'main' into SwiftEngineer/org-level-agent-pools
SwiftEngineer Nov 14, 2023
bd56db5
fix agent_pool_id not being set to null after being unset
SwiftEngineer Nov 20, 2023
ca9e73d
fmt
SwiftEngineer Nov 20, 2023
8f432dc
fix validations erroring erroneously when the workspace is being set …
SwiftEngineer Nov 20, 2023
70d8c7e
fix for error thrown when switching from the default agent pool to th…
SwiftEngineer Nov 21, 2023
7f5df02
Merge branch 'main' into SwiftEngineer/org-level-agent-pools
SwiftEngineer Nov 21, 2023
f6d3bd9
remove default_execution_mode from organization, it is handled via it…
SwiftEngineer Nov 21, 2023
cdd8f1b
add changelog entry and documentation
SwiftEngineer Nov 21, 2023
4d4d4df
remove extra space
SwiftEngineer Nov 21, 2023
938dc89
clean up comments
SwiftEngineer Nov 21, 2023
9c38d83
fix agent pool id not receiving new configuration value due to underl…
SwiftEngineer Nov 21, 2023
37019aa
simplify test helper by removing unnecessary cleanup
SwiftEngineer Nov 21, 2023
9d1b0d4
add import docs
SwiftEngineer Nov 27, 2023
ca721a3
add note in changelog about execution_mode default changing
SwiftEngineer Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
<!-- Add CHANGELOG entry to this section for any PR awaiting the next release -->
<!-- Please also include if this is a Bug Fix, Enhancement, or Feature -->

BREAKING CHANGES:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't believe this rises to the level of a breaking change, but do think it deserves a special note. I'll think on it some more.

* `r/tfe_workspace`: Default value of the `execution_mode` field now uses the organization's `default_execution_mode`. If no `default_execution_mode` has been set, the default `execution_mode` will be unchanged (i.e. `remote`).

FEATURES:
* `d/tfe_registry_module`: Add `vcs_repo.tags` and `vcs_repo.branch` attributes to allow configuration of `publishing_mechanism`. Add `test_config` to support running tests on `branch`-based registry modules, by @hashimoon [1096](https://github.com/hashicorp/terraform-provider-tfe/pull/1096)
* **New Resource**: `r/tfe_organization_default_execution_mode` is a new resource to set the `default_execution_mode` and `default_agent_pool_id` for an organization, by @SwiftEngineer [1137](https://github.com/hashicorp/terraform-provider-tfe/pull/1137)'
* `r/tfe_workspace`: Now uses the organization's `default_execution_mode` and `default_agent_pool_id` as the default `execution_mode`, by @SwiftEngineer [1137](https://github.com/hashicorp/terraform-provider-tfe/pull/1137)'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change which should be highlighted

Copy link
Collaborator

@brandonc brandonc Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this breaks the API of the provider or the platform API itself because it's a purely additive change. If you don't change the default at the org level, everything continues to behave as before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't change the default at the org level

Yeah but what if someone does? 🙂
The provider is still pre-1.0 so everything is allowed. I'm just suggesting to add a specific note about this in the changelog.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. Previously, I was thinking setting the default execution_mode in an admission that you were prepared for that change to affect all the workspaces in your organization, but adding a note here couldn't hurt!


## v0.50.0

Expand Down
77 changes: 39 additions & 38 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,44 +106,45 @@ func Provider() *schema.Provider {
},

ResourcesMap: map[string]*schema.Resource{
"tfe_admin_organization_settings": resourceTFEAdminOrganizationSettings(),
"tfe_agent_pool": resourceTFEAgentPool(),
"tfe_agent_pool_allowed_workspaces": resourceTFEAgentPoolAllowedWorkspaces(),
"tfe_agent_token": resourceTFEAgentToken(),
"tfe_notification_configuration": resourceTFENotificationConfiguration(),
"tfe_oauth_client": resourceTFEOAuthClient(),
"tfe_organization": resourceTFEOrganization(),
"tfe_organization_membership": resourceTFEOrganizationMembership(),
"tfe_organization_module_sharing": resourceTFEOrganizationModuleSharing(),
"tfe_organization_run_task": resourceTFEOrganizationRunTask(),
"tfe_organization_token": resourceTFEOrganizationToken(),
"tfe_policy": resourceTFEPolicy(),
"tfe_policy_set": resourceTFEPolicySet(),
"tfe_policy_set_parameter": resourceTFEPolicySetParameter(),
"tfe_project": resourceTFEProject(),
"tfe_project_policy_set": resourceTFEProjectPolicySet(),
"tfe_project_variable_set": resourceTFEProjectVariableSet(),
"tfe_registry_module": resourceTFERegistryModule(),
"tfe_no_code_module": resourceTFENoCodeModule(),
"tfe_run_trigger": resourceTFERunTrigger(),
"tfe_sentinel_policy": resourceTFESentinelPolicy(),
"tfe_ssh_key": resourceTFESSHKey(),
"tfe_team": resourceTFETeam(),
"tfe_team_access": resourceTFETeamAccess(),
"tfe_team_organization_member": resourceTFETeamOrganizationMember(),
"tfe_team_organization_members": resourceTFETeamOrganizationMembers(),
"tfe_team_project_access": resourceTFETeamProjectAccess(),
"tfe_team_member": resourceTFETeamMember(),
"tfe_team_members": resourceTFETeamMembers(),
"tfe_team_token": resourceTFETeamToken(),
"tfe_terraform_version": resourceTFETerraformVersion(),
"tfe_workspace": resourceTFEWorkspace(),
"tfe_workspace_run_task": resourceTFEWorkspaceRunTask(),
"tfe_variable_set": resourceTFEVariableSet(),
"tfe_workspace_policy_set": resourceTFEWorkspacePolicySet(),
"tfe_workspace_policy_set_exclusion": resourceTFEWorkspacePolicySetExclusion(),
"tfe_workspace_run": resourceTFEWorkspaceRun(),
"tfe_workspace_variable_set": resourceTFEWorkspaceVariableSet(),
"tfe_admin_organization_settings": resourceTFEAdminOrganizationSettings(),
"tfe_agent_pool": resourceTFEAgentPool(),
"tfe_agent_pool_allowed_workspaces": resourceTFEAgentPoolAllowedWorkspaces(),
"tfe_agent_token": resourceTFEAgentToken(),
"tfe_notification_configuration": resourceTFENotificationConfiguration(),
"tfe_oauth_client": resourceTFEOAuthClient(),
"tfe_organization": resourceTFEOrganization(),
"tfe_organization_default_execution_mode": resourceTFEOrganizationDefaultExecutionMode(),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ the only line that really changed

"tfe_organization_membership": resourceTFEOrganizationMembership(),
"tfe_organization_module_sharing": resourceTFEOrganizationModuleSharing(),
"tfe_organization_run_task": resourceTFEOrganizationRunTask(),
"tfe_organization_token": resourceTFEOrganizationToken(),
"tfe_policy": resourceTFEPolicy(),
"tfe_policy_set": resourceTFEPolicySet(),
"tfe_policy_set_parameter": resourceTFEPolicySetParameter(),
"tfe_project": resourceTFEProject(),
"tfe_project_policy_set": resourceTFEProjectPolicySet(),
"tfe_project_variable_set": resourceTFEProjectVariableSet(),
"tfe_registry_module": resourceTFERegistryModule(),
"tfe_no_code_module": resourceTFENoCodeModule(),
"tfe_run_trigger": resourceTFERunTrigger(),
"tfe_sentinel_policy": resourceTFESentinelPolicy(),
"tfe_ssh_key": resourceTFESSHKey(),
"tfe_team": resourceTFETeam(),
"tfe_team_access": resourceTFETeamAccess(),
"tfe_team_organization_member": resourceTFETeamOrganizationMember(),
"tfe_team_organization_members": resourceTFETeamOrganizationMembers(),
"tfe_team_project_access": resourceTFETeamProjectAccess(),
"tfe_team_member": resourceTFETeamMember(),
"tfe_team_members": resourceTFETeamMembers(),
"tfe_team_token": resourceTFETeamToken(),
"tfe_terraform_version": resourceTFETerraformVersion(),
"tfe_workspace": resourceTFEWorkspace(),
"tfe_workspace_run_task": resourceTFEWorkspaceRunTask(),
"tfe_variable_set": resourceTFEVariableSet(),
"tfe_workspace_policy_set": resourceTFEWorkspacePolicySet(),
"tfe_workspace_policy_set_exclusion": resourceTFEWorkspacePolicySetExclusion(),
"tfe_workspace_run": resourceTFEWorkspaceRun(),
"tfe_workspace_variable_set": resourceTFEWorkspaceVariableSet(),
},
ConfigureContextFunc: configure(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package provider

import (
"context"
"errors"
"fmt"
tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"log"
)

func resourceTFEOrganizationDefaultExecutionMode() *schema.Resource {
return &schema.Resource{
Create: resourceTFEOrganizationDefaultExecutionModeCreate,
Read: resourceTFEOrganizationDefaultExecutionModeRead,
Delete: resourceTFEOrganizationDefaultExecutionModeDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceTFEOrganizationDefaultExecutionModeImporter,
},

Schema: map[string]*schema.Schema{
"organization": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"default_execution_mode": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(
[]string{
"agent",
"local",
"remote",
},
false,
),
ForceNew: true,
},

"default_agent_pool_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}

func resourceTFEOrganizationDefaultExecutionModeCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(ConfiguredClient)

// Get the organization name.
organization, err := config.schemaOrDefaultOrganization(d)
if err != nil {
return fmt.Errorf("error getting organization name: %w", err)
}

// If the "default_agent_pool_id" was provided, get the agent pool
var agentPool *tfe.AgentPool
if v, ok := d.GetOk("default_agent_pool_id"); ok && v.(string) != "" {
agentPool = &tfe.AgentPool{
ID: v.(string),
}
}

defaultExecutionMode := ""
if v, ok := d.GetOk("default_execution_mode"); ok {
defaultExecutionMode = v.(string)
} else {
return fmt.Errorf("default_execution_mode was missing from tfstate, please create an issue to report this error")
}

// set organization default execution mode
_, err = config.Client.Organizations.Update(context.Background(), organization, tfe.OrganizationUpdateOptions{
DefaultExecutionMode: tfe.String(defaultExecutionMode),
DefaultAgentPool: agentPool,
})
if err != nil {
return fmt.Errorf("error setting default execution mode of organization %s: %w", d.Id(), err)
}

d.SetId(organization)

return resourceTFEOrganizationDefaultExecutionModeRead(d, meta)
}

func resourceTFEOrganizationDefaultExecutionModeRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(ConfiguredClient)

log.Printf("[DEBUG] Read the organization: %s", d.Id())
organization, err := config.Client.Organizations.Read(ctx, d.Id())
if err != nil {
if errors.Is(err, tfe.ErrResourceNotFound) {
log.Printf("[DEBUG] organization %s no longer exists", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("error reading organization %s: %w", d.Id(), err)
}

defaultExecutionMode := ""
if v, ok := d.GetOk("default_execution_mode"); ok {
defaultExecutionMode = v.(string)
} else {
return fmt.Errorf("default_execution_mode was missing from tfstate, please create an issue to report this error")
}
if organization.DefaultExecutionMode != defaultExecutionMode {
// set id to empty string so that the provider knows it needs to set the default execution mode again
d.SetId("")
}

return nil
}

func resourceTFEOrganizationDefaultExecutionModeDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(ConfiguredClient)

// Get the organization name.
organization, err := config.schemaOrDefaultOrganization(d)
if err != nil {
return fmt.Errorf("error getting organization name: %w", err)
}

log.Printf("[DEBUG] Reseting default execution mode of organization: %s", organization)
// reset organization default execution mode
_, err = config.Client.Organizations.Update(context.Background(), organization, tfe.OrganizationUpdateOptions{
DefaultExecutionMode: tfe.String("remote"),
DefaultAgentPool: nil,
})
if err != nil {
return fmt.Errorf("error updating organization default execution mode: %w", err)
}

return nil
}

func resourceTFEOrganizationDefaultExecutionModeImporter(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(ConfiguredClient)

log.Printf("[DEBUG] Read the organization: %s", d.Id())
organization, err := config.Client.Organizations.Read(ctx, d.Id())
if err != nil {
if errors.Is(err, tfe.ErrResourceNotFound) {
log.Printf("[DEBUG] organization %s no longer exists", d.Id())
d.SetId("")
}
return nil, fmt.Errorf("error reading organization %s: %w", d.Id(), err)
}

// Set the organization field.
d.Set("organization", d.Id())
d.Set("default_execution_mode", organization.DefaultExecutionMode)
if organization.DefaultAgentPool != nil {
d.Set("default_agent_pool_id", organization.DefaultAgentPool.ID)
}

return []*schema.ResourceData{d}, nil
}
Loading