Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions examples/hosted_runner/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ resource "github_actions_hosted_runner" "advanced" {
source = "github"
}

size = "8-core"
runner_group_id = github_actions_runner_group.example.id
maximum_runners = 10
enable_static_ip = true
size = "8-core"
runner_group_id = github_actions_runner_group.example.id
maximum_runners = 10
public_ip_enabled = true
}
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func Provider() *schema.Provider {
"github_organization_block": resourceOrganizationBlock(),
"github_organization_custom_role": resourceGithubOrganizationCustomRole(),
"github_organization_custom_properties": resourceGithubOrganizationCustomProperties(),
"github_organization_network_configuration": resourceGithubOrganizationNetworkConfiguration(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_repository_role": resourceGithubOrganizationRepositoryRole(),
"github_organization_role": resourceGithubOrganizationRole(),
Expand Down
223 changes: 145 additions & 78 deletions github/resource_github_actions_runner_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

type organizationRunnerGroup struct {
NetworkConfigurationID *string `json:"network_configuration_id,omitempty"`
}

func resourceGithubActionsRunnerGroup() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsRunnerGroupCreate,
Expand Down Expand Up @@ -92,10 +96,115 @@ func resourceGithubActionsRunnerGroup() *schema.Resource {
Optional: true,
Description: "List of workflows the runner group should be allowed to run. This setting will be ignored unless restricted_to_workflows is set to 'true'.",
},
"network_configuration_id": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 255),
Description: "The identifier of the hosted compute network configuration to associate with this runner group for GitHub-hosted private networking.",
},
},
}
}

func getOrganizationRunnerGroupNetworking(client *github.Client, ctx context.Context, org string, groupID int64) (*organizationRunnerGroup, *github.Response, error) {
req, err := client.NewRequest("GET", fmt.Sprintf("orgs/%s/actions/runner-groups/%d", org, groupID), nil)
if err != nil {
return nil, nil, err
}

var runnerGroup organizationRunnerGroup
resp, err := client.Do(ctx, req, &runnerGroup)
if err != nil {
return nil, resp, err
}

return &runnerGroup, resp, nil
}

func getOrganizationRunnerGroup(client *github.Client, ctx context.Context, org string, groupID int64) (*github.RunnerGroup, *github.Response, error) {
runnerGroup, resp, err := client.Actions.GetOrganizationRunnerGroup(ctx, org, groupID)
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) && ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotModified {
// ignore error StatusNotModified
return runnerGroup, resp, nil
}
}
return runnerGroup, resp, err
}

func updateOrganizationRunnerGroupNetworking(client *github.Client, ctx context.Context, org string, groupID int64, networkConfigurationID *string) (*github.Response, error) {
payload := map[string]any{
"network_configuration_id": networkConfigurationID,
}

req, err := client.NewRequest("PATCH", fmt.Sprintf("orgs/%s/actions/runner-groups/%d", org, groupID), payload)
if err != nil {
return nil, err
}

resp, err := client.Do(ctx, req, nil)
if err != nil {
return resp, err
}

return resp, nil
}

func setGithubActionsRunnerGroupNetworkingState(d *schema.ResourceData, runnerGroup *organizationRunnerGroup) error {
if runnerGroup != nil && runnerGroup.NetworkConfigurationID != nil && *runnerGroup.NetworkConfigurationID != "" {
if err := d.Set("network_configuration_id", *runnerGroup.NetworkConfigurationID); err != nil {
return err
}
} else {
if err := d.Set("network_configuration_id", nil); err != nil {
return err
}
}
return nil
}

func setGithubActionsRunnerGroupState(d *schema.ResourceData, runnerGroup *github.RunnerGroup, etag string, selectedRepositoryIDs []int64) error {
if err := d.Set("etag", etag); err != nil {
return err
}
if err := d.Set("allows_public_repositories", runnerGroup.GetAllowsPublicRepositories()); err != nil {
return err
}
if err := d.Set("default", runnerGroup.GetDefault()); err != nil {
return err
}
if err := d.Set("id", strconv.FormatInt(runnerGroup.GetID(), 10)); err != nil {
return err
}
if err := d.Set("inherited", runnerGroup.GetInherited()); err != nil {
return err
}
if err := d.Set("name", runnerGroup.GetName()); err != nil {
return err
}
if err := d.Set("runners_url", runnerGroup.GetRunnersURL()); err != nil {
return err
}
if err := d.Set("selected_repositories_url", runnerGroup.GetSelectedRepositoriesURL()); err != nil {
return err
}
if err := d.Set("visibility", runnerGroup.GetVisibility()); err != nil {
return err
}
if err := d.Set("selected_repository_ids", selectedRepositoryIDs); err != nil {
return err
}
if err := d.Set("restricted_to_workflows", runnerGroup.GetRestrictedToWorkflows()); err != nil {
return err
}
if err := d.Set("selected_workflows", runnerGroup.SelectedWorkflows); err != nil {
return err
}

return nil
}

func resourceGithubActionsRunnerGroupCreate(d *schema.ResourceData, meta any) error {
err := checkOrganization(meta)
if err != nil {
Expand Down Expand Up @@ -148,57 +257,28 @@ func resourceGithubActionsRunnerGroupCreate(d *schema.ResourceData, meta any) er
return err
}
d.SetId(strconv.FormatInt(runnerGroup.GetID(), 10))
if err = d.Set("etag", resp.Header.Get("ETag")); err != nil {
return err
}
if err = d.Set("allows_public_repositories", runnerGroup.GetAllowsPublicRepositories()); err != nil {
return err
}
if err = d.Set("default", runnerGroup.GetDefault()); err != nil {
return err
}

if err = d.Set("id", strconv.FormatInt(runnerGroup.GetID(), 10)); err != nil {
return err
}
if err = d.Set("inherited", runnerGroup.GetInherited()); err != nil {
return err
}
if err = d.Set("name", runnerGroup.GetName()); err != nil {
return err
}
if err = d.Set("runners_url", runnerGroup.GetRunnersURL()); err != nil {
return err
}
if err = d.Set("selected_repositories_url", runnerGroup.GetSelectedRepositoriesURL()); err != nil {
return err
}
if err = d.Set("visibility", runnerGroup.GetVisibility()); err != nil {
return err
}
if err = d.Set("selected_repository_ids", selectedRepositoryIDs); err != nil { // Note: runnerGroup has no method to get selected repository IDs
return err
}
if err = d.Set("restricted_to_workflows", runnerGroup.GetRestrictedToWorkflows()); err != nil {
return err
}
if err = d.Set("selected_workflows", runnerGroup.SelectedWorkflows); err != nil {
if err = setGithubActionsRunnerGroupState(d, runnerGroup, resp.Header.Get("ETag"), selectedRepositoryIDs); err != nil {
return err
}

return resourceGithubActionsRunnerGroupRead(d, meta)
}
if networkConfigurationID, ok := d.GetOk("network_configuration_id"); ok {
networkConfigurationIDValue := networkConfigurationID.(string)
// The create endpoint does not accept network_configuration_id, so private networking
// must be attached with a follow-up PATCH after the runner group has been created.
if _, err = updateOrganizationRunnerGroupNetworking(client, ctx, orgName, runnerGroup.GetID(), &networkConfigurationIDValue); err != nil {
return err
}

func getOrganizationRunnerGroup(client *github.Client, ctx context.Context, org string, groupID int64) (*github.RunnerGroup, *github.Response, error) {
runnerGroup, resp, err := client.Actions.GetOrganizationRunnerGroup(ctx, org, groupID)
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
// ignore error StatusNotModified
return runnerGroup, resp, nil
if err = setGithubActionsRunnerGroupNetworkingState(d, &organizationRunnerGroup{NetworkConfigurationID: &networkConfigurationIDValue}); err != nil {
return err
}
} else {
if err = setGithubActionsRunnerGroupNetworkingState(d, nil); err != nil {
return err
}
}
return runnerGroup, resp, err

return nil
}

func resourceGithubActionsRunnerGroupRead(d *schema.ResourceData, meta any) error {
Expand All @@ -223,7 +303,7 @@ func resourceGithubActionsRunnerGroupRead(d *schema.ResourceData, meta any) erro
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
if ghErr.Response.StatusCode == http.StatusNotFound {
if ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[INFO] Removing organization runner group %s/%s from state because it no longer exists in GitHub",
orgName, d.Id())
d.SetId("")
Expand All @@ -237,38 +317,10 @@ func resourceGithubActionsRunnerGroupRead(d *schema.ResourceData, meta any) erro
if runnerGroup == nil {
return nil
}
runnerGroupEtag := resp.Header.Get("ETag")

if err = d.Set("etag", resp.Header.Get("ETag")); err != nil {
return err
}
if err = d.Set("allows_public_repositories", runnerGroup.GetAllowsPublicRepositories()); err != nil {
return err
}
if err = d.Set("default", runnerGroup.GetDefault()); err != nil {
return err
}
if err = d.Set("id", strconv.FormatInt(runnerGroup.GetID(), 10)); err != nil {
return err
}
if err = d.Set("inherited", runnerGroup.GetInherited()); err != nil {
return err
}
if err = d.Set("name", runnerGroup.GetName()); err != nil {
return err
}
if err = d.Set("runners_url", runnerGroup.GetRunnersURL()); err != nil {
return err
}
if err = d.Set("selected_repositories_url", runnerGroup.GetSelectedRepositoriesURL()); err != nil {
return err
}
if err = d.Set("visibility", runnerGroup.GetVisibility()); err != nil {
return err
}
if err = d.Set("restricted_to_workflows", runnerGroup.GetRestrictedToWorkflows()); err != nil {
return err
}
if err = d.Set("selected_workflows", runnerGroup.SelectedWorkflows); err != nil {
runnerGroupNetworking, _, err := getOrganizationRunnerGroupNetworking(client, context.WithValue(context.Background(), ctxId, d.Id()), orgName, runnerGroupID)
if err != nil {
return err
}

Expand All @@ -294,7 +346,10 @@ func resourceGithubActionsRunnerGroupRead(d *schema.ResourceData, meta any) erro
options.Page = resp.NextPage
}

if err = d.Set("selected_repository_ids", selectedRepositoryIDs); err != nil {
if err = setGithubActionsRunnerGroupState(d, runnerGroup, runnerGroupEtag, selectedRepositoryIDs); err != nil {
return err
}
if err = setGithubActionsRunnerGroupNetworkingState(d, runnerGroupNetworking); err != nil {
return err
}

Expand Down Expand Up @@ -339,6 +394,18 @@ func resourceGithubActionsRunnerGroupUpdate(d *schema.ResourceData, meta any) er
return err
}

if d.HasChange("network_configuration_id") {
var networkConfigurationIDValue *string
if networkConfigurationID, ok := d.GetOk("network_configuration_id"); ok {
value := networkConfigurationID.(string)
networkConfigurationIDValue = &value
}

if _, err := updateOrganizationRunnerGroupNetworking(client, ctx, orgName, runnerGroupID, networkConfigurationIDValue); err != nil {
return err
}
}

selectedRepositories, hasSelectedRepositories := d.GetOk("selected_repository_ids")
selectedRepositoryIDs := []int64{}

Expand Down
Loading