Skip to content

Commit 0b7a285

Browse files
committed
chore: cleanup firewalls in cleanup script
1 parent c72f64c commit 0b7a285

File tree

3 files changed

+293
-29
lines changed

3 files changed

+293
-29
lines changed

cmd/cleanup/main.go

Lines changed: 161 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,37 @@ func main() {
3737

3838
doClient := digitalocean.NewGodoClient(*token)
3939

40-
logger.Info("Starting droplet cleanup",
40+
logger.Info("Starting resource cleanup",
4141
zap.String("prefix", *namePrefix),
4242
zap.String("long_running_tag", *longRunning),
4343
zap.Bool("dry_run", *dryRun))
4444

45-
if err := cleanupDroplets(ctx, doClient, logger); err != nil {
45+
skippedPrefixes, err := getSkippedDropletPrefixes(ctx, doClient, logger)
46+
if err != nil {
47+
logger.Fatal("Failed to get skipped droplet prefixes", zap.Error(err))
48+
}
49+
50+
if err := cleanupDroplets(ctx, doClient, logger, skippedPrefixes); err != nil {
4651
logger.Fatal("Failed to cleanup droplets", zap.Error(err))
4752
}
4853

49-
logger.Info("Droplet cleanup completed successfully")
54+
if err := cleanupFirewalls(ctx, doClient, logger, skippedPrefixes); err != nil {
55+
logger.Fatal("Failed to cleanup firewalls", zap.Error(err))
56+
}
57+
58+
logger.Info("Resource cleanup completed successfully")
59+
}
60+
61+
func extractPrefix(dropletName string) string {
62+
// Extract prefix pattern like "petri-ib-XXXXXX" from "petri-ib-XXXXXX-validator-0"
63+
parts := strings.Split(dropletName, "-")
64+
if len(parts) >= 3 {
65+
return strings.Join(parts[:3], "-")
66+
}
67+
return dropletName
5068
}
5169

52-
func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *zap.Logger) error {
70+
func getSkippedDropletPrefixes(ctx context.Context, client digitalocean.DoClient, logger *zap.Logger) (map[string]bool, error) {
5371
opts := &godo.ListOptions{
5472
Page: 1,
5573
PerPage: 200,
@@ -59,7 +77,7 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
5977
for {
6078
droplets, err := client.ListDroplets(ctx, opts)
6179
if err != nil {
62-
return fmt.Errorf("failed to list droplets: %w", err)
80+
return nil, fmt.Errorf("failed to list droplets: %w", err)
6381
}
6482

6583
allDroplets = append(allDroplets, droplets...)
@@ -71,15 +89,21 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
7189
opts.Page++
7290
}
7391

74-
logger.Info("Retrieved droplets", zap.Int("total_count", len(allDroplets)))
75-
76-
var dropletsToDelete []godo.Droplet
92+
skippedPrefixes := make(map[string]bool)
7793
now := time.Now()
94+
7895
for _, droplet := range allDroplets {
96+
// Skip non-petri droplets
7997
if !strings.HasPrefix(droplet.Name, *namePrefix) {
8098
continue
8199
}
82100

101+
prefix := extractPrefix(droplet.Name)
102+
if skippedPrefixes[prefix] {
103+
continue
104+
}
105+
106+
// Skip droplets with the LONG_RUNNING tag
83107
hasLongRunningTag := false
84108
for _, tag := range droplet.Tags {
85109
if tag == *longRunning {
@@ -89,29 +113,69 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
89113
}
90114

91115
if hasLongRunningTag {
92-
logger.Info("Skipping droplet with long-running tag",
93-
zap.String("name", droplet.Name),
94-
zap.Int("id", droplet.ID))
116+
prefix := extractPrefix(droplet.Name)
117+
skippedPrefixes[prefix] = true
118+
logger.Info("Marking droplet as skipped (long-running)",
119+
zap.String("droplet_name", droplet.Name))
95120
continue
96121
}
97122

98-
// Parse the creation time and skip if created in the last 30 minutes
123+
// Skip if droplet was created in the last 30 minutes
99124
createdAt, err := time.Parse(time.RFC3339, droplet.Created)
100125
if err != nil {
101-
logger.Warn("Failed to parse droplet creation time, skipping",
126+
logger.Error("Failed to parse droplet creation time, marking as skipped",
102127
zap.String("name", droplet.Name),
103-
zap.Int("id", droplet.ID),
104128
zap.String("created_at", droplet.Created),
105129
zap.Error(err))
130+
prefix := extractPrefix(droplet.Name)
131+
skippedPrefixes[prefix] = true
106132
continue
107133
}
108134

109135
if now.Sub(createdAt) < 30*time.Minute {
110-
logger.Info("Skipping recently created droplet",
111-
zap.String("name", droplet.Name),
112-
zap.Int("id", droplet.ID),
113-
zap.String("created_at", droplet.Created),
114-
zap.Duration("age", now.Sub(createdAt)))
136+
prefix := extractPrefix(droplet.Name)
137+
skippedPrefixes[prefix] = true
138+
logger.Info("Marking droplet as skipped (too recent)",
139+
zap.String("droplet_name", droplet.Name),
140+
zap.String("created_at", droplet.Created))
141+
}
142+
}
143+
144+
return skippedPrefixes, nil
145+
}
146+
147+
func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *zap.Logger, skippedPrefixes map[string]bool) error {
148+
opts := &godo.ListOptions{
149+
Page: 1,
150+
PerPage: 200,
151+
}
152+
153+
var allDroplets []godo.Droplet
154+
for {
155+
droplets, err := client.ListDroplets(ctx, opts)
156+
if err != nil {
157+
return fmt.Errorf("failed to list droplets: %w", err)
158+
}
159+
160+
allDroplets = append(allDroplets, droplets...)
161+
162+
if len(droplets) < opts.PerPage {
163+
break
164+
}
165+
166+
opts.Page++
167+
}
168+
169+
logger.Info("Retrieved droplets", zap.Int("total_count", len(allDroplets)))
170+
171+
var dropletsToDelete []godo.Droplet
172+
for _, droplet := range allDroplets {
173+
if !strings.HasPrefix(droplet.Name, *namePrefix) {
174+
continue
175+
}
176+
177+
if shouldSkipResource(droplet.Name, skippedPrefixes) {
178+
logger.Info("Skipping droplet", zap.String("name", droplet.Name))
115179
continue
116180
}
117181

@@ -121,14 +185,9 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
121185
logger.Info("Found droplets to delete", zap.Int("count", len(dropletsToDelete)))
122186

123187
if *dryRun {
124-
logger.Info("Dry run mode - would delete the following droplets:")
125-
for _, droplet := range dropletsToDelete {
126-
logger.Info("Would delete droplet",
127-
zap.String("name", droplet.Name),
128-
zap.Int("id", droplet.ID),
129-
zap.Strings("tags", droplet.Tags),
130-
zap.String("created_at", droplet.Created))
131-
}
188+
logger.Info("Dry run mode - would delete droplets",
189+
zap.Int("count", len(dropletsToDelete)),
190+
zap.Any("droplets", dropletsToDelete))
132191
return nil
133192
}
134193

@@ -150,8 +209,6 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
150209
logger.Info("Successfully deleted droplet",
151210
zap.String("name", droplet.Name),
152211
zap.Int("id", droplet.ID))
153-
154-
time.Sleep(100 * time.Millisecond)
155212
}
156213

157214
logger.Info("Cleanup completed",
@@ -160,3 +217,78 @@ func cleanupDroplets(ctx context.Context, client digitalocean.DoClient, logger *
160217

161218
return nil
162219
}
220+
221+
func shouldSkipResource(resourceName string, skippedPrefixes map[string]bool) bool {
222+
prefix := extractPrefix(resourceName)
223+
return skippedPrefixes[prefix]
224+
}
225+
226+
func cleanupFirewalls(ctx context.Context, client digitalocean.DoClient, logger *zap.Logger, skippedPrefixes map[string]bool) error {
227+
opts := &godo.ListOptions{
228+
Page: 1,
229+
PerPage: 200,
230+
}
231+
232+
var allFirewalls []godo.Firewall
233+
for {
234+
firewalls, err := client.ListFirewalls(ctx, opts)
235+
if err != nil {
236+
return fmt.Errorf("failed to list firewalls: %w", err)
237+
}
238+
239+
allFirewalls = append(allFirewalls, firewalls...)
240+
241+
if len(firewalls) < opts.PerPage {
242+
break
243+
}
244+
245+
opts.Page++
246+
}
247+
248+
logger.Info("Retrieved firewalls", zap.Int("total_count", len(allFirewalls)))
249+
250+
var firewallsToDelete []godo.Firewall
251+
for _, firewall := range allFirewalls {
252+
if !strings.HasPrefix(firewall.Name, *namePrefix) {
253+
continue
254+
}
255+
256+
if shouldSkipResource(firewall.Name, skippedPrefixes) {
257+
logger.Info("Skipping firewall ",
258+
zap.String("name", firewall.Name))
259+
continue
260+
}
261+
262+
firewallsToDelete = append(firewallsToDelete, firewall)
263+
}
264+
265+
logger.Info("Found firewalls to delete", zap.Int("count", len(firewallsToDelete)))
266+
267+
if *dryRun {
268+
logger.Info("Dry run mode - would delete firewalls",
269+
zap.Int("count", len(firewallsToDelete)),
270+
zap.Any("firewalls", firewallsToDelete))
271+
return nil
272+
}
273+
274+
var deletedCount int
275+
for _, firewall := range firewallsToDelete {
276+
logger.Info("Deleting firewall", zap.String("name", firewall.Name))
277+
278+
if err := client.DeleteFirewall(ctx, firewall.ID); err != nil {
279+
logger.Error("Failed to delete firewall",
280+
zap.String("name", firewall.Name),
281+
zap.Error(err))
282+
continue
283+
}
284+
285+
deletedCount++
286+
logger.Info("Successfully deleted firewall",
287+
zap.String("name", firewall.Name))
288+
}
289+
290+
logger.Info("Firewall cleanup completed",
291+
zap.Int("deleted_count", deletedCount))
292+
293+
return nil
294+
}

petri/core/provider/digitalocean/client.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type DoClient interface {
2020
// Firewall operations
2121
CreateFirewall(ctx context.Context, req *godo.FirewallRequest) (*godo.Firewall, error)
2222
GetFirewall(ctx context.Context, firewallID string) (*godo.Firewall, error)
23+
ListFirewalls(ctx context.Context, opts *godo.ListOptions) ([]godo.Firewall, error)
2324
DeleteFirewall(ctx context.Context, firewallID string) error
2425

2526
// Domain operations
@@ -34,6 +35,7 @@ type DoClient interface {
3435

3536
// Tag operations
3637
CreateTag(ctx context.Context, req *godo.TagCreateRequest) (*godo.Tag, error)
38+
ListTags(ctx context.Context, opts *godo.ListOptions) ([]godo.Tag, error)
3739
DeleteTag(ctx context.Context, tag string) error
3840
}
3941

@@ -115,6 +117,14 @@ func (c *godoClient) CreateFirewall(ctx context.Context, req *godo.FirewallReque
115117
return firewall, nil
116118
}
117119

120+
func (c *godoClient) ListFirewalls(ctx context.Context, opts *godo.ListOptions) ([]godo.Firewall, error) {
121+
firewalls, res, err := c.Firewalls.List(ctx, opts)
122+
if err := checkResponse(res, err); err != nil {
123+
return nil, err
124+
}
125+
return firewalls, nil
126+
}
127+
118128
func (c *godoClient) DeleteFirewall(ctx context.Context, firewallID string) error {
119129
res, err := c.Firewalls.Delete(ctx, firewallID)
120130
return checkResponse(res, err)
@@ -183,6 +193,14 @@ func (c *godoClient) CreateTag(ctx context.Context, req *godo.TagCreateRequest)
183193
return tag, nil
184194
}
185195

196+
func (c *godoClient) ListTags(ctx context.Context, opts *godo.ListOptions) ([]godo.Tag, error) {
197+
tags, res, err := c.Tags.List(ctx, opts)
198+
if err := checkResponse(res, err); err != nil {
199+
return nil, err
200+
}
201+
return tags, nil
202+
}
203+
186204
func (c *godoClient) DeleteTag(ctx context.Context, tag string) error {
187205
res, err := c.Tags.Delete(ctx, tag)
188206
return checkResponse(res, err)

0 commit comments

Comments
 (0)