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

Handle the case when issue labels already exist #13182

Merged
merged 2 commits into from
Mar 31, 2017
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
73 changes: 44 additions & 29 deletions builtin/providers/github/resource_github_issue_label.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (

func resourceGithubIssueLabel() *schema.Resource {
return &schema.Resource{
Create: resourceGithubIssueLabelCreate,
Create: resourceGithubIssueLabelCreateOrUpdate,
Read: resourceGithubIssueLabelRead,
Update: resourceGithubIssueLabelUpdate,
Update: resourceGithubIssueLabelCreateOrUpdate,
Delete: resourceGithubIssueLabelDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
Expand Down Expand Up @@ -40,21 +40,54 @@ func resourceGithubIssueLabel() *schema.Resource {
}
}

func resourceGithubIssueLabelCreate(d *schema.ResourceData, meta interface{}) error {
// resourceGithubIssueLabelCreateOrUpdate idempotently creates or updates an
// issue label. Issue labels are keyed off of their "name", so pre-existing
// issue labels result in a 422 HTTP error if they exist outside of Terraform.
// Normally this would not be an issue, except new repositories are created with
// a "default" set of labels, and those labels easily conflict with custom ones.
//
// This function will first check if the label exists, and then issue an update,
// otherwise it will create. This is also advantageous in that we get to use the
// same function for two schema funcs.

func resourceGithubIssueLabelCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
o := meta.(*Organization).name
r := d.Get("repository").(string)
n := d.Get("name").(string)
c := d.Get("color").(string)
label := github.Label{

label := &github.Label{
Name: &n,
Color: &c,
}

log.Printf("[DEBUG] Creating label: %#v", label)
_, resp, err := client.Issues.CreateLabel(context.TODO(), meta.(*Organization).name, r, &label)
log.Printf("[DEBUG] Response from creating label: %s", *resp)
if err != nil {
return err
log.Printf("[DEBUG] Querying label existence %s/%s (%s)", o, r, n)
existing, _, _ := client.Issues.GetLabel(context.TODO(), o, r, n)

if existing != nil {
log.Printf("[DEBUG] Updating label: %s/%s (%s: %s)", o, r, n, c)

// Pull out the original name. If we already have a resource, this is the
// parsed ID. If not, it's the value given to the resource.
var oname string
if d.Id() == "" {
oname = n
} else {
_, oname = parseTwoPartID(d.Id())
}

_, _, err := client.Issues.EditLabel(context.TODO(), o, r, oname, label)
if err != nil {
return err
}
} else {
log.Printf("[DEBUG] Creating label: %s/%s (%s: %s)", o, r, n, c)
_, resp, err := client.Issues.CreateLabel(context.TODO(), o, r, label)
log.Printf("[DEBUG] Response from creating label: %s", *resp)
if err != nil {
return err
}
}

d.SetId(buildTwoPartID(&r, &n))
Expand All @@ -66,6 +99,7 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro
client := meta.(*Organization).client
r, n := parseTwoPartID(d.Id())

log.Printf("[DEBUG] Reading label: %s/%s", r, n)
githubLabel, _, err := client.Issues.GetLabel(context.TODO(), meta.(*Organization).name, r, n)
if err != nil {
d.SetId("")
Expand All @@ -80,31 +114,12 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro
return nil
}

func resourceGithubIssueLabelUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
r := d.Get("repository").(string)
n := d.Get("name").(string)
c := d.Get("color").(string)

_, originalName := parseTwoPartID(d.Id())
_, _, err := client.Issues.EditLabel(context.TODO(), meta.(*Organization).name, r, originalName, &github.Label{
Name: &n,
Color: &c,
})
if err != nil {
return err
}

d.SetId(buildTwoPartID(&r, &n))

return resourceGithubIssueLabelRead(d, meta)
}

func resourceGithubIssueLabelDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
r := d.Get("repository").(string)
n := d.Get("name").(string)

log.Printf("[DEBUG] Deleting label: %s/%s", r, n)
_, err := client.Issues.DeleteLabel(context.TODO(), meta.(*Organization).name, r, n)
return err
}
20 changes: 20 additions & 0 deletions builtin/providers/github/resource_github_issue_label_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ func TestAccGithubIssueLabel_basic(t *testing.T) {
testAccCheckGithubIssueLabelAttributes(&label, "bar", "FFFFFF"),
),
},
{
Config: testAccGitHubIssueLabelExistsConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubIssueLabelExists("github_issue_label.test", &label),
testAccCheckGithubIssueLabelAttributes(&label, "enhancement", "FF00FF"),
),
},
},
})
}
Expand Down Expand Up @@ -134,3 +141,16 @@ resource "github_issue_label" "test" {
color = "FFFFFF"
}
`, testRepo)

var testAccGitHubIssueLabelExistsConfig string = fmt.Sprintf(`
// Create a repository which has the default labels
resource "github_repository" "test" {
name = "tf-acc-repo-label-abc1234"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to randomize this, but apparently the other tests here aren't randomized yet either, so I'll do it in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I tried that. I used random_id, but random_id doesn't get compiled 😦

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we usually generate the randomness within the test via helpers (https://github.com/hashicorp/terraform/tree/master/helper/acctest), intentionally to not couple providers like random with other ones.

}

resource "github_issue_label" "test" {
repository = "${github_repository.test.name}"
name = "enhancement" // Important! This is a pre-created label
color = "FF00FF"
}
`)
16 changes: 13 additions & 3 deletions website/source/docs/providers/github/r/issue_label.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ description: |-
Provides a GitHub issue label resource.
---

# github\_issue_label
# github_issue_label
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never really understood the point of the \ escaping here in this context 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does nothing... Trust me lol. This is identical.


Provides a GitHub issue label resource.

This resource allows you to create and manage issue labels within your
Github organization.

Issue labels are keyed off of their "name", so pre-existing issue labels result
in a 422 HTTP error if they exist outside of Terraform. Normally this would not
be an issue, except new repositories are created with a "default" set of labels,
and those labels easily conflict with custom ones.

This resource will first check if the label exists, and then issue an update,
otherwise it will create.

## Example Usage

```
```hcl
# Create a new, red colored label
resource "github_issue_label" "test_repo" {
repository = "test-repo"
Expand All @@ -29,5 +37,7 @@ resource "github_issue_label" "test_repo" {
The following arguments are supported:

* `repository` - (Required) The GitHub repository

* `name` - (Required) The name of the label.
* `color` - (Required) A 6 character hex code, without the leading #, identifying the color of the label.

* `color` - (Required) A 6 character hex code, **without the leading #**, identifying the color of the label.