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 github_branch_protection_v3 Resource #642

Merged
merged 2 commits into from
Jan 14, 2021
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
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func Provider() terraform.ResourceProvider {
"github_actions_secret": resourceGithubActionsSecret(),
"github_branch": resourceGithubBranch(),
"github_branch_protection": resourceGithubBranchProtection(),
"github_branch_protection_v3": resourceGithubBranchProtectionV3(),
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
Expand Down
4 changes: 2 additions & 2 deletions github/resource_github_branch_protection.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ func resourceGithubBranchProtection() *schema.Resource {
},
},
PROTECTION_REQUIRES_STATUS_CHECKS: {
Type: schema.TypeList,
Optional: true,
Type: schema.TypeList,
Optional: true,
DiffSuppressFunc: statusChecksDiffSuppression,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down
335 changes: 335 additions & 0 deletions github/resource_github_branch_protection_v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
package github

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

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

func resourceGithubBranchProtectionV3() *schema.Resource {
return &schema.Resource{
Create: resourceGithubBranchProtectionV3Create,
Read: resourceGithubBranchProtectionV3Read,
Update: resourceGithubBranchProtectionV3Update,
Delete: resourceGithubBranchProtectionV3Delete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"branch": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"required_status_checks": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"include_admins": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Deprecated: "Use enforce_admins instead",
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return true
},
},
"strict": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"contexts": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"required_pull_request_reviews": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// FIXME: Remove this deprecated field
"include_admins": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Deprecated: "Use enforce_admins instead",
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return true
},
},
"dismiss_stale_reviews": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"dismissal_users": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"dismissal_teams": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"require_code_owner_reviews": {
Type: schema.TypeBool,
Optional: true,
},
"required_approving_review_count": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
ValidateFunc: validation.IntBetween(1, 6),
},
},
},
},
"restrictions": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"users": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"teams": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"apps": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"enforce_admins": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"require_signed_commits": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

client := meta.(*Owner).v3client

orgName := meta.(*Owner).name
repoName := d.Get("repository").(string)
branch := d.Get("branch").(string)

protectionRequest, err := buildProtectionRequest(d)
if err != nil {
return err
}
ctx := context.Background()

log.Printf("[DEBUG] Creating branch protection: %s/%s (%s)",
orgName, repoName, branch)
protection, _, err := client.Repositories.UpdateBranchProtection(ctx,
orgName,
repoName,
branch,
protectionRequest,
)
if err != nil {
return err
}

if err := checkBranchRestrictionsUsers(protection.GetRestrictions(), protectionRequest.GetRestrictions()); err != nil {
return err
}

d.SetId(buildTwoPartID(repoName, branch))

if err = requireSignedCommitsUpdate(d, meta); err != nil {
return err
}

return resourceGithubBranchProtectionV3Read(d, meta)
}

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

client := meta.(*Owner).v3client

repoName, branch, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return err
}
orgName := meta.(*Owner).name

ctx := context.WithValue(context.Background(), ctxId, d.Id())
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
}

log.Printf("[DEBUG] Reading branch protection: %s/%s (%s)",
orgName, repoName, branch)
githubProtection, resp, err := client.Repositories.GetBranchProtection(ctx,
orgName, repoName, branch)
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok {
if ghErr.Response.StatusCode == http.StatusNotModified {
if err := requireSignedCommitsRead(d, meta); err != nil {
return fmt.Errorf("Error setting signed commit restriction: %v", err)
}
return nil
}
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing branch protection %s/%s (%s) from state because it no longer exists in GitHub",
orgName, repoName, branch)
d.SetId("")
return nil
}
}

return err
}

d.Set("etag", resp.Header.Get("ETag"))
d.Set("repository", repoName)
d.Set("branch", branch)
d.Set("enforce_admins", githubProtection.GetEnforceAdmins().Enabled)

if err := flattenAndSetRequiredStatusChecks(d, githubProtection); err != nil {
return fmt.Errorf("Error setting required_status_checks: %v", err)
}

if err := flattenAndSetRequiredPullRequestReviews(d, githubProtection); err != nil {
return fmt.Errorf("Error setting required_pull_request_reviews: %v", err)
}

if err := flattenAndSetRestrictions(d, githubProtection); err != nil {
return fmt.Errorf("Error setting restrictions: %v", err)
}

if err := requireSignedCommitsRead(d, meta); err != nil {
return fmt.Errorf("Error setting signed commit restriction: %v", err)
}

return nil
}

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

client := meta.(*Owner).v3client
repoName, branch, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return err
}

protectionRequest, err := buildProtectionRequest(d)
if err != nil {
return err
}

orgName := meta.(*Owner).name
ctx := context.WithValue(context.Background(), ctxId, d.Id())

log.Printf("[DEBUG] Updating branch protection: %s/%s (%s)",
orgName, repoName, branch)
protection, _, err := client.Repositories.UpdateBranchProtection(ctx,
orgName,
repoName,
branch,
protectionRequest,
)
if err != nil {
return err
}

if err := checkBranchRestrictionsUsers(protection.GetRestrictions(), protectionRequest.GetRestrictions()); err != nil {
return err
}

if protectionRequest.RequiredPullRequestReviews == nil {
_, err = client.Repositories.RemovePullRequestReviewEnforcement(ctx,
orgName,
repoName,
branch,
)
if err != nil {
return err
}
}

d.SetId(buildTwoPartID(repoName, branch))

if err = requireSignedCommitsUpdate(d, meta); err != nil {
return err
}

return resourceGithubBranchProtectionV3Read(d, meta)
}

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

client := meta.(*Owner).v3client
repoName, branch, err := parseTwoPartID(d.Id(), "repository", "branch")
if err != nil {
return err
}

orgName := meta.(*Owner).name
ctx := context.WithValue(context.Background(), ctxId, d.Id())

log.Printf("[DEBUG] Deleting branch protection: %s/%s (%s)", orgName, repoName, branch)
_, err = client.Repositories.RemoveBranchProtection(ctx,
orgName, repoName, branch)
return err
}
Loading