Skip to content

Commit a3e128c

Browse files
refactor: Use go-github v81 Cost Center API and fix review comments
- Replace custom API implementation with go-github v81 Enterprise.* methods - Remove util_cost_centers.go and consolidate helpers into resource file - Fix deprecated GetOkExists usage, replaced with GetOk - Fix GitHub capitalization in documentation page titles
1 parent 1958402 commit a3e128c

7 files changed

+90
-252
lines changed

github/data_source_github_enterprise_cost_center.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,31 +85,34 @@ func dataSourceGithubEnterpriseCostCenterRead(ctx context.Context, d *schema.Res
8585

8686
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/%s", enterpriseSlug, costCenterID))
8787

88-
cc, err := enterpriseCostCenterGet(ctx, client, enterpriseSlug, costCenterID)
88+
cc, _, err := client.Enterprise.GetCostCenter(ctx, enterpriseSlug, costCenterID)
8989
if err != nil {
9090
return diag.FromErr(err)
9191
}
9292

9393
d.SetId(costCenterID)
9494
_ = d.Set("name", cc.Name)
9595

96-
state := strings.ToLower(cc.State)
96+
state := strings.ToLower(cc.GetState())
9797
if state == "" {
9898
state = "active"
9999
}
100100
_ = d.Set("state", state)
101-
_ = d.Set("azure_subscription", cc.AzureSubscription)
101+
_ = d.Set("azure_subscription", cc.GetAzureSubscription())
102102

103103
resources := make([]map[string]any, 0)
104104
for _, r := range cc.Resources {
105+
if r == nil {
106+
continue
107+
}
105108
resources = append(resources, map[string]any{
106109
"type": r.Type,
107110
"name": r.Name,
108111
})
109112
}
110113
_ = d.Set("resources", resources)
111114

112-
users, organizations, repositories := enterpriseCostCenterSplitResources(cc.Resources)
115+
users, organizations, repositories := costCenterSplitResources(cc.Resources)
113116
sort.Strings(users)
114117
sort.Strings(organizations)
115118
sort.Strings(repositories)

github/data_source_github_enterprise_cost_centers.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/google/go-github/v81/github"
78
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
89
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
910
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -66,33 +67,44 @@ func dataSourceGithubEnterpriseCostCenters() *schema.Resource {
6667
func dataSourceGithubEnterpriseCostCentersRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
6768
client := meta.(*Owner).v3client
6869
enterpriseSlug := d.Get("enterprise_slug").(string)
69-
state := ""
70+
var state *string
7071
if v, ok := d.GetOk("state"); ok {
71-
state = v.(string)
72+
s := v.(string)
73+
state = &s
7274
}
7375

7476
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/cost-centers", enterpriseSlug))
75-
centers, err := enterpriseCostCentersList(ctx, client, enterpriseSlug, state)
77+
result, _, err := client.Enterprise.ListCostCenters(ctx, enterpriseSlug, &github.ListCostCenterOptions{State: state})
7678
if err != nil {
7779
return diag.FromErr(err)
7880
}
7981

80-
items := make([]any, 0, len(centers))
81-
for _, cc := range centers {
82+
items := make([]any, 0, len(result.CostCenters))
83+
for _, cc := range result.CostCenters {
84+
if cc == nil {
85+
continue
86+
}
8287
resources := make([]map[string]any, 0)
8388
for _, r := range cc.Resources {
89+
if r == nil {
90+
continue
91+
}
8492
resources = append(resources, map[string]any{"type": r.Type, "name": r.Name})
8593
}
8694
items = append(items, map[string]any{
8795
"id": cc.ID,
8896
"name": cc.Name,
89-
"state": cc.State,
90-
"azure_subscription": cc.AzureSubscription,
97+
"state": cc.GetState(),
98+
"azure_subscription": cc.GetAzureSubscription(),
9199
"resources": resources,
92100
})
93101
}
94102

95-
d.SetId(fmt.Sprintf("%s/%s", enterpriseSlug, state))
103+
stateStr := ""
104+
if state != nil {
105+
stateStr = *state
106+
}
107+
d.SetId(fmt.Sprintf("%s/%s", enterpriseSlug, stateStr))
96108
_ = d.Set("cost_centers", items)
97109
return nil
98110
}

github/resource_github_enterprise_cost_center.go

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func resourceGithubEnterpriseCostCenterCreate(ctx context.Context, d *schema.Res
9595
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/%s", enterpriseSlug, name))
9696
log.Printf("[INFO] Creating enterprise cost center: %s (%s)", name, enterpriseSlug)
9797

98-
cc, err := enterpriseCostCenterCreate(ctx, client, enterpriseSlug, name)
98+
cc, _, err := client.Enterprise.CreateCostCenter(ctx, enterpriseSlug, github.CostCenterRequest{Name: name})
9999
if err != nil {
100100
return diag.FromErr(err)
101101
}
@@ -108,7 +108,7 @@ func resourceGithubEnterpriseCostCenterCreate(ctx context.Context, d *schema.Res
108108

109109
if hasCostCenterAssignmentsConfigured(d) {
110110
// Ensure we operate on fresh API state before mutations.
111-
current, err := enterpriseCostCenterGet(ctx, client, enterpriseSlug, cc.ID)
111+
current, _, err := client.Enterprise.GetCostCenter(ctx, enterpriseSlug, cc.ID)
112112
if err != nil {
113113
return diag.FromErr(err)
114114
}
@@ -127,7 +127,7 @@ func resourceGithubEnterpriseCostCenterRead(ctx context.Context, d *schema.Resou
127127

128128
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/%s", enterpriseSlug, costCenterID))
129129

130-
cc, err := enterpriseCostCenterGet(ctx, client, enterpriseSlug, costCenterID)
130+
cc, _, err := client.Enterprise.GetCostCenter(ctx, enterpriseSlug, costCenterID)
131131
if err != nil {
132132
if is404(err) {
133133
// If the API starts returning 404 for archived cost centers, we remove it from state.
@@ -139,23 +139,26 @@ func resourceGithubEnterpriseCostCenterRead(ctx context.Context, d *schema.Resou
139139

140140
_ = d.Set("name", cc.Name)
141141

142-
state := strings.ToLower(cc.State)
142+
state := strings.ToLower(cc.GetState())
143143
if state == "" {
144144
state = "active"
145145
}
146146
_ = d.Set("state", state)
147-
_ = d.Set("azure_subscription", cc.AzureSubscription)
147+
_ = d.Set("azure_subscription", cc.GetAzureSubscription())
148148

149149
resources := make([]map[string]any, 0)
150150
for _, r := range cc.Resources {
151+
if r == nil {
152+
continue
153+
}
151154
resources = append(resources, map[string]any{
152155
"type": r.Type,
153156
"name": r.Name,
154157
})
155158
}
156159
_ = d.Set("resources", resources)
157160

158-
users, organizations, repositories := enterpriseCostCenterSplitResources(cc.Resources)
161+
users, organizations, repositories := costCenterSplitResources(cc.Resources)
159162
sort.Strings(users)
160163
sort.Strings(organizations)
161164
sort.Strings(repositories)
@@ -173,23 +176,23 @@ func resourceGithubEnterpriseCostCenterUpdate(ctx context.Context, d *schema.Res
173176

174177
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/%s", enterpriseSlug, costCenterID))
175178

176-
cc, err := enterpriseCostCenterGet(ctx, client, enterpriseSlug, costCenterID)
179+
cc, _, err := client.Enterprise.GetCostCenter(ctx, enterpriseSlug, costCenterID)
177180
if err != nil {
178181
return diag.FromErr(err)
179182
}
180-
if strings.EqualFold(cc.State, "deleted") {
183+
if strings.EqualFold(cc.GetState(), "deleted") {
181184
return diag.FromErr(fmt.Errorf("cannot update cost center %q because it is archived", costCenterID))
182185
}
183186

184187
if d.HasChange("name") {
185188
name := d.Get("name").(string)
186189
log.Printf("[INFO] Updating enterprise cost center: %s/%s", enterpriseSlug, costCenterID)
187-
_, err := enterpriseCostCenterUpdate(ctx, client, enterpriseSlug, costCenterID, name)
190+
_, _, err := client.Enterprise.UpdateCostCenter(ctx, enterpriseSlug, costCenterID, github.CostCenterRequest{Name: name})
188191
if err != nil {
189192
return diag.FromErr(err)
190193
}
191194

192-
cc, err = enterpriseCostCenterGet(ctx, client, enterpriseSlug, costCenterID)
195+
cc, _, err = client.Enterprise.GetCostCenter(ctx, enterpriseSlug, costCenterID)
193196
if err != nil {
194197
return diag.FromErr(err)
195198
}
@@ -212,7 +215,7 @@ func resourceGithubEnterpriseCostCenterDelete(ctx context.Context, d *schema.Res
212215
ctx = context.WithValue(ctx, ctxId, fmt.Sprintf("%s/%s", enterpriseSlug, costCenterID))
213216
log.Printf("[INFO] Archiving enterprise cost center: %s/%s", enterpriseSlug, costCenterID)
214217

215-
_, err := enterpriseCostCenterArchive(ctx, client, enterpriseSlug, costCenterID)
218+
_, _, err := client.Enterprise.DeleteCostCenter(ctx, enterpriseSlug, costCenterID)
216219
if err != nil {
217220
if is404(err) {
218221
return nil
@@ -236,12 +239,12 @@ func resourceGithubEnterpriseCostCenterImport(ctx context.Context, d *schema.Res
236239
return []*schema.ResourceData{d}, nil
237240
}
238241

239-
func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.ResourceData, client *github.Client, enterpriseSlug, costCenterID string, currentResources []enterpriseCostCenterResource) diag.Diagnostics {
242+
func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.ResourceData, client *github.Client, enterpriseSlug, costCenterID string, currentResources []*github.CostCenterResource) diag.Diagnostics {
240243
desiredUsers := expandStringSet(getStringSetOrEmpty(d, "users"))
241244
desiredOrgs := expandStringSet(getStringSetOrEmpty(d, "organizations"))
242245
desiredRepos := expandStringSet(getStringSetOrEmpty(d, "repositories"))
243246

244-
currentUsers, currentOrgs, currentRepos := enterpriseCostCenterSplitResources(currentResources)
247+
currentUsers, currentOrgs, currentRepos := costCenterSplitResources(currentResources)
245248

246249
toAddUsers, toRemoveUsers := diffStringSlices(currentUsers, desiredUsers)
247250
toAddOrgs, toRemoveOrgs := diffStringSlices(currentOrgs, desiredOrgs)
@@ -250,10 +253,10 @@ func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.Resource
250253
const maxResourcesPerRequest = 50
251254
const costCenterResourcesRetryTimeout = 5 * time.Minute
252255

253-
retryRemove := func(req enterpriseCostCenterResourcesRequest) diag.Diagnostics {
256+
retryRemove := func(req github.CostCenterResourceRequest) diag.Diagnostics {
254257
//nolint:staticcheck
255258
err := resource.RetryContext(ctx, costCenterResourcesRetryTimeout, func() *resource.RetryError {
256-
_, err := enterpriseCostCenterRemoveResources(ctx, client, enterpriseSlug, costCenterID, req)
259+
_, _, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, enterpriseSlug, costCenterID, req)
257260
if err == nil {
258261
return nil
259262
}
@@ -270,10 +273,10 @@ func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.Resource
270273
return nil
271274
}
272275

273-
retryAssign := func(req enterpriseCostCenterResourcesRequest) diag.Diagnostics {
276+
retryAssign := func(req github.CostCenterResourceRequest) diag.Diagnostics {
274277
//nolint:staticcheck
275278
err := resource.RetryContext(ctx, costCenterResourcesRetryTimeout, func() *resource.RetryError {
276-
_, err := enterpriseCostCenterAssignResources(ctx, client, enterpriseSlug, costCenterID, req)
279+
_, _, err := client.Enterprise.AddResourcesToCostCenter(ctx, enterpriseSlug, costCenterID, req)
277280
if err == nil {
278281
return nil
279282
}
@@ -307,17 +310,17 @@ func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.Resource
307310
log.Printf("[INFO] Removing enterprise cost center resources: %s/%s", enterpriseSlug, costCenterID)
308311

309312
for _, batch := range chunk(toRemoveUsers) {
310-
if diags := retryRemove(enterpriseCostCenterResourcesRequest{Users: batch}); diags.HasError() {
313+
if diags := retryRemove(github.CostCenterResourceRequest{Users: batch}); diags.HasError() {
311314
return diags
312315
}
313316
}
314317
for _, batch := range chunk(toRemoveOrgs) {
315-
if diags := retryRemove(enterpriseCostCenterResourcesRequest{Organizations: batch}); diags.HasError() {
318+
if diags := retryRemove(github.CostCenterResourceRequest{Organizations: batch}); diags.HasError() {
316319
return diags
317320
}
318321
}
319322
for _, batch := range chunk(toRemoveRepos) {
320-
if diags := retryRemove(enterpriseCostCenterResourcesRequest{Repositories: batch}); diags.HasError() {
323+
if diags := retryRemove(github.CostCenterResourceRequest{Repositories: batch}); diags.HasError() {
321324
return diags
322325
}
323326
}
@@ -327,17 +330,17 @@ func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.Resource
327330
log.Printf("[INFO] Assigning enterprise cost center resources: %s/%s", enterpriseSlug, costCenterID)
328331

329332
for _, batch := range chunk(toAddUsers) {
330-
if diags := retryAssign(enterpriseCostCenterResourcesRequest{Users: batch}); diags.HasError() {
333+
if diags := retryAssign(github.CostCenterResourceRequest{Users: batch}); diags.HasError() {
331334
return diags
332335
}
333336
}
334337
for _, batch := range chunk(toAddOrgs) {
335-
if diags := retryAssign(enterpriseCostCenterResourcesRequest{Organizations: batch}); diags.HasError() {
338+
if diags := retryAssign(github.CostCenterResourceRequest{Organizations: batch}); diags.HasError() {
336339
return diags
337340
}
338341
}
339342
for _, batch := range chunk(toAddRepos) {
340-
if diags := retryAssign(enterpriseCostCenterResourcesRequest{Repositories: batch}); diags.HasError() {
343+
if diags := retryAssign(github.CostCenterResourceRequest{Repositories: batch}); diags.HasError() {
341344
return diags
342345
}
343346
}
@@ -349,14 +352,10 @@ func syncEnterpriseCostCenterAssignments(ctx context.Context, d *schema.Resource
349352
func hasCostCenterAssignmentsConfigured(d *schema.ResourceData) bool {
350353
assignmentKeys := []string{"users", "organizations", "repositories"}
351354
for _, key := range assignmentKeys {
352-
if v, ok := d.GetOkExists(key); ok {
355+
if v, ok := d.GetOk(key); ok {
353356
if set, ok := v.(*schema.Set); ok && set != nil && set.Len() > 0 {
354357
return true
355358
}
356-
if !ok {
357-
// Non-set values still indicate explicit configuration.
358-
return true
359-
}
360359
}
361360
}
362361
return false
@@ -418,3 +417,36 @@ func isRetryableGithubResponseError(err error) bool {
418417
}
419418
return false
420419
}
420+
421+
func costCenterSplitResources(resources []*github.CostCenterResource) (users, organizations, repositories []string) {
422+
for _, r := range resources {
423+
if r == nil {
424+
continue
425+
}
426+
switch strings.ToLower(r.Type) {
427+
case "user":
428+
users = append(users, r.Name)
429+
case "org", "organization":
430+
organizations = append(organizations, r.Name)
431+
case "repo", "repository":
432+
repositories = append(repositories, r.Name)
433+
}
434+
}
435+
return users, organizations, repositories
436+
}
437+
438+
func stringSliceToAnySlice(v []string) []any {
439+
out := make([]any, 0, len(v))
440+
for _, s := range v {
441+
out = append(out, s)
442+
}
443+
return out
444+
}
445+
446+
func is404(err error) bool {
447+
var ghErr *github.ErrorResponse
448+
if errors.As(err, &ghErr) && ghErr.Response != nil {
449+
return ghErr.Response.StatusCode == 404
450+
}
451+
return false
452+
}

0 commit comments

Comments
 (0)