-
Notifications
You must be signed in to change notification settings - Fork 225
/
clean.go
151 lines (129 loc) · 4.19 KB
/
clean.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Apache License 2.0.
* See the file "LICENSE" for details.
*/
package main
import (
"context"
"flag"
"fmt"
"time"
"github.com/elastic/otel-profiling-agent/libpf"
"github.com/elastic/otel-profiling-agent/utils/coredump/modulestore"
"github.com/peterbourgon/ff/v3/ffcli"
log "github.com/sirupsen/logrus"
)
type cleanCmd struct {
store *modulestore.Store
// User-specified command line arguments.
local, remote, temp, dry bool
minAge uint64
}
func newCleanCmd(store *modulestore.Store) *ffcli.Command {
cmd := cleanCmd{store: store}
set := flag.NewFlagSet("clean", flag.ExitOnError)
set.BoolVar(&cmd.temp, "temp", true, "Delete lingering temporary files in the local cache")
set.BoolVar(&cmd.local, "local", true, "Clean the local cache")
set.BoolVar(&cmd.remote, "remote", false, "Clean the remote storage")
set.BoolVar(&cmd.dry, "dry-run", false, "Perform a dry-run (don't actually delete)")
set.Uint64Var(&cmd.minAge, "min-age", 6*30,
"Minimum module age to remove from remote, in days (default: 6 months)")
return &ffcli.Command{
Name: "clean",
ShortUsage: "clean [flags]",
ShortHelp: "Remove unreferenced files in the module store",
FlagSet: set,
Exec: cmd.exec,
}
}
func (cmd *cleanCmd) exec(context.Context, []string) error {
referenced, err := collectReferencedIDs()
if err != nil {
return fmt.Errorf("failed to collect referenced IDs")
}
for _, task := range []struct {
enabled bool
fn func(libpf.Set[modulestore.ID]) error
}{
{cmd.temp, cmd.cleanTemp},
{cmd.local, cmd.cleanLocal},
{cmd.remote, cmd.cleanRemote},
} {
if task.enabled {
if err := task.fn(referenced); err != nil {
return err
}
}
}
return nil
}
func (cmd *cleanCmd) cleanTemp(libpf.Set[modulestore.ID]) error {
if err := cmd.store.RemoveLocalTempFiles(); err != nil {
return fmt.Errorf("failed to delete temp files: %w", err)
}
return nil
}
func (cmd *cleanCmd) cleanLocal(referenced libpf.Set[modulestore.ID]) error {
localModules, err := cmd.store.ListLocalModules()
if err != nil {
return fmt.Errorf("failed to read local cache contents: %w", err)
}
for module := range localModules {
if _, exists := referenced[module]; exists {
continue
}
log.Infof("Removing local module `%s`", module.String())
if !cmd.dry {
if err := cmd.store.RemoveLocalModule(module); err != nil {
return fmt.Errorf("failed to delete module: %w", err)
}
}
}
return nil
}
func (cmd *cleanCmd) cleanRemote(referenced libpf.Set[modulestore.ID]) error {
remoteModules, err := cmd.store.ListRemoteModules()
if err != nil {
return fmt.Errorf("failed to receive remote module list: %w", err)
}
for module, lastChanged := range remoteModules {
if _, exists := referenced[module]; exists {
continue
}
if time.Since(lastChanged) < time.Duration(cmd.minAge)*24*time.Hour {
// In order to prevent us from accidentally deleting modules uploaded for tests
// proposed on other branches (but not yet merged with the current branch), we check
// whether the module was recently uploaded before deleting it.
log.Infof("Module `%s` is unreferenced, but was uploaded recently (%s). Skipping.",
module.String(), lastChanged)
continue
}
log.Infof("Deleting unreferenced module `%s` (uploaded: %s)", module.String(), lastChanged)
if !cmd.dry {
if err = cmd.store.RemoveRemoteModule(module); err != nil {
return fmt.Errorf("failed to delete remote module: %w", err)
}
}
}
return nil
}
// collectReferencedIDs gathers a set of all modules referenced from all testcases.
func collectReferencedIDs() (libpf.Set[modulestore.ID], error) {
cases, err := findTestCases(false)
if err != nil {
return nil, fmt.Errorf("failed to find test cases: %w", err)
}
referenced := libpf.Set[modulestore.ID]{}
for _, path := range cases {
test, err := readTestCase(path)
if err != nil {
return nil, fmt.Errorf("failed to read test case: %w", err)
}
referenced[test.CoredumpRef] = libpf.Void{}
for _, module := range test.Modules {
referenced[module.Ref] = libpf.Void{}
}
}
return referenced, nil
}