Skip to content

Commit ebbe15f

Browse files
committed
automount: shut down automount daemon with SIGKILL
automount daemon unmounts the autofs root in /cvmfs upon receiving SIGTERM. This makes it impossible to reconnect the daemon to the mount later, so all consumer Pods will loose their mounts CVMFS, without the possibility of restoring them (unless these Pods are restarted too). The implication is that the nodeplugin is just being restarted, and will be needed again. SIGKILL is handled differently in automount, as this forces the daemon to skip the cleanup at exit, leaving the autofs mount behind and making it possible to reconnect to it later. We make a use of this, and unless the admin doesn't explicitly ask for cleanup with AUTOFS_TRY_CLEAN_AT_EXIT env var, no cleanup is done. Cherry-pick f1d7ee2 (#122)
1 parent 729bbcc commit ebbe15f

File tree

4 files changed

+132
-6
lines changed

4 files changed

+132
-6
lines changed

cmd/automount-runner/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323

2424
"github.com/cvmfs-contrib/cvmfs-csi/internal/cvmfs/automount"
25+
"github.com/cvmfs-contrib/cvmfs-csi/internal/cvmfs/env"
2526
"github.com/cvmfs-contrib/cvmfs-csi/internal/log"
2627
cvmfsversion "github.com/cvmfs-contrib/cvmfs-csi/internal/version"
2728

@@ -54,6 +55,7 @@ func main() {
5455

5556
log.Infof("automount-runner for CVMFS CSI plugin version %s", cvmfsversion.FullVersion())
5657
log.Infof("Command line arguments %v", os.Args)
58+
log.Infof("Environment variables %s", env.StringAutofsTryCleanAtExit())
5759

5860
err := automount.Init(&automount.Opts{
5961
UnmountTimeoutSeconds: *unmountTimeoutSeconds,

docs/uninstalling.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Uninstalling cvmfs-csi driver
2+
3+
The nodeplugin Pods store various resources on the node hosts they are running on:
4+
* autofs mount and the respective inner CVMFS mounts,
5+
* CVMFS client cache.
6+
7+
By default, the nodeplugin Pod leaves autofs and its respective inner mounts on the node
8+
in `/var/cvmfs`. They may need to be unmounted recursively. To do that, you can set
9+
`AUTOFS_TRY_CLEAN_AT_EXIT` environment variable to `true` in nodeplugin's DaemonSet and restart
10+
the Pods. On the next exit, they will be unmounted.
11+
12+
```
13+
kubectl set env daemonset -l app=cvmfs-csi,component=nodeplugin AUTOFS_TRY_CLEAN_AT_EXIT=true
14+
# Restarting nodeplugin Pods needs attention, as this may break existing mounts.
15+
# They will be restored once the Pods come back up.
16+
kubectl delete pods -l app=cvmfs-csi,component=nodeplugin
17+
```
18+
19+
The CVMFS client cache is stored by default in `/var/lib/cvmfs.csi.cern.ch/cache`.
20+
This directory is not deleted automatically, and manual intervention is currently needed.

internal/cvmfs/automount/automount.go

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
goexec "os/exec"
2525
"os/signal"
2626
"path"
27+
"sync/atomic"
2728
"syscall"
2829

30+
"github.com/cvmfs-contrib/cvmfs-csi/internal/cvmfs/env"
2931
"github.com/cvmfs-contrib/cvmfs-csi/internal/exec"
3032
"github.com/cvmfs-contrib/cvmfs-csi/internal/log"
3133
)
@@ -245,6 +247,19 @@ func RunBlocking() error {
245247

246248
if log.LevelEnabled(log.LevelDebug) {
247249
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+
}
248263
}
249264

250265
if log.LevelEnabled(log.LevelTrace) {
@@ -276,20 +291,62 @@ func RunBlocking() error {
276291

277292
// Catch SIGTERM and SIGKILL and forward it to the automount process.
278293

279-
sigCh := make(chan os.Signal, 1)
294+
autofsTryCleanAtExit := env.GetAutofsTryCleanAtExit()
295+
296+
sigCh := make(chan os.Signal, 2)
280297
defer close(sigCh)
281298

299+
var exitedWithSigTerm atomic.Bool
300+
282301
go func() {
283302
for {
284-
if sig, more := <-sigCh; more {
285-
cmd.Process.Signal(sig)
286-
} else {
303+
sig, more := <-sigCh
304+
if !more {
287305
break
288306
}
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)
289340
}
290341
}()
291342

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...)
293350

294351
// Start automount daemon.
295352

@@ -303,7 +360,7 @@ func RunBlocking() error {
303360

304361
cmd.Wait()
305362

306-
if cmd.ProcessState.ExitCode() != 0 {
363+
if !exitedWithSigTerm.Load() && cmd.ProcessState.ExitCode() != 0 {
307364
log.Fatalf(fmt.Sprintf("automount[%d] has exited unexpectedly: %s", cmd.Process.Pid, cmd.ProcessState))
308365
}
309366

internal/cvmfs/env/env.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright CERN.
2+
//
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
package env
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"strconv"
23+
)
24+
25+
const (
26+
// Boolean value. By default, when exiting, automount daemon is sent
27+
// SIGKILL signal forcing it to skip its clean up procedure, leaving
28+
// the autofs mount behind. This is needed for the daemon to be able
29+
// to reconnect to the autofs mount when the nodeplugin Pod is being
30+
// restarted.
31+
//
32+
// Setting the value of this environment value to TRUE overrides this,
33+
// and allows the daemon to do the clean up. This is useful when
34+
// e.g. uninstalling the eosxd-csi driver.
35+
AutofsTryCleanAtExit = "AUTOFS_TRY_CLEAN_AT_EXIT"
36+
)
37+
38+
func GetAutofsTryCleanAtExit() bool {
39+
strVal := os.Getenv(AutofsTryCleanAtExit)
40+
boolVal, _ := strconv.ParseBool(strVal)
41+
42+
return boolVal
43+
}
44+
45+
func StringAutofsTryCleanAtExit() string {
46+
return fmt.Sprintf("%s=\"%v\"", AutofsTryCleanAtExit, GetAutofsTryCleanAtExit())
47+
}

0 commit comments

Comments
 (0)