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

Add in github_actions_organization_secret #584

Merged
merged 1 commit into from
Nov 23, 2020
Merged
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
5 changes: 5 additions & 0 deletions github/data_source_github_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ func dataSourceGithubRepository() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"repo_id": {
Type: schema.TypeInt,
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -158,6 +162,7 @@ func dataSourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) er
d.Set("http_clone_url", repo.GetCloneURL())
d.Set("archived", repo.GetArchived())
d.Set("node_id", repo.GetNodeID())
d.Set("repo_id", repo.GetID())

err = d.Set("topics", flattenStringList(repo.Topics))
if err != nil {
Expand Down
46 changes: 23 additions & 23 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,29 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"github_actions_secret": resourceGithubActionsSecret(),
"github_branch": resourceGithubBranch(),
"github_branch_protection": resourceGithubBranchProtection(),
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_webhook": resourceGithubOrganizationWebhook(),
"github_project_column": resourceGithubProjectColumn(),
"github_repository_collaborator": resourceGithubRepositoryCollaborator(),
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_project": resourceGithubRepositoryProject(),
"github_repository_webhook": resourceGithubRepositoryWebhook(),
"github_repository": resourceGithubRepository(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_team_sync_group_mapping": resourceGithubTeamSyncGroupMapping(),
"github_team": resourceGithubTeam(),
"github_user_gpg_key": resourceGithubUserGpgKey(),
"github_user_invitation_accepter": resourceGithubUserInvitationAccepter(),
"github_user_ssh_key": resourceGithubUserSshKey(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_secret": resourceGithubActionsSecret(),
"github_branch": resourceGithubBranch(),
"github_branch_protection": resourceGithubBranchProtection(),
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_webhook": resourceGithubOrganizationWebhook(),
"github_project_column": resourceGithubProjectColumn(),
"github_repository_collaborator": resourceGithubRepositoryCollaborator(),
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_project": resourceGithubRepositoryProject(),
"github_repository_webhook": resourceGithubRepositoryWebhook(),
"github_repository": resourceGithubRepository(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_team_sync_group_mapping": resourceGithubTeamSyncGroupMapping(),
"github_team": resourceGithubTeam(),
"github_user_gpg_key": resourceGithubUserGpgKey(),
"github_user_invitation_accepter": resourceGithubUserInvitationAccepter(),
"github_user_ssh_key": resourceGithubUserSshKey(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down Expand Up @@ -107,7 +108,6 @@ func init() {

func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
return func(d *schema.ResourceData) (interface{}, error) {

anonymous := true
if d.Get("token").(string) != "" {
anonymous = false
Expand Down
159 changes: 159 additions & 0 deletions github/resource_github_actions_organization_secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package github

import (
"context"
"encoding/base64"
"fmt"
"log"
"net/http"

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

func resourceGithubActionsOrganizationSecret() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsOrganizationSecretCreateOrUpdate,
Read: resourceGithubActionsOrganizationSecretRead,
Update: resourceGithubActionsOrganizationSecretCreateOrUpdate,
Delete: resourceGithubActionsOrganizationSecretDelete,

Schema: map[string]*schema.Schema{
"secret_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"plaintext_value": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Sensitive: true,
},
"visibility": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateValueFunc([]string{"all", "private", "selected"}),
ForceNew: true,
},
"selected_repository_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Set: schema.HashInt,
Optional: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceGithubActionsOrganizationSecretCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
owner := meta.(*Owner).name
ctx := context.Background()

secretName := d.Get("secret_name").(string)
plaintextValue := d.Get("plaintext_value").(string)

visibility := d.Get("visibility").(string)
selectedRepositories, hasSelectedRepositories := d.GetOk("selected_repository_ids")

if visibility == "selected" && !hasSelectedRepositories {
return fmt.Errorf("Cannot use visbility set to selected without selected_repository_ids")
} else 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)))
}
}

keyId, publicKey, err := getOrganizationPublicKeyDetails(owner, meta)
if err != nil {
return err
}

encryptedText, err := encryptPlaintext(plaintextValue, publicKey)
if err != nil {
return err
}

// Create an EncryptedSecret and encrypt the plaintext value into it
eSecret := &github.EncryptedSecret{
Name: secretName,
KeyID: keyId,
Visibility: visibility,
SelectedRepositoryIDs: selectedRepositoryIDs,
EncryptedValue: base64.StdEncoding.EncodeToString(encryptedText),
}

_, err = client.Actions.CreateOrUpdateOrgSecret(ctx, owner, eSecret)
if err != nil {
return err
}

d.SetId(secretName)
return resourceGithubActionsOrganizationSecretRead(d, meta)
}

func resourceGithubActionsOrganizationSecretRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
owner := meta.(*Owner).name
ctx := context.Background()

secret, _, err := client.Actions.GetOrgSecret(ctx, owner, d.Id())
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok {
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing actions secret %s from state because it no longer exists in GitHub",
d.Id())
d.SetId("")
return nil
}
}
return err
}

d.Set("plaintext_value", d.Get("plaintext_value"))
d.Set("updated_at", secret.UpdatedAt.String())
d.Set("created_at", secret.CreatedAt.String())

return nil
}

func resourceGithubActionsOrganizationSecretDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.WithValue(context.Background(), ctxId, d.Id())

log.Printf("[DEBUG] Deleting secret: %s", d.Id())
_, err := client.Actions.DeleteOrgSecret(ctx, orgName, d.Id())
return err
}

func getOrganizationPublicKeyDetails(owner string, meta interface{}) (keyId, pkValue string, err error) {
client := meta.(*Owner).v3client
ctx := context.Background()

publicKey, _, err := client.Actions.GetOrgPublicKey(ctx, owner)
if err != nil {
return keyId, pkValue, err
}

return publicKey.GetKeyID(), publicKey.GetKey(), err
}
119 changes: 119 additions & 0 deletions github/resource_github_actions_organization_secret_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package github

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccGithubActionsOrganizationSecret(t *testing.T) {
t.Run("creates and updates secrets without error", func(t *testing.T) {
secretValue := "super_secret_value"
updatedSecretValue := "updated_super_secret_value"

config := fmt.Sprintf(`
resource "github_actions_organization_secret" "test_secret" {
secret_name = "test_secret_name"
plaintext_value = "%s"
visibility = "private"
}
`, secretValue)

checks := map[string]resource.TestCheckFunc{
"before": resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_secret.test_secret", "plaintext_value",
secretValue,
),
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret.test_secret", "created_at",
),
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret.test_secret", "updated_at",
),
),
"after": resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_secret.test_secret", "plaintext_value",
updatedSecretValue,
),
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret.test_secret", "created_at",
),
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret.test_secret", "updated_at",
),
),
}

testCase := func(t *testing.T, mode string) {
resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessMode(t, mode) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: checks["before"],
},
{
Config: strings.Replace(config,
secretValue,
updatedSecretValue, 1),
Check: checks["after"],
},
},
})
}

t.Run("with an anonymous account", func(t *testing.T) {
t.Skip("anonymous account not supported for this operation")
})

t.Run("with an individual account", func(t *testing.T) {
t.Skip("individual account not supported for this operation")
})

t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})
})

t.Run("deletes secrets without error", func(t *testing.T) {
secretValue := "super_secret_value"

config := fmt.Sprintf(`
resource "github_actions_organization_secret" "test_secret" {
secret_name = "test_secret_name"
plaintext_value = "%s"
jacobfoard marked this conversation as resolved.
Show resolved Hide resolved
visibility = "private"
}
`, secretValue)

testCase := func(t *testing.T, mode string) {
resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessMode(t, mode) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Destroy: true,
},
},
})
}

t.Run("with an anonymous account", func(t *testing.T) {
t.Skip("anonymous account not supported for this operation")
})

t.Run("with an individual account", func(t *testing.T) {
t.Skip("individual account not supported for this operation")
})

t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})
})
}
4 changes: 2 additions & 2 deletions github/resource_github_actions_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func resourceGithubActionsSecretRead(d *schema.ResourceData, meta interface{}) e
}

d.Set("plaintext_value", d.Get("plaintext_value"))
d.Set("updated_at", secret.UpdatedAt.Format("default"))
d.Set("created_at", secret.CreatedAt.Format("default"))
d.Set("updated_at", secret.UpdatedAt.String())
d.Set("created_at", secret.CreatedAt.String())

return nil
}
Expand Down
Loading