@@ -24,8 +24,10 @@ import (
24
24
goexec "os/exec"
25
25
"os/signal"
26
26
"path"
27
+ "sync/atomic"
27
28
"syscall"
28
29
30
+ "github.com/cvmfs-contrib/cvmfs-csi/internal/cvmfs/env"
29
31
"github.com/cvmfs-contrib/cvmfs-csi/internal/exec"
30
32
"github.com/cvmfs-contrib/cvmfs-csi/internal/log"
31
33
)
@@ -245,6 +247,19 @@ func RunBlocking() error {
245
247
246
248
if log .LevelEnabled (log .LevelDebug ) {
247
249
args = append (args , "--verbose" )
250
+
251
+ // Log info about autofs mount in /cvmfs.
252
+
253
+ isAutofs , err := IsAutofs ("/cvmfs" )
254
+ if err != nil {
255
+ log .Fatalf ("Failed to stat /cvmfs: %v" , err )
256
+ }
257
+
258
+ if isAutofs {
259
+ log .Debugf ("autofs already mounted in /cvmfs, automount daemon will reconnect..." )
260
+ } else {
261
+ log .Debugf ("autofs not mounted in /cvmfs, automount daemon will mount it now..." )
262
+ }
248
263
}
249
264
250
265
if log .LevelEnabled (log .LevelTrace ) {
@@ -276,20 +291,62 @@ func RunBlocking() error {
276
291
277
292
// Catch SIGTERM and SIGKILL and forward it to the automount process.
278
293
279
- sigCh := make (chan os.Signal , 1 )
294
+ autofsTryCleanAtExit := env .GetAutofsTryCleanAtExit ()
295
+
296
+ sigCh := make (chan os.Signal , 2 )
280
297
defer close (sigCh )
281
298
299
+ var exitedWithSigTerm atomic.Bool
300
+
282
301
go func () {
283
302
for {
284
- if sig , more := <- sigCh ; more {
285
- cmd .Process .Signal (sig )
286
- } else {
303
+ sig , more := <- sigCh
304
+ if ! more {
287
305
break
288
306
}
307
+
308
+ if ! autofsTryCleanAtExit && sig == syscall .SIGTERM {
309
+ // automount daemon unmounts the autofs root in /cvmfs upon
310
+ // receiving SIGTERM. This makes it impossible to reconnect
311
+ // the daemon to the mount later, so all consumer Pods will
312
+ // loose their mounts CVMFS, without the possibility of restoring
313
+ // them (unless these Pods are restarted too). The implication
314
+ // is that the nodeplugin is just being restarted, and will be
315
+ // needed again.
316
+ //
317
+ // SIGKILL is handled differently in automount, as this forces
318
+ // the daemon to skip the cleanup at exit, leaving the autofs
319
+ // mount behind and making it possible to reconnect to it later.
320
+ // We make a use of this, and unless the admin doesn't explicitly
321
+ // ask for cleanup with AUTOFS_TRY_CLEAN_AT_EXIT env var, no cleanup
322
+ // is done.
323
+ //
324
+ // Also, we intentionally don't unmount the existing autofs-managed
325
+ // mounts inside /cvmfs, so that any existing consumers receive ENOTCONN
326
+ // (due to broken FUSE mounts), so that accidental `mkdir -p` won't
327
+ // succeed. They are cleaned by the daemon on startup.
328
+ //
329
+ // TODO: remove this once the automount daemon supports skipping
330
+ // cleanup (via a command line flag).
331
+
332
+ log .Debugf ("Sending SIGKILL to automount daemon" )
333
+
334
+ exitedWithSigTerm .Store (true )
335
+ cmd .Process .Signal (syscall .SIGKILL )
336
+ break
337
+ }
338
+
339
+ cmd .Process .Signal (sig )
289
340
}
290
341
}()
291
342
292
- signal .Notify (sigCh , syscall .SIGTERM , syscall .SIGKILL )
343
+ shutdownSignals := []os.Signal {
344
+ syscall .SIGINT ,
345
+ syscall .SIGTERM ,
346
+ syscall .SIGKILL ,
347
+ }
348
+
349
+ signal .Notify (sigCh , shutdownSignals ... )
293
350
294
351
// Start automount daemon.
295
352
@@ -303,7 +360,7 @@ func RunBlocking() error {
303
360
304
361
cmd .Wait ()
305
362
306
- if cmd .ProcessState .ExitCode () != 0 {
363
+ if ! exitedWithSigTerm . Load () && cmd .ProcessState .ExitCode () != 0 {
307
364
log .Fatalf (fmt .Sprintf ("automount[%d] has exited unexpectedly: %s" , cmd .Process .Pid , cmd .ProcessState ))
308
365
}
309
366
0 commit comments