Skip to content
Closed
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
151 changes: 142 additions & 9 deletions github/resource_github_repository_environment_deployment_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package github
import (
"context"
"errors"
"log"
"net/http"
"net/url"
"strconv"

"github.com/google/go-github/v81/github"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
Expand All @@ -21,7 +21,7 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicy() *schema.Resource {
UpdateContext: resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate,
DeleteContext: resourceGithubRepositoryEnvironmentDeploymentPolicyDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
StateContext: resourceGithubRepositoryEnvironmentDeploymentPolicyImport,
},
Schema: map[string]*schema.Schema{
"repository": {
Expand Down Expand Up @@ -78,30 +78,56 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyCreate(ctx context.Conte
escapedEnvName := url.PathEscape(envName)

var createData github.DeploymentBranchPolicyRequest
var patternType string
if v, ok := d.GetOk("branch_pattern"); ok {
patternType = "branch"
createData = github.DeploymentBranchPolicyRequest{
Name: github.Ptr(v.(string)),
Type: github.Ptr("branch"),
}
} else if v, ok := d.GetOk("tag_pattern"); ok {
patternType = "tag"
createData = github.DeploymentBranchPolicyRequest{
Name: github.Ptr(v.(string)),
Type: github.Ptr("tag"),
}
} else {
return diag.Errorf("only one of 'branch_pattern' or 'tag_pattern' must be specified")
}

tflog.Debug(ctx, "Creating repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"pattern_type": patternType,
})

resultKey, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, &createData)
if err != nil {
tflog.Error(ctx, "Failed to create repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"error": err.Error(),
})
return diag.FromErr(err)
}

d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))
policyID := resultKey.GetID()
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(policyID, 10)))

tflog.Info(ctx, "Created repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": policyID,
})

return nil
}

func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
tflog.Debug(ctx, "Reading repository environment deployment policy", map[string]any{
"id": d.Id(),
})
client := meta.(*Owner).v3client

owner := meta.(*Owner).name
Expand All @@ -120,23 +146,44 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(ctx context.Context
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
if ghErr.Response.StatusCode == http.StatusNotModified {
tflog.Debug(ctx, "API responded with StatusNotModified, not refreshing state")
return nil
}
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[INFO] Removing branch deployment policy for %s/%s/%s from state because it no longer exists in GitHub",
owner, repoName, envName)
tflog.Info(ctx, "Removing branch deployment policy from state because it no longer exists in GitHub", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
})
d.SetId("")
return nil
}
}
tflog.Error(ctx, "Failed to read repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
"error": err.Error(),
})
return diag.FromErr(err)
}

if branchPolicy.GetType() == "branch" {
patternType := branchPolicy.GetType()
if patternType == "branch" {
_ = d.Set("branch_pattern", branchPolicy.GetName())
} else {
_ = d.Set("tag_pattern", branchPolicy.GetName())
}

tflog.Debug(ctx, "Successfully read repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
"pattern_type": patternType,
})

return nil
}

Expand All @@ -159,6 +206,13 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(ctx context.Conte
return diag.FromErr(err)
}

tflog.Debug(ctx, "Updating repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
})

pattern := branchPattern
if branchPattern == "" {
pattern = tagPattern
Expand All @@ -170,9 +224,26 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(ctx context.Conte

resultKey, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, branchPolicyId, &updateData)
if err != nil {
tflog.Error(ctx, "Failed to update repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
"error": err.Error(),
})
return diag.FromErr(err)
}
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))

policyID := resultKey.GetID()
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(policyID, 10)))

tflog.Info(ctx, "Updated repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": policyID,
})

return nil
}

Expand All @@ -190,11 +261,32 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyDelete(ctx context.Conte
return diag.FromErr(err)
}

tflog.Debug(ctx, "Deleting repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
})

_, err = client.Repositories.DeleteDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId)
if err != nil {
tflog.Error(ctx, "Failed to delete repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
"error": err.Error(),
})
return diag.FromErr(err)
}

tflog.Info(ctx, "Deleted repository environment deployment policy", map[string]any{
"owner": owner,
"repository": repoName,
"environment": envName,
"policy_id": branchPolicyId,
})

return nil
}

Expand All @@ -210,3 +302,44 @@ func customDeploymentPolicyDiffFunction(_ context.Context, diff *schema.Resource

return nil
}

func resourceGithubRepositoryEnvironmentDeploymentPolicyImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
tflog.Debug(ctx, "Importing repository environment deployment policy", map[string]any{
"id": d.Id(),
})

repoName, envName, policyId, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
if err != nil {
tflog.Error(ctx, "Failed to parse import ID", map[string]any{
"id": d.Id(),
"error": err.Error(),
})
return nil, err
}

if err := d.Set("repository", repoName); err != nil {
return nil, err
}

envNameUnescaped, err := url.PathUnescape(envName)
if err != nil {
tflog.Error(ctx, "Failed to unescape environment name", map[string]any{
"id": d.Id(),
"environment": envName,
"error": err.Error(),
})
return nil, err
}

if err := d.Set("environment", envNameUnescaped); err != nil {
return nil, err
}

tflog.Info(ctx, "Imported repository environment deployment policy", map[string]any{
"repository": repoName,
"environment": envNameUnescaped,
"policy_id": policyId,
})

return []*schema.ResourceData{d}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,75 @@ func TestAccGithubRepositoryEnvironmentDeploymentPolicyBranchUpdate(t *testing.T
})
}

func TestAccGithubRepositoryEnvironmentDeploymentPolicyBranch_import(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
repoName := fmt.Sprintf("%senv-deploy-%s", testResourcePrefix, randomID)

config := fmt.Sprintf(`

data "github_user" "current" {
username = ""
}

resource "github_repository" "test" {
name = "%s"
ignore_vulnerability_alerts_during_read = true
}

resource "github_repository_environment" "test" {
repository = github_repository.test.name
environment = "environment/test"
wait_timer = 10000
reviewers {
users = [data.github_user.current.id]
}
deployment_branch_policy {
protected_branches = false
custom_branch_policies = true
}
}

resource "github_repository_environment_deployment_policy" "test" {
repository = github_repository.test.name
environment = github_repository_environment.test.environment
branch_pattern = "main"
}

`, repoName)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnauthenticated(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "repository",
repoName,
),
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "environment",
"environment/test",
),
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "branch_pattern",
"main",
),
resource.TestCheckNoResourceAttr(
"github_repository_environment_deployment_policy.test", "tag_pattern",
),
),
},
{
ResourceName: "github_repository_environment_deployment_policy.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccGithubRepositoryEnvironmentDeploymentPolicyTag(t *testing.T) {
t.Run("creates a repository environment with tag-based deployment policy", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
Expand Down