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

New Resource: github_actions_runner_group #821

Merged
merged 12 commits into from
Sep 21, 2021
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func Provider() terraform.ResourceProvider {
"github_actions_environment_secret": resourceGithubActionsEnvironmentSecret(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_organization_secret_repositories": resourceGithubActionsOrganizationSecretRepositories(),
"github_actions_runner_group": resourceGithubActionsRunnerGroup(),
"github_actions_secret": resourceGithubActionsSecret(),
"github_app_installation_repository": resourceGithubAppInstallationRepository(),
"github_branch": resourceGithubBranch(),
Expand Down
232 changes: 232 additions & 0 deletions github/resource_github_actions_runner_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package github

import (
"context"
"fmt"
"log"
"net/http"
"strconv"

"github.com/google/go-github/v38/github"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceGithubActionsRunnerGroup() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsRunnerGroupCreate,
Read: resourceGithubActionsRunnerGroupRead,
Update: resourceGithubActionsRunnerGroupUpdate,
Delete: resourceGithubActionsRunnerGroupDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"allows_public_repositories": {
Type: schema.TypeBool,
Computed: true,
},
"default": {
Type: schema.TypeBool,
Computed: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"inherited": {
Type: schema.TypeBool,
Computed: true,
},
"name": {
Type: schema.TypeString,
Required: true,
},
"runners_url": {
Type: schema.TypeString,
Computed: true,
},
"selected_repository_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Set: schema.HashInt,
Optional: true,
},
"selected_repositories_url": {
Type: schema.TypeString,
Computed: true,
},
"visibility": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"all", "selected", "private"}, false),
},
},
}
}

func resourceGithubActionsRunnerGroupCreate(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
name := d.Get("name").(string)
visibility := d.Get("visibility").(string)
selectedRepositories, hasSelectedRepositories := d.GetOk("selected_repository_ids")

if visibility != "selected" && hasSelectedRepositories {
return fmt.Errorf("Cannot use selected_repository_ids without visibility being set to selected")
}

selectedRepositoryIDs := []int64{}

if hasSelectedRepositories {
ids := selectedRepositories.(*schema.Set).List()

for _, id := range ids {
selectedRepositoryIDs = append(selectedRepositoryIDs, int64(id.(int)))
}
}

ctx := context.Background()

log.Printf("[DEBUG] Creating organization runner group: %s (%s)", name, orgName)
runnerGroup, resp, err := client.Actions.CreateOrganizationRunnerGroup(ctx,
orgName,
github.CreateRunnerGroupRequest{
Name: &name,
Visibility: &visibility,
SelectedRepositoryIDs: selectedRepositoryIDs,
},
)
if err != nil {
return err
}
d.SetId(strconv.FormatInt(runnerGroup.GetID(), 10))
d.Set("etag", resp.Header.Get("ETag"))
d.Set("allows_public_repositories", runnerGroup.GetAllowsPublicRepositories())
d.Set("default", runnerGroup.GetDefault())
d.Set("id", runnerGroup.GetID())
d.Set("inherited", runnerGroup.GetInherited())
d.Set("name", runnerGroup.GetName())
d.Set("runners_url", runnerGroup.GetRunnersURL())
d.Set("selected_repositories_url", runnerGroup.GetSelectedRepositoriesURL())
d.Set("visibility", runnerGroup.GetVisibility())
d.Set("selected_repository_ids", selectedRepositoryIDs) // Note: runnerGroup has no method to get selected repository IDs

return resourceGithubActionsRunnerGroupRead(d, meta)
}

func resourceGithubActionsRunnerGroupRead(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name

runnerGroupID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), ctxId, d.Id())
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
}

log.Printf("[DEBUG] Reading organization runner group: %s (%s)", d.Id(), orgName)
runnerGroup, resp, err := client.Actions.GetOrganizationRunnerGroup(ctx, orgName, runnerGroupID)
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok {
if ghErr.Response.StatusCode == http.StatusNotModified {
return nil
}
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing organization runner group %s/%s from state because it no longer exists in GitHub",
orgName, d.Id())
d.SetId("")
return nil
}
}
return err
}

d.Set("etag", resp.Header.Get("ETag"))
d.Set("allows_public_repositories", runnerGroup.GetAllowsPublicRepositories())
d.Set("default", runnerGroup.GetDefault())
d.Set("id", runnerGroup.GetID())
d.Set("inherited", runnerGroup.GetInherited())
d.Set("name", runnerGroup.GetName())
d.Set("runners_url", runnerGroup.GetRunnersURL())
d.Set("selected_repositories_url", runnerGroup.GetSelectedRepositoriesURL())
d.Set("visibility", runnerGroup.GetVisibility())

log.Printf("[DEBUG] Reading organization runner group repositories: %s (%s)", d.Id(), orgName)
runnerGroupRepositories, resp, err := client.Actions.ListRepositoryAccessRunnerGroup(ctx, orgName, runnerGroupID)
jcudit marked this conversation as resolved.
Show resolved Hide resolved

selectedRepositoryIDs := []int64{}
for _, repo := range runnerGroupRepositories.Repositories {
selectedRepositoryIDs = append(selectedRepositoryIDs, *repo.ID)
}
log.Printf("[DEBUG] Got selected_repository_ids: %v", selectedRepositoryIDs)
d.Set("selected_repository_ids", selectedRepositoryIDs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not see a way to get the selected_repository_ids without making this additional API call.


return nil
}

func resourceGithubActionsRunnerGroupUpdate(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name

name := d.Get("name").(string)
visibility := d.Get("visibility").(string)

options := github.UpdateRunnerGroupRequest{
Name: &name,
Visibility: &visibility,
}

runnerGroupID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), ctxId, d.Id())

log.Printf("[DEBUG] Updating organization runner group: %s (%s)", d.Id(), orgName)
if _, _, err := client.Actions.UpdateOrganizationRunnerGroup(ctx, orgName, runnerGroupID, options); err != nil {
return err
}

return resourceGithubActionsRunnerGroupRead(d, meta)
}

func resourceGithubActionsRunnerGroupDelete(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
runnerGroupID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return err
}
ctx := context.WithValue(context.Background(), ctxId, d.Id())

log.Printf("[DEBUG] Deleting organization runner group: %s (%s)", d.Id(), orgName)
_, err = client.Actions.DeleteOrganizationRunnerGroup(ctx, orgName, runnerGroupID)
return err
}
Loading