Skip to content

Commit 72681b4

Browse files
authored
[FEAT] Add new resource for repository vulnerability alerts (#3166)
* Add resource for repository vulnerability alerts Signed-off-by: Timo Sand <[email protected]> * Mark `vulnerability_alerts` in `github_repository` as deprecated Signed-off-by: Timo Sand <[email protected]> * Update formatting Signed-off-by: Timo Sand <[email protected]> * Update import after rebase Signed-off-by: Timo Sand <[email protected]> * Correct file names Signed-off-by: Timo Sand <[email protected]> * Address review comments Signed-off-by: Timo Sand <[email protected]> * Simplify `SetId` Signed-off-by: Timo Sand <[email protected]> * Comment out `owner` until we can support it better Signed-off-by: Timo Sand <[email protected]> * Refactor to use `ConfigStateChecks` Signed-off-by: Timo Sand <[email protected]> * Update import ID to only repository name Signed-off-by: Timo Sand <[email protected]> * Improves tests Signed-off-by: Timo Sand <[email protected]> * Updates docs Signed-off-by: Timo Sand <[email protected]> * Add clearer error for user when trying to add to an archived repository Signed-off-by: Timo Sand <[email protected]> * Re-order `SetId` Signed-off-by: Timo Sand <[email protected]> * Simplify archived repo handling Signed-off-by: Timo Sand <[email protected]> * Fix behaviour on archived repository Signed-off-by: Timo Sand <[email protected]> --------- Signed-off-by: Timo Sand <[email protected]>
1 parent 866a673 commit 72681b4

7 files changed

Lines changed: 611 additions & 72 deletions

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func Provider() *schema.Provider {
203203
"github_repository_ruleset": resourceGithubRepositoryRuleset(),
204204
"github_repository_topics": resourceGithubRepositoryTopics(),
205205
"github_repository_webhook": resourceGithubRepositoryWebhook(),
206+
"github_repository_vulnerability_alerts": resourceGithubRepositoryVulnerabilityAlerts(),
206207
"github_team": resourceGithubTeam(),
207208
"github_team_members": resourceGithubTeamMembers(),
208209
"github_team_membership": resourceGithubTeamMembership(),

github/resource_github_repository.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,12 +400,13 @@ func resourceGithubRepository() *schema.Resource {
400400
Optional: true,
401401
Computed: true,
402402
Description: "Set to 'true' to enable security alerts for vulnerable dependencies. Enabling requires alerts to be enabled on the owner level. (Note for importing: GitHub enables the alerts on all repos by default). Note that vulnerability alerts have not been successfully tested on any GitHub Enterprise instance and may be unavailable in those settings.",
403+
Deprecated: "Use the github_repository_vulnerability_alerts resource instead. This field will be removed in a future version.",
403404
},
404405
"ignore_vulnerability_alerts_during_read": {
405406
Type: schema.TypeBool,
406407
Optional: true,
407408
Default: false,
408-
Deprecated: "This is ignored as the provider now handles lack of permissions automatically.",
409+
Deprecated: "This is ignored as the provider now handles lack of permissions automatically. This field will be removed in a future version.",
409410
},
410411
"full_name": {
411412
Type: schema.TypeString,
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"strconv"
7+
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
)
12+
13+
func resourceGithubRepositoryVulnerabilityAlerts() *schema.Resource {
14+
return &schema.Resource{
15+
CreateContext: resourceGithubRepositoryVulnerabilityAlertsCreate,
16+
ReadContext: resourceGithubRepositoryVulnerabilityAlertsRead,
17+
UpdateContext: resourceGithubRepositoryVulnerabilityAlertsUpdate,
18+
DeleteContext: resourceGithubRepositoryVulnerabilityAlertsDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: resourceGithubRepositoryVulnerabilityAlertsImport,
21+
},
22+
23+
Schema: map[string]*schema.Schema{
24+
"repository": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
ForceNew: true,
28+
Description: "The repository name to configure vulnerability alerts for.",
29+
},
30+
"repository_id": {
31+
Type: schema.TypeInt,
32+
Computed: true,
33+
Description: "The ID of the repository to configure vulnerability alerts for.",
34+
},
35+
// TODO: Uncomment this when we are ready to support owner fields properly. https://github.com/integrations/terraform-provider-github/pull/3166#discussion_r2816053082
36+
// "owner": {
37+
// Type: schema.TypeString,
38+
// Required: true,
39+
// ForceNew: true,
40+
// Description: "The owner of the repository to configure vulnerability alerts for.",
41+
// },
42+
"enabled": {
43+
Type: schema.TypeBool,
44+
Optional: true,
45+
Default: true,
46+
Description: "Whether vulnerability alerts are enabled for the repository.",
47+
},
48+
},
49+
50+
CustomizeDiff: diffRepository,
51+
}
52+
}
53+
54+
func resourceGithubRepositoryVulnerabilityAlertsCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
55+
tflog.Info(ctx, "Creating repository vulnerability alerts", map[string]any{"id": d.Id()})
56+
meta := m.(*Owner)
57+
client := meta.v3client
58+
59+
owner := meta.name // TODO: Add owner support // d.Get("owner").(string)
60+
repoName := d.Get("repository").(string)
61+
62+
vulnerabilityAlertsEnabled := d.Get("enabled").(bool)
63+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
64+
if err != nil {
65+
return diag.FromErr(err)
66+
}
67+
if repo.GetArchived() {
68+
return diag.Errorf("cannot enable vulnerability alerts on archived repository %s/%s", owner, repoName)
69+
}
70+
if vulnerabilityAlertsEnabled {
71+
_, err := client.Repositories.EnableVulnerabilityAlerts(ctx, owner, repoName)
72+
if err != nil {
73+
return diag.FromErr(err)
74+
}
75+
} else {
76+
_, err := client.Repositories.DisableVulnerabilityAlerts(ctx, owner, repoName)
77+
if err != nil {
78+
return diag.FromErr(err)
79+
}
80+
}
81+
82+
d.SetId(strconv.Itoa(int(repo.GetID())))
83+
84+
if err = d.Set("repository_id", repo.GetID()); err != nil {
85+
return diag.FromErr(err)
86+
}
87+
88+
return nil
89+
}
90+
91+
func resourceGithubRepositoryVulnerabilityAlertsRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
92+
tflog.Info(ctx, "Reading repository vulnerability alerts", map[string]any{"id": d.Id()})
93+
meta := m.(*Owner)
94+
client := meta.v3client
95+
96+
owner := meta.name // TODO: Add owner support // d.Get("owner").(string)
97+
repoName := d.Get("repository").(string)
98+
vulnerabilityAlertsEnabled, resp, err := client.Repositories.GetVulnerabilityAlerts(ctx, owner, repoName)
99+
if err != nil {
100+
if resp.StatusCode == http.StatusNotFound {
101+
d.SetId("")
102+
return diag.Errorf("vulnerability alerts don't exist for repository %s/%s, removing resource from state", owner, repoName)
103+
}
104+
return diag.Errorf("error reading repository vulnerability alerts: %s", err.Error())
105+
}
106+
// If no error, but the response status code is 404, we need to check if the repository is accessible.
107+
if resp.StatusCode == http.StatusNotFound {
108+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
109+
if err != nil {
110+
return diag.Errorf("repository doesn't exist anymore, please remove the resource from your configuration: %s", err.Error())
111+
}
112+
if repo.GetArchived() {
113+
return diag.Errorf("repository %s/%s is archived, please remove the resource from your configuration", owner, repoName)
114+
}
115+
}
116+
tflog.Debug(ctx, "Setting vulnerability alerts enabled state", map[string]any{"owner": owner, "repo_name": repoName, "vulnerability_alerts_enabled": vulnerabilityAlertsEnabled, "response_status": resp.StatusCode})
117+
if err = d.Set("enabled", vulnerabilityAlertsEnabled); err != nil {
118+
return diag.FromErr(err)
119+
}
120+
121+
return nil
122+
}
123+
124+
func resourceGithubRepositoryVulnerabilityAlertsUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
125+
tflog.Info(ctx, "Updating repository vulnerability alerts", map[string]any{"id": d.Id()})
126+
meta := m.(*Owner)
127+
client := meta.v3client
128+
129+
owner := meta.name // TODO: Add owner support // d.Get("owner").(string)
130+
repoName := d.Get("repository").(string)
131+
132+
vulnerabilityAlertsEnabled := d.Get("enabled").(bool)
133+
if vulnerabilityAlertsEnabled {
134+
_, err := client.Repositories.EnableVulnerabilityAlerts(ctx, owner, repoName)
135+
if err != nil {
136+
return diag.FromErr(err)
137+
}
138+
} else {
139+
_, err := client.Repositories.DisableVulnerabilityAlerts(ctx, owner, repoName)
140+
if err != nil {
141+
return diag.FromErr(err)
142+
}
143+
}
144+
145+
return nil
146+
}
147+
148+
func resourceGithubRepositoryVulnerabilityAlertsDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
149+
tflog.Info(ctx, "Deleting repository vulnerability alerts", map[string]any{"id": d.Id()})
150+
meta := m.(*Owner)
151+
client := meta.v3client
152+
153+
owner := meta.name // TODO: Add owner support // d.Get("owner").(string)
154+
repoName := d.Get("repository").(string)
155+
_, err := client.Repositories.DisableVulnerabilityAlerts(ctx, owner, repoName)
156+
if err != nil {
157+
return diag.FromErr(handleArchivedRepoDelete(err, "repository vulnerability alerts", d.Id(), owner, repoName))
158+
}
159+
160+
return nil
161+
}
162+
163+
func resourceGithubRepositoryVulnerabilityAlertsImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) {
164+
tflog.Debug(ctx, "Importing repository vulnerability alerts", map[string]any{"id": d.Id()})
165+
repoName := d.Id()
166+
// if err := d.Set("owner", repoOwner); err != nil { // TODO: Add owner support
167+
// return nil, err
168+
// }
169+
if err := d.Set("repository", repoName); err != nil {
170+
return nil, err
171+
}
172+
173+
meta := m.(*Owner)
174+
owner := meta.name
175+
client := meta.v3client
176+
177+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
178+
if err != nil {
179+
return nil, err
180+
}
181+
182+
d.SetId(strconv.Itoa(int(repo.GetID())))
183+
184+
if err = d.Set("repository_id", repo.GetID()); err != nil {
185+
return nil, err
186+
}
187+
return []*schema.ResourceData{d}, nil
188+
}

0 commit comments

Comments
 (0)