4
4
"context"
5
5
"io"
6
6
"os"
7
+ "os/signal"
7
8
"sync"
9
+ "syscall"
8
10
"time"
9
11
10
12
"github.com/ctfer-io/chall-manager/api/v1/challenge"
@@ -68,6 +70,14 @@ func main() {
68
70
Destination : & serviceName ,
69
71
Usage : "Override the service name. Useful when deploying multiple instances to filter signals." ,
70
72
},
73
+ & cli.DurationFlag {
74
+ Name : "ticker" ,
75
+ EnvVars : []string {"TICKER" },
76
+ Usage : `If set, define the tick between 2 run of the janitor. ` +
77
+ `This mode is not recommended and should be preferred to a cron or an equivalent.` ,
78
+ // Not recommended because the janitor was not made to be a long-running software.
79
+ // It was not optimised in this way, despite it should work fine.
80
+ },
71
81
},
72
82
Action : run ,
73
83
Authors : []* cli.Author {
@@ -93,41 +103,97 @@ func main() {
93
103
}
94
104
}
95
105
96
- func run (ctx * cli.Context ) error {
106
+ func run (c * cli.Context ) error {
97
107
opts := []grpc.DialOption {
98
108
grpc .WithTransportCredentials (insecure .NewCredentials ()),
99
109
}
100
110
if tracing {
101
111
opts = append (opts , grpc .WithStatsHandler (otelgrpc .NewClientHandler ()))
102
112
103
113
// Set up OpenTelemetry.
104
- otelShutdown , err := setupOtelSDK (ctx .Context )
114
+ otelShutdown , err := setupOtelSDK (c .Context )
105
115
if err != nil {
106
116
return err
107
117
}
108
118
// Handle shutdown properly so nothing leaks.
109
119
defer func () {
110
- err = multierr .Append (err , otelShutdown (ctx .Context ))
120
+ err = multierr .Append (err , otelShutdown (c .Context ))
111
121
}()
112
122
}
113
123
114
124
logger := Log ()
115
125
116
- cli , err := grpc .NewClient (ctx .String ("url" ), opts ... )
126
+ // Create context that listens for the interrupt signal from the OS
127
+ ctx , stop := signal .NotifyContext (c .Context , syscall .SIGINT , syscall .SIGTERM )
128
+ defer stop ()
129
+
130
+ cli , err := grpc .NewClient (c .String ("url" ), opts ... )
117
131
if err != nil {
118
132
return err
119
133
}
120
- store := challenge .NewChallengeStoreClient (cli )
121
- manager := instance .NewInstanceManagerClient (cli )
122
134
defer func (cli * grpc.ClientConn ) {
123
135
if err := cli .Close (); err != nil {
124
- logger .Error (ctx . Context , "closing gRPC connection" , zap .Error (err ))
136
+ logger .Error (ctx , "closing gRPC connection" , zap .Error (err ))
125
137
}
126
138
}(cli )
127
139
128
- span := trace .SpanFromContext (ctx .Context )
140
+ if c .IsSet ("ticker" ) {
141
+ if err := janitorWithTicker (ctx , cli , c .Duration ("ticker" )); err != nil {
142
+ return err
143
+ }
144
+ } else {
145
+ if err := janitor (ctx , cli ); err != nil {
146
+ return err
147
+ }
148
+ }
149
+
150
+ // Listen for the interrupt signal
151
+ <- ctx .Done ()
152
+
153
+ // Restore default behavior on the interrupt signal
154
+ stop ()
155
+ logger .Info (ctx , "shutting down gracefully" )
156
+
157
+ return nil
158
+ }
159
+
160
+ func janitorWithTicker (ctx context.Context , cli * grpc.ClientConn , d time.Duration ) error {
161
+ logger := Log ()
162
+ ticker := time .NewTicker (d )
163
+ wg := sync.WaitGroup {}
164
+
165
+ run := true
166
+ for run {
167
+ select {
168
+ case <- ticker .C :
169
+ wg .Add (1 )
170
+ go func (ctx context.Context , cli * grpc.ClientConn ) {
171
+ defer wg .Done ()
172
+
173
+ if err := janitor (ctx , cli ); err != nil {
174
+ logger .Error (ctx , "janitoring did not succeed" , zap .Error (err ))
175
+ }
176
+ }(ctx , cli )
177
+
178
+ case <- ctx .Done ():
179
+ ticker .Stop ()
180
+ run = false
181
+ }
182
+ }
183
+ wg .Wait ()
184
+ return nil
185
+ }
186
+
187
+ func janitor (ctx context.Context , cli * grpc.ClientConn ) error {
188
+ logger := Log ()
189
+ logger .Info (ctx , "starting janitoring" )
190
+
191
+ store := challenge .NewChallengeStoreClient (cli )
192
+ manager := instance .NewInstanceManagerClient (cli )
193
+
194
+ span := trace .SpanFromContext (ctx )
129
195
span .AddEvent ("querying challenges" )
130
- challs , err := store .QueryChallenge (ctx . Context , nil )
196
+ challs , err := store .QueryChallenge (ctx , nil )
131
197
if err != nil {
132
198
return err
133
199
}
@@ -139,7 +205,7 @@ func run(ctx *cli.Context) error {
139
205
}
140
206
return err
141
207
}
142
- ctx := WithChallengeID (ctx . Context , chall .Id )
208
+ ctx := WithChallengeID (ctx , chall .Id )
143
209
144
210
// Don't janitor if the challenge has no dates configured
145
211
if chall .Timeout == nil && chall .Until == nil {
@@ -172,6 +238,9 @@ func run(ctx *cli.Context) error {
172
238
}
173
239
wg .Wait ()
174
240
}
241
+
242
+ logger .Info (ctx , "completed janitoring" )
243
+
175
244
return nil
176
245
}
177
246
0 commit comments