Skip to content

Commit 208b271

Browse files
committed
refactor: migrate resource_github_issue_label to context-aware CRUD and tflog
1 parent c8f2ce4 commit 208b271

1 file changed

Lines changed: 158 additions & 105 deletions

File tree

github/resource_github_issue_label.go

Lines changed: 158 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@ package github
33
import (
44
"context"
55
"errors"
6-
"log"
76
"net/http"
87

98
"github.com/google/go-github/v88/github"
9+
"github.com/hashicorp/terraform-plugin-log/tflog"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1011
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1112
)
1213

1314
func resourceGithubIssueLabel() *schema.Resource {
1415
return &schema.Resource{
15-
Create: resourceGithubIssueLabelCreateOrUpdate,
16-
Read: resourceGithubIssueLabelRead,
17-
Update: resourceGithubIssueLabelCreateOrUpdate,
18-
Delete: resourceGithubIssueLabelDelete,
16+
CreateContext: resourceGithubIssueLabelCreate,
17+
ReadContext: resourceGithubIssueLabelRead,
18+
UpdateContext: resourceGithubIssueLabelUpdate,
19+
DeleteContext: resourceGithubIssueLabelDelete,
1920
Importer: &schema.ResourceImporter{
20-
StateContext: schema.ImportStatePassthroughContext,
21+
StateContext: resourceGithubIssueLabelImport,
2122
},
2223

2324
Schema: map[string]*schema.Schema{
@@ -60,150 +61,202 @@ func resourceGithubIssueLabel() *schema.Resource {
6061
}
6162
}
6263

63-
// resourceGithubIssueLabelCreateOrUpdate idempotently creates or updates an
64-
// issue label. Issue labels are keyed off of their "name", so pre-existing
65-
// issue labels result in a 422 HTTP error if they exist outside of Terraform.
66-
// Normally this would not be an issue, except new repositories are created with
67-
// a "default" set of labels, and those labels easily conflict with custom ones.
68-
//
69-
// This function will first check if the label exists, and then issue an update,
70-
// otherwise it will create. This is also advantageous in that we get to use the
71-
// same function for two schema funcs.
72-
73-
func resourceGithubIssueLabelCreateOrUpdate(d *schema.ResourceData, meta any) error {
74-
client := meta.(*Owner).v3client
75-
orgName := meta.(*Owner).name
76-
repoName := d.Get("repository").(string)
77-
name := d.Get("name").(string)
78-
color := d.Get("color").(string)
64+
// resourceGithubIssueLabelCreate creates an issue label.
65+
func resourceGithubIssueLabelCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
66+
meta, _ := m.(*Owner)
67+
client := meta.v3client
68+
orgName := meta.name
69+
repoName, ok := d.Get("repository").(string)
70+
if !ok {
71+
return diag.Errorf(`expected "repository" to be string`)
72+
}
73+
name, ok := d.Get("name").(string)
74+
if !ok {
75+
return diag.Errorf(`expected "name" to be string`)
76+
}
77+
color, ok := d.Get("color").(string)
78+
if !ok {
79+
return diag.Errorf(`expected "color" to be string`)
80+
}
7981

8082
label := &github.Label{
8183
Name: new(name),
8284
Color: new(color),
8385
}
84-
ctx := context.Background()
85-
if !d.IsNewResource() {
86-
ctx = context.WithValue(ctx, ctxId, d.Id())
87-
}
8886

89-
// Pull out the original name. If we already have a resource, this is the
90-
// parsed ID. If not, it's the value given to the resource.
91-
var originalName string
92-
if d.Id() == "" {
93-
originalName = name
94-
} else {
95-
var err error
96-
_, originalName, err = parseID2(d.Id())
97-
if err != nil {
98-
return err
87+
if v, ok := d.GetOk("description"); ok {
88+
description, ok := v.(string)
89+
if !ok {
90+
return diag.Errorf(`expected "description" to be string`)
9991
}
92+
label.Description = &description
10093
}
101-
102-
existing, resp, err := client.Issues.GetLabel(ctx,
103-
orgName, repoName, originalName)
104-
if err != nil && resp.StatusCode != http.StatusNotFound {
105-
return err
106-
}
107-
108-
if existing != nil {
109-
label.Description = new(d.Get("description").(string))
110-
111-
// Pull out the original name. If we already have a resource, this is the
112-
// parsed ID. If not, it's the value given to the resource.
113-
var originalName string
114-
if d.Id() == "" {
115-
originalName = name
116-
} else {
117-
var err error
118-
_, originalName, err = parseID2(d.Id())
119-
if err != nil {
120-
return err
121-
}
122-
}
123-
124-
_, _, err := client.Issues.EditLabel(ctx,
125-
orgName, repoName, originalName, label)
126-
if err != nil {
127-
return err
94+
githubLabel, resp, err := client.Issues.GetLabel(ctx, orgName, repoName, name)
95+
if err != nil {
96+
if resp == nil || resp.StatusCode != http.StatusNotFound {
97+
return diag.FromErr(err)
12898
}
99+
githubLabel, resp, err = client.Issues.CreateLabel(ctx, orgName, repoName, label)
129100
} else {
130-
if v, ok := d.GetOk("description"); ok {
131-
label.Description = new(v.(string))
132-
}
133-
134-
_, _, err := client.Issues.CreateLabel(ctx,
135-
orgName, repoName, label)
136-
if err != nil {
137-
return err
138-
}
101+
githubLabel, resp, err = client.Issues.EditLabel(ctx, orgName, repoName, name, label)
139102
}
140103

141-
d.SetId(buildTwoPartID(repoName, name))
104+
if err != nil {
105+
return diag.FromErr(err)
106+
}
107+
id, err := buildID(repoName, name)
108+
if err != nil {
109+
return diag.FromErr(err)
110+
}
111+
d.SetId(id)
112+
if err := d.Set("url", githubLabel.GetURL()); err != nil {
113+
return diag.FromErr(err)
114+
}
142115

143-
return resourceGithubIssueLabelRead(d, meta)
116+
if err := d.Set("etag", resp.Header.Get("ETag")); err != nil {
117+
return diag.FromErr(err)
118+
}
119+
return nil
144120
}
145121

146-
func resourceGithubIssueLabelRead(d *schema.ResourceData, meta any) error {
147-
client := meta.(*Owner).v3client
148-
repoName, name, err := parseID2(d.Id())
149-
if err != nil {
150-
return err
122+
func resourceGithubIssueLabelRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
123+
meta, _ := m.(*Owner)
124+
client := meta.v3client
125+
repoName, ok := d.Get("repository").(string)
126+
if !ok {
127+
return diag.Errorf(`expected "repository" to be string`)
128+
}
129+
name, ok := d.Get("name").(string)
130+
if !ok {
131+
return diag.Errorf(`expected "name" to be string`)
151132
}
152133

153-
orgName := meta.(*Owner).name
154-
ctx := context.WithValue(context.Background(), ctxId, d.Id())
134+
orgName := meta.name
155135
if !d.IsNewResource() {
156136
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
157137
}
158138

159-
githubLabel, resp, err := client.Issues.GetLabel(ctx,
160-
orgName, repoName, name)
139+
githubLabel, resp, err := client.Issues.GetLabel(ctx, orgName, repoName, name)
161140
if err != nil {
162141
var ghErr *github.ErrorResponse
163142
if errors.As(err, &ghErr) {
164143
if ghErr.Response.StatusCode == http.StatusNotModified {
165144
return nil
166145
}
167146
if ghErr.Response.StatusCode == http.StatusNotFound {
168-
log.Printf("[INFO] Removing label %s (%s/%s) from state because it no longer exists in GitHub",
169-
name, orgName, repoName)
147+
tflog.Info(ctx, "Removing label from state because it no longer exists in GitHub", map[string]any{"name": name, "org_name": orgName, "repo_name": repoName})
170148
d.SetId("")
171149
return nil
172150
}
173151
}
174-
return err
152+
return diag.FromErr(err)
175153
}
176154

177-
if err = d.Set("etag", resp.Header.Get("ETag")); err != nil {
178-
return err
179-
}
180-
if err = d.Set("repository", repoName); err != nil {
181-
return err
182-
}
183-
if err = d.Set("name", name); err != nil {
184-
return err
185-
}
186155
if err = d.Set("color", githubLabel.GetColor()); err != nil {
187-
return err
156+
return diag.FromErr(err)
188157
}
189158
if err = d.Set("description", githubLabel.GetDescription()); err != nil {
190-
return err
159+
return diag.FromErr(err)
160+
}
161+
162+
if err = d.Set("etag", resp.Header.Get("ETag")); err != nil {
163+
return diag.FromErr(err)
191164
}
192165
if err = d.Set("url", githubLabel.GetURL()); err != nil {
193-
return err
166+
return diag.FromErr(err)
194167
}
195168

196169
return nil
197170
}
198171

199-
func resourceGithubIssueLabelDelete(d *schema.ResourceData, meta any) error {
200-
client := meta.(*Owner).v3client
172+
func resourceGithubIssueLabelUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
173+
meta, _ := m.(*Owner)
174+
client := meta.v3client
175+
orgName := meta.name
176+
repoName, ok := d.Get("repository").(string)
177+
if !ok {
178+
return diag.Errorf(`expected "repository" to be string`)
179+
}
180+
name, ok := d.Get("name").(string)
181+
if !ok {
182+
return diag.Errorf(`expected "name" to be string`)
183+
}
184+
color, ok := d.Get("color").(string)
185+
if !ok {
186+
return diag.Errorf(`expected "color" to be string`)
187+
}
188+
189+
originalName := name
190+
if d.HasChange("name") {
191+
oldName, _ := d.GetChange("name")
192+
oldNameString, ok := oldName.(string)
193+
if !ok {
194+
return diag.Errorf(`expected old "name" to be string`)
195+
}
196+
originalName = oldNameString
197+
}
198+
label := &github.Label{
199+
Name: new(name),
200+
Color: new(color),
201+
}
202+
if v, ok := d.GetOk("description"); ok {
203+
description, ok := v.(string)
204+
if !ok {
205+
return diag.Errorf(`expected "description" to be string`)
206+
}
207+
label.Description = &description
208+
}
209+
githubLabel, resp, err := client.Issues.EditLabel(ctx, orgName, repoName, originalName, label)
210+
if err != nil {
211+
return diag.FromErr(err)
212+
}
213+
id, err := buildID(repoName, name)
214+
if err != nil {
215+
return diag.FromErr(err)
216+
}
217+
d.SetId(id)
218+
219+
if err := d.Set("url", githubLabel.GetURL()); err != nil {
220+
return diag.FromErr(err)
221+
}
201222

202-
orgName := meta.(*Owner).name
203-
repoName := d.Get("repository").(string)
204-
name := d.Get("name").(string)
205-
ctx := context.WithValue(context.Background(), ctxId, d.Id())
223+
if err := d.Set("etag", resp.Header.Get("ETag")); err != nil {
224+
return diag.FromErr(err)
225+
}
226+
227+
return nil
228+
}
229+
230+
func resourceGithubIssueLabelDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
231+
meta, _ := m.(*Owner)
232+
client := meta.v3client
233+
orgName := meta.name
206234

235+
repoName, ok := d.Get("repository").(string)
236+
if !ok {
237+
return diag.Errorf(`expected "repository" to be string`)
238+
}
239+
name, ok := d.Get("name").(string)
240+
if !ok {
241+
return diag.Errorf(`expected "name" to be string`)
242+
}
207243
_, err := client.Issues.DeleteLabel(ctx, orgName, repoName, name)
208-
return handleArchivedRepoDelete(err, "issue label", name, orgName, repoName)
244+
return diag.FromErr(handleArchivedRepoDelete(err, "issue label", name, orgName, repoName))
245+
}
246+
247+
func resourceGithubIssueLabelImport(_ context.Context, d *schema.ResourceData, _ any) ([]*schema.ResourceData, error) {
248+
repoName, name, err := parseID2(d.Id())
249+
if err != nil {
250+
return nil, err
251+
}
252+
253+
if err := d.Set("repository", repoName); err != nil {
254+
return nil, err
255+
}
256+
257+
if err := d.Set("name", name); err != nil {
258+
return nil, err
259+
}
260+
261+
return []*schema.ResourceData{d}, nil
209262
}

0 commit comments

Comments
 (0)