Skip to content

Commit d39df5a

Browse files
authored
Merge pull request #124 from gman0/backport-122
(Backport #122) automount: shut down automount daemon with SIGKILL
2 parents 729bbcc + ebbe15f commit d39df5a

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)