diff --git a/github/data_source_github_actions_hosted_runner_custom_image.go b/github/data_source_github_actions_hosted_runner_custom_image.go new file mode 100644 index 0000000000..29c3651bfc --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_image.go @@ -0,0 +1,122 @@ +package github + +import ( + "context" + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubActionsHostedRunnerCustomImage() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceGithubActionsHostedRunnerCustomImageRead, + + Schema: map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeInt, + Required: true, + Description: "The custom image definition ID.", + }, + "platform": { + Type: schema.TypeString, + Computed: true, + Description: "Platform of the image (e.g., linux-x64).", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the custom image.", + }, + "source": { + Type: schema.TypeString, + Computed: true, + Description: "Source of the image.", + }, + "versions_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of versions of this image.", + }, + "total_versions_size": { + Type: schema.TypeInt, + Computed: true, + Description: "Total size of all versions in GB.", + }, + "latest_version": { + Type: schema.TypeString, + Computed: true, + Description: "Latest version string.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the image (e.g., Ready).", + }, + }, + } +} + +func dataSourceGithubActionsHostedRunnerCustomImageRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + err := checkOrganization(meta) + if err != nil { + return diag.FromErr(err) + } + + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + imageID := d.Get("image_id").(int) + + req, err := client.NewRequest("GET", fmt.Sprintf("orgs/%s/actions/hosted-runners/images/custom/%d", orgName, imageID), nil) + if err != nil { + return diag.FromErr(err) + } + + var image map[string]any + _, err = client.Do(ctx, req, &image) + if err != nil { + return diag.FromErr(err) + } + + if id, ok := image["id"].(float64); ok { + d.SetId(strconv.FormatInt(int64(id), 10)) + } + if v, ok := image["platform"].(string); ok { + if err := d.Set("platform", v); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["name"].(string); ok { + if err := d.Set("name", v); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["source"].(string); ok { + if err := d.Set("source", v); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["versions_count"].(float64); ok { + if err := d.Set("versions_count", int(v)); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["total_versions_size"].(float64); ok { + if err := d.Set("total_versions_size", int(v)); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["latest_version"].(string); ok { + if err := d.Set("latest_version", v); err != nil { + return diag.FromErr(err) + } + } + if v, ok := image["state"].(string); ok { + if err := d.Set("state", v); err != nil { + return diag.FromErr(err) + } + } + + return nil +} diff --git a/github/data_source_github_actions_hosted_runner_custom_image_test.go b/github/data_source_github_actions_hosted_runner_custom_image_test.go new file mode 100644 index 0000000000..dced8a4aa4 --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_image_test.go @@ -0,0 +1,47 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccGithubActionsHostedRunnerCustomImageDataSource(t *testing.T) { + t.Run("queries a single custom image for hosted runners", func(t *testing.T) { + // This test requires a custom image to exist in the org. + // Set GITHUB_HOSTED_RUNNER_CUSTOM_IMAGE_ID env var to a valid image ID. + imageID := testAccGetCustomImageID(t) + + config := fmt.Sprintf(` + data "github_actions_hosted_runner_custom_image" "test" { + image_id = %s + } + `, imageID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_actions_hosted_runner_custom_image.test", "name"), + resource.TestCheckResourceAttrSet("data.github_actions_hosted_runner_custom_image.test", "platform"), + resource.TestCheckResourceAttrSet("data.github_actions_hosted_runner_custom_image.test", "state"), + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + }) +} + +func testAccGetCustomImageID(t *testing.T) string { + t.Helper() + id := testOrganization + "_CUSTOM_IMAGE_ID" + // For CI, we'd look up from env. For now, skip if not available. + t.Skipf("Skipping: requires a custom image in the org (set %s)", id) + return "" +} diff --git a/github/data_source_github_actions_hosted_runner_custom_image_versions.go b/github/data_source_github_actions_hosted_runner_custom_image_versions.go new file mode 100644 index 0000000000..31704bbd92 --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_image_versions.go @@ -0,0 +1,103 @@ +package github + +import ( + "context" + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubActionsHostedRunnerCustomImageVersions() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceGithubActionsHostedRunnerCustomImageVersionsRead, + + Schema: map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeInt, + Required: true, + Description: "The custom image definition ID.", + }, + "versions": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "version": { + Type: schema.TypeString, + Computed: true, + Description: "Version string (e.g., 1.0.0).", + }, + "size_gb": { + Type: schema.TypeInt, + Computed: true, + Description: "Size of the image version in GB.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the version (e.g., Ready).", + }, + "created_on": { + Type: schema.TypeString, + Computed: true, + Description: "Timestamp when the version was created.", + }, + }, + }, + Description: "List of versions for this custom image.", + }, + }, + } +} + +func dataSourceGithubActionsHostedRunnerCustomImageVersionsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + err := checkOrganization(meta) + if err != nil { + return diag.FromErr(err) + } + + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + imageID := d.Get("image_id").(int) + + req, err := client.NewRequest("GET", fmt.Sprintf("orgs/%s/actions/hosted-runners/images/custom/%d/versions", orgName, imageID), nil) + if err != nil { + return diag.FromErr(err) + } + + var result struct { + TotalCount int `json:"total_count"` + ImageVersions []map[string]any `json:"image_versions"` + } + _, err = client.Do(ctx, req, &result) + if err != nil { + return diag.FromErr(err) + } + + var allVersions []map[string]any + for _, v := range result.ImageVersions { + m := map[string]any{} + if ver, ok := v["version"].(string); ok { + m["version"] = ver + } + if size, ok := v["size_gb"].(float64); ok { + m["size_gb"] = int(size) + } + if state, ok := v["state"].(string); ok { + m["state"] = state + } + if created, ok := v["created_on"].(string); ok { + m["created_on"] = created + } + allVersions = append(allVersions, m) + } + + d.SetId(fmt.Sprintf("%s/%s", orgName, strconv.Itoa(imageID))) + if err := d.Set("versions", allVersions); err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/github/data_source_github_actions_hosted_runner_custom_image_versions_test.go b/github/data_source_github_actions_hosted_runner_custom_image_versions_test.go new file mode 100644 index 0000000000..48ff963ecf --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_image_versions_test.go @@ -0,0 +1,42 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccGithubActionsHostedRunnerCustomImageVersionsDataSource(t *testing.T) { + t.Run("queries versions of a custom image for hosted runners", func(t *testing.T) { + imageID := testAccGetCustomImageIDForVersions(t) + + config := fmt.Sprintf(` + data "github_actions_hosted_runner_custom_image_versions" "test" { + image_id = %s + } + `, imageID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_actions_hosted_runner_custom_image_versions.test", "versions.#"), + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + }) +} + +func testAccGetCustomImageIDForVersions(t *testing.T) string { + t.Helper() + id := testOrganization + "_CUSTOM_IMAGE_ID" + t.Skipf("Skipping: requires a custom image in the org (set %s)", id) + return "" +} diff --git a/github/data_source_github_actions_hosted_runner_custom_images.go b/github/data_source_github_actions_hosted_runner_custom_images.go new file mode 100644 index 0000000000..e251cac69d --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_images.go @@ -0,0 +1,129 @@ +package github + +import ( + "context" + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubActionsHostedRunnerCustomImages() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceGithubActionsHostedRunnerCustomImagesRead, + + Schema: map[string]*schema.Schema{ + "images": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The custom image definition ID.", + }, + "platform": { + Type: schema.TypeString, + Computed: true, + Description: "Platform of the image (e.g., linux-x64).", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the custom image.", + }, + "source": { + Type: schema.TypeString, + Computed: true, + Description: "Source of the image.", + }, + "versions_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of versions of this image.", + }, + "total_versions_size": { + Type: schema.TypeInt, + Computed: true, + Description: "Total size of all versions in GB.", + }, + "latest_version": { + Type: schema.TypeString, + Computed: true, + Description: "Latest version string.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the image (e.g., Ready).", + }, + }, + }, + Description: "List of custom images for GitHub-hosted runners.", + }, + }, + } +} + +func dataSourceGithubActionsHostedRunnerCustomImagesRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + err := checkOrganization(meta) + if err != nil { + return diag.FromErr(err) + } + + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + + req, err := client.NewRequest("GET", fmt.Sprintf("orgs/%s/actions/hosted-runners/images/custom", orgName), nil) + if err != nil { + return diag.FromErr(err) + } + + var result struct { + TotalCount int `json:"total_count"` + ImageVersions []map[string]any `json:"image_versions"` + } + _, err = client.Do(ctx, req, &result) + if err != nil { + return diag.FromErr(err) + } + + var allImages []map[string]any + for _, img := range result.ImageVersions { + m := map[string]any{} + if id, ok := img["id"].(float64); ok { + m["id"] = strconv.FormatInt(int64(id), 10) + } + if v, ok := img["platform"].(string); ok { + m["platform"] = v + } + if v, ok := img["name"].(string); ok { + m["name"] = v + } + if v, ok := img["source"].(string); ok { + m["source"] = v + } + if v, ok := img["versions_count"].(float64); ok { + m["versions_count"] = int(v) + } + if v, ok := img["total_versions_size"].(float64); ok { + m["total_versions_size"] = int(v) + } + if v, ok := img["latest_version"].(string); ok { + m["latest_version"] = v + } + if v, ok := img["state"].(string); ok { + m["state"] = v + } + allImages = append(allImages, m) + } + + d.SetId(orgName) + if err := d.Set("images", allImages); err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/github/data_source_github_actions_hosted_runner_custom_images_test.go b/github/data_source_github_actions_hosted_runner_custom_images_test.go new file mode 100644 index 0000000000..6aac8bd70d --- /dev/null +++ b/github/data_source_github_actions_hosted_runner_custom_images_test.go @@ -0,0 +1,31 @@ +package github + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccGithubActionsHostedRunnerCustomImagesDataSource(t *testing.T) { + t.Run("queries custom images for hosted runners", func(t *testing.T) { + config := ` + data "github_actions_hosted_runner_custom_images" "test" { + } + ` + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_actions_hosted_runner_custom_images.test", "images.#"), + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + }) +} diff --git a/github/provider.go b/github/provider.go index 2d5d6dc33a..f1f94e7c1c 100644 --- a/github/provider.go +++ b/github/provider.go @@ -224,6 +224,9 @@ func Provider() *schema.Provider { "github_actions_environment_public_key": dataSourceGithubActionsEnvironmentPublicKey(), "github_actions_environment_secrets": dataSourceGithubActionsEnvironmentSecrets(), "github_actions_environment_variables": dataSourceGithubActionsEnvironmentVariables(), + "github_actions_hosted_runner_custom_image": dataSourceGithubActionsHostedRunnerCustomImage(), + "github_actions_hosted_runner_custom_images": dataSourceGithubActionsHostedRunnerCustomImages(), + "github_actions_hosted_runner_custom_image_versions": dataSourceGithubActionsHostedRunnerCustomImageVersions(), "github_actions_organization_oidc_subject_claim_customization_template": dataSourceGithubActionsOrganizationOIDCSubjectClaimCustomizationTemplate(), "github_actions_organization_public_key": dataSourceGithubActionsOrganizationPublicKey(), "github_actions_organization_registration_token": dataSourceGithubActionsOrganizationRegistrationToken(), diff --git a/website/docs/d/actions_hosted_runner_custom_image.html.markdown b/website/docs/d/actions_hosted_runner_custom_image.html.markdown new file mode 100644 index 0000000000..421212d9a8 --- /dev/null +++ b/website/docs/d/actions_hosted_runner_custom_image.html.markdown @@ -0,0 +1,40 @@ +--- +layout: "github" +page_title: "GitHub: github_actions_hosted_runner_custom_image" +description: |- + Get a single custom image definition for GitHub-hosted runners +--- + +# github_actions_hosted_runner_custom_image + +Use this data source to retrieve details of a specific custom image for GitHub-hosted runners in your organization. + +## Example Usage + +```hcl +data "github_actions_hosted_runner_custom_image" "my_image" { + image_id = 123 +} + +output "image_name" { + value = data.github_actions_hosted_runner_custom_image.my_image.name +} + +output "image_state" { + value = data.github_actions_hosted_runner_custom_image.my_image.state +} +``` + +## Argument Reference + +* `image_id` - (Required) The custom image definition ID. + +## Attributes Reference + +* `platform` - Platform of the image (e.g., `linux-x64`). +* `name` - Name of the custom image. +* `source` - Source of the image. +* `versions_count` - Number of versions of this image. +* `total_versions_size` - Total size of all versions in GB. +* `latest_version` - Latest version string. +* `state` - State of the image (e.g., `Ready`). diff --git a/website/docs/d/actions_hosted_runner_custom_image_versions.html.markdown b/website/docs/d/actions_hosted_runner_custom_image_versions.html.markdown new file mode 100644 index 0000000000..c6906d1e15 --- /dev/null +++ b/website/docs/d/actions_hosted_runner_custom_image_versions.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "github" +page_title: "GitHub: github_actions_hosted_runner_custom_image_versions" +description: |- + Get a list of versions for a custom image for GitHub-hosted runners +--- + +# github_actions_hosted_runner_custom_image_versions + +Use this data source to retrieve all versions of a specific custom image for GitHub-hosted runners in your organization. + +## Example Usage + +```hcl +data "github_actions_hosted_runner_custom_image_versions" "my_image_versions" { + image_id = 123 +} + +output "versions" { + value = data.github_actions_hosted_runner_custom_image_versions.my_image_versions.versions +} +``` + +## Argument Reference + +* `image_id` - (Required) The custom image definition ID. + +## Attributes Reference + +* `versions` - A list of image versions. Each version has the following attributes: + * `version` - Version string (e.g., `1.0.0`). + * `size_gb` - Size of the image version in GB. + * `state` - State of the version (e.g., `Ready`). + * `created_on` - Timestamp when the version was created. diff --git a/website/docs/d/actions_hosted_runner_custom_images.html.markdown b/website/docs/d/actions_hosted_runner_custom_images.html.markdown new file mode 100644 index 0000000000..97bd4cf4d2 --- /dev/null +++ b/website/docs/d/actions_hosted_runner_custom_images.html.markdown @@ -0,0 +1,33 @@ +--- +layout: "github" +page_title: "GitHub: github_actions_hosted_runner_custom_images" +description: |- + Get a list of custom images for GitHub-hosted runners in an organization +--- + +# github_actions_hosted_runner_custom_images + +Use this data source to retrieve a list of custom images available for GitHub-hosted runners in your organization. + +## Example Usage + +```hcl +data "github_actions_hosted_runner_custom_images" "all" { +} + +output "custom_images" { + value = data.github_actions_hosted_runner_custom_images.all.images +} +``` + +## Attributes Reference + +* `images` - A list of custom images. Each image has the following attributes: + * `id` - The custom image definition ID. + * `platform` - Platform of the image (e.g., `linux-x64`). + * `name` - Name of the custom image. + * `source` - Source of the image. + * `versions_count` - Number of versions of this image. + * `total_versions_size` - Total size of all versions in GB. + * `latest_version` - Latest version string. + * `state` - State of the image (e.g., `Ready`).