Skip to content

Commit

Permalink
Add github_branch data and resource (integrations#364)
Browse files Browse the repository at this point in the history
* add github_repository_branch data and resource

* rename github_repository_branch to github_branch and adjust tests to use required fixtures

* nit

* Trigger travis

* Trigger travis

* add github_branch documentation

* revert go.sum change.

* remove sha attribute

* fix importing

* include doc links

* fix broken doc link

* clean up github_branch:
 - add etag and sha attributes back
 - make errors more verbose for easier debugging
 - expand test case
 - require branch for data source

* review

* review

* review

* fix client references for github v3 client

* fix util call

* nit

* fix import id when specifying source_branch
  • Loading branch information
cornfeedhobo authored May 1, 2020
1 parent 4c352c3 commit 54bbd47
Show file tree
Hide file tree
Showing 8 changed files with 656 additions and 1 deletion.
69 changes: 69 additions & 0 deletions github/data_source_github_branch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package github

import (
"context"
"fmt"
"log"

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

func dataSourceGithubBranch() *schema.Resource {
return &schema.Resource{
Read: dataSourceGithubBranchRead,

Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"branch": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"ref": {
Type: schema.TypeString,
Computed: true,
},
"sha": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

client := meta.(*Organization).v3client
orgName := meta.(*Organization).name
repoName := d.Get("repository").(string)
branchName := d.Get("branch").(string)
branchRefName := "refs/heads/" + branchName

log.Printf("[DEBUG] Reading GitHub branch reference %s/%s (%s)",
orgName, repoName, branchRefName)
ref, resp, err := client.Git.GetRef(
context.TODO(), orgName, repoName, branchRefName)
if err != nil {
return fmt.Errorf("Error reading GitHub branch reference %s/%s (%s): %s",
orgName, repoName, branchRefName, err)
}

d.SetId(buildTwoPartID(repoName, branchName))
d.Set("etag", resp.Header.Get("ETag"))
d.Set("ref", *ref.Ref)
d.Set("sha", *ref.Object.SHA)

return nil
}
74 changes: 74 additions & 0 deletions github/data_source_github_branch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package github

import (
"fmt"
"testing"

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

func TestAccGithubBranchDataSource_basic(t *testing.T) {

var (
name = "main"
repo = "test-repo"
rn = "data.github_branch." + name
)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGithubBranchDataSourceConfig(name, repo, "master"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(rn, "repository", repo),
resource.TestCheckResourceAttr(rn, "branch", "master"),
resource.TestCheckResourceAttrSet(rn, "etag"),
resource.TestCheckResourceAttrSet(rn, "ref"),
resource.TestCheckResourceAttrSet(rn, "sha"),
),
},
{
Config: testAccCheckGithubBranchDataSourceConfig(name, repo, "master"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(rn, "repository", repo),
resource.TestCheckResourceAttr(rn, "branch", "master"),
resource.TestCheckResourceAttrSet(rn, "etag"),
resource.TestCheckResourceAttrSet(rn, "ref"),
resource.TestCheckResourceAttrSet(rn, "sha"),
),
},
{
Config: testAccCheckGithubBranchDataSourceConfig(name, repo, "test-branch"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(rn, "repository", repo),
resource.TestCheckResourceAttr(rn, "branch", "test-branch"),
resource.TestCheckResourceAttrSet(rn, "etag"),
resource.TestCheckResourceAttrSet(rn, "ref"),
resource.TestCheckResourceAttrSet(rn, "sha"),
),
},
{
Config: testAccCheckGithubBranchDataSourceConfig(name, repo, "test-branch"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(rn, "repository", repo),
resource.TestCheckResourceAttr(rn, "branch", "test-branch"),
resource.TestCheckResourceAttrSet(rn, "etag"),
resource.TestCheckResourceAttrSet(rn, "ref"),
resource.TestCheckResourceAttrSet(rn, "sha"),
),
},
},
})

}

func testAccCheckGithubBranchDataSourceConfig(name, repo, branch string) string {
return fmt.Sprintf(`
data "github_branch" "%s" {
repository = "%s"
branch = "%s"
}
`, name, repo, branch)
}
4 changes: 3 additions & 1 deletion github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ 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(),
Expand All @@ -69,6 +70,8 @@ func Provider() terraform.ResourceProvider {
},

DataSourcesMap: map[string]*schema.Resource{
"github_actions_public_key": dataSourceGithubActionsPublicKey(),
"github_branch": dataSourceGithubBranch(),
"github_collaborators": dataSourceGithubCollaborators(),
"github_ip_ranges": dataSourceGithubIpRanges(),
"github_membership": dataSourceGithubMembership(),
Expand All @@ -77,7 +80,6 @@ func Provider() terraform.ResourceProvider {
"github_repository": dataSourceGithubRepository(),
"github_team": dataSourceGithubTeam(),
"github_user": dataSourceGithubUser(),
"github_actions_public_key": dataSourceGithubActionsPublicKey(),
},
}

Expand Down
202 changes: 202 additions & 0 deletions github/resource_github_branch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package github

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

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

func resourceGithubBranch() *schema.Resource {
return &schema.Resource{
Create: resourceGithubBranchCreate,
Read: resourceGithubBranchRead,
Delete: resourceGithubBranchDelete,
Importer: &schema.ResourceImporter{
State: resourceGithubBranchImport,
},

Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"branch": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"source_branch": {
Type: schema.TypeString,
Default: "master",
Optional: true,
ForceNew: true,
},
"source_sha": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"ref": {
Type: schema.TypeString,
Computed: true,
},
"sha": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

ctx := context.Background()
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxId, d.Id())
}

client := meta.(*Organization).v3client
orgName := meta.(*Organization).name
repoName := d.Get("repository").(string)
branchName := d.Get("branch").(string)
branchRefName := "refs/heads/" + branchName
sourceBranchName := d.Get("source_branch").(string)
sourceBranchRefName := "refs/heads/" + sourceBranchName

if _, hasSourceSHA := d.GetOk("source_sha"); !hasSourceSHA {
log.Printf("[DEBUG] Querying GitHub branch reference %s/%s (%s) to derive source_sha",
orgName, repoName, sourceBranchRefName)
ref, _, err := client.Git.GetRef(ctx, orgName, repoName, sourceBranchRefName)
if err != nil {
return fmt.Errorf("Error querying GitHub branch reference %s/%s (%s): %s",
orgName, repoName, sourceBranchRefName, err)
}
d.Set("source_sha", *ref.Object.SHA)
}
sourceBranchSHA := d.Get("source_sha").(string)

log.Printf("[DEBUG] Creating GitHub branch reference %s/%s (%s)",
orgName, repoName, branchRefName)
_, _, err = client.Git.CreateRef(ctx, orgName, repoName, &github.Reference{
Ref: &branchRefName,
Object: &github.GitObject{SHA: &sourceBranchSHA},
})
if err != nil {
return fmt.Errorf("Error creating GitHub branch reference %s/%s (%s): %s",
orgName, repoName, branchRefName, err)
}

d.SetId(buildTwoPartID(repoName, branchName))

return resourceGithubBranchRead(d, meta)
}

func resourceGithubBranchRead(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
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))
}

client := meta.(*Organization).v3client
orgName := meta.(*Organization).name
repoName, branchName, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return err
}
branchRefName := "refs/heads/" + branchName

log.Printf("[DEBUG] Querying GitHub branch reference %s/%s (%s)",
orgName, repoName, branchRefName)
ref, resp, err := client.Git.GetRef(ctx, orgName, repoName, branchRefName)
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 branch %s/%s (%s) from state because it no longer exists in Github",
orgName, repoName, branchName)
d.SetId("")
return nil
}
}
return fmt.Errorf("Error querying GitHub branch reference %s/%s (%s): %s",
orgName, repoName, branchRefName, err)
}

d.SetId(buildTwoPartID(repoName, branchName))
d.Set("etag", resp.Header.Get("ETag"))
d.Set("repository", repoName)
d.Set("branch", branchName)
d.Set("ref", *ref.Ref)
d.Set("sha", *ref.Object.SHA)

return nil
}

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

ctx := context.WithValue(context.Background(), ctxId, d.Id())

client := meta.(*Organization).v3client
orgName := meta.(*Organization).name
repoName, branchName, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return err
}
branchRefName := "refs/heads/" + branchName

log.Printf("[DEBUG] Deleting GitHub branch reference %s/%s (%s)",
orgName, repoName, branchRefName)
_, err = client.Git.DeleteRef(ctx, orgName, repoName, branchRefName)
if err != nil {
return fmt.Errorf("Error deleting GitHub branch reference %s/%s (%s): %s",
orgName, repoName, branchRefName, err)
}

return nil
}

func resourceGithubBranchImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
repoName, branchName, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return nil, err
}

sourceBranch := "master"
if strings.Contains(branchName, ":") {
branchName, sourceBranch, err = parseTwoPartID(branchName, "branch", "source_branch")
if err != nil {
return nil, err
}
d.SetId(buildTwoPartID(repoName, branchName))
}

d.Set("source_branch", sourceBranch)

return []*schema.ResourceData{d}, resourceGithubBranchRead(d, meta)
}
Loading

0 comments on commit 54bbd47

Please sign in to comment.