Skip to content

Commit 6128b02

Browse files
committed
Pipe automount's stdout and stderr to logging
automount's stdout and stderr are now piped to driver's logging. Two new cmd parameters added: * --automount-startup-timeout * --automount-unmount-timeout Log level 4 enables automount verbose logging. Log level 5 enables CVMFS client debug logging.
1 parent 18097c8 commit 6128b02

File tree

6 files changed

+147
-20
lines changed

6 files changed

+147
-20
lines changed

cmd/csi-cvmfsplugin/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ var (
6464

6565
hasAlienCache = flag.Bool("has-alien-cache", false, "CVMFS client is using alien cache volume")
6666
startAutomountDaemon = flag.Bool("start-automount-daemon", true, "start automount daemon when initializing CVMFS CSI driver")
67+
68+
automountDaemonStartupTimeoutSeconds = flag.Int("automount-startup-timeout", 5, "number of seconds to wait for automount daemon to start up before exiting")
69+
automountDaemonUnmountAfterIdleSeconds = flag.Int("automount-unmount-timeout", -1, "number of seconds of idle time after which an autofs-managed CVMFS mount will be unmounted. '0' means never unmount, '-1' leaves automount default option.")
6770
)
6871

6972
func printVersion() {
@@ -106,6 +109,9 @@ func main() {
106109

107110
StartAutomountDaemon: *startAutomountDaemon,
108111
HasAlienCache: *hasAlienCache,
112+
113+
AutomountDaemonStartupTimeoutSeconds: *automountDaemonStartupTimeoutSeconds,
114+
AutomountDaemonUnmountAfterIdleSeconds: *automountDaemonUnmountAfterIdleSeconds,
109115
})
110116

111117
if err != nil {

deployments/helm/cvmfs-csi/templates/nodeplugin-daemonset.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ spec:
4848
- --endpoint=$(CSI_ENDPOINT)
4949
- --drivername=$(CSI_DRIVERNAME)
5050
- --start-automount-daemon={{ .Values.startAutomountDaemon }}
51+
- --automount-startup-timeout={{ .Values.automountDaemonStartupTimeout }}
52+
- --automount-unmount-timeout={{ .Values.automountDaemonStartupTimeout }}
5153
- --role=identity,node
5254
{{- if .Values.cache.alien.enabled }}
5355
- --has-alien-cache

deployments/helm/cvmfs-csi/values.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ cvmfsCSIPluginSocketFile: csi.sock
245245
# of the daemon using this switch.
246246
startAutomountDaemon: true
247247

248+
# Number of seconds to wait for automount daemon to start up before exiting.
249+
automountDaemonStartupTimeout: 5
250+
# Number of seconds of idle time after which an autofs-managed CVMFS mount will
251+
# be unmounted. '0' means never unmount, '-1' leaves automount default option.
252+
automountDaemonUnmountTimeout: -1
253+
248254
# Chart name overrides.
249255
nameOverride: ""
250256
fullNameOverride: ""

docs/deploying.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,7 @@ CVMFS CSI driver executable accepts following set of command line arguments:
6060
|`--nodeid`|_none, required_|(string value) Unique identifier of the node on which the CVMFS CSI node plugin pod is running. Should be set to the value of `Pod.spec.nodeName`.|
6161
|`--has-alien-cache`|_false_|(boolean value) CVMFS client is using alien cache volume. The volume will be `chmod`'d with correct permissions.|
6262
|`--start-automount-daemon`|_true_|(boolean value) Whether CVMFS CSI nodeplugin Pod should run automount daemon. This is required for automounts to work. If however worker nodes are already running automount daemon (e.g. as a systemd service), you may disable running yet another instance of the daemon using this switch.|
63+
|`automount-startup-timeout`|_5_|number of seconds to wait for automount daemon to start up before exiting|
64+
|`automount-unmount-timeout`|_-1_|number of seconds of idle time after which an autofs-managed CVMFS mount will be unmounted. '0' means never unmount, '-1' leaves automount default option.|
6365
|`--role`|_none, required_|Enable driver service role (comma-separated list or repeated `--role` flags). Allowed values are: `identity`, `node`, `controller`.|
6466
|`--version`|_false_|(boolean value) Print driver version and exit.|

internal/cvmfs/driver/driver.go

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
package driver
1818

1919
import (
20+
"bufio"
2021
"bytes"
2122
"context"
2223
"fmt"
2324
"os"
2425
goexec "os/exec"
2526
"strings"
27+
"syscall"
28+
"time"
2629

2730
"github.com/cernops/cvmfs-csi/internal/cvmfs/controller"
2831
"github.com/cernops/cvmfs-csi/internal/cvmfs/identity"
@@ -67,6 +70,16 @@ type (
6770

6871
// Role under which will the driver operate.
6972
Roles map[ServiceRole]bool
73+
74+
// How many seconds to wait for automount daemon to start up,
75+
// and /cvmfs mountpoint to become available.
76+
AutomountDaemonStartupTimeoutSeconds int
77+
78+
// After how many seconds of inactivity of an autofs-managed mount
79+
// should the volume be unmounted, i.e. automount --timeout <value>.
80+
// 0: Never unmount.
81+
// -1: Leave automount's default timeout.
82+
AutomountDaemonUnmountAfterIdleSeconds int
7083
}
7184

7285
// Driver holds CVMFS-CSI driver runtime state.
@@ -230,7 +243,7 @@ func setupCvmfs(o *Opts) error {
230243

231244
if o.StartAutomountDaemon {
232245
// Start the automount daemon.
233-
if _, err := exec.CombinedOutput(goexec.Command("automount")); err != nil {
246+
if err := automountRunner(o); err != nil {
234247
return fmt.Errorf("failed to start automount daemon: %v", err)
235248
}
236249
}
@@ -243,3 +256,97 @@ func setupCvmfs(o *Opts) error {
243256

244257
return nil
245258
}
259+
260+
func automountRunner(o *Opts) error {
261+
args := []string{
262+
"--foreground",
263+
}
264+
265+
if log.LevelEnabled(log.LevelDebug) {
266+
// Enable automount verbose logging.
267+
args = append(args, "--verbose")
268+
}
269+
270+
if log.LevelEnabled(log.LevelTrace) {
271+
// automount passes -O options to the underlying fs mounts.
272+
// Enable CVMFS debug logging.
273+
args = append(args, "-O", "debug")
274+
}
275+
276+
if o.AutomountDaemonUnmountAfterIdleSeconds != -1 {
277+
args = append(args, fmt.Sprintf("--timeout=%d", o.AutomountDaemonUnmountAfterIdleSeconds))
278+
}
279+
280+
cmd := goexec.Command("automount", args...)
281+
282+
// Set-up piping output for stdout and stderr to driver's logging.
283+
284+
outp, err := cmd.StdoutPipe()
285+
if err != nil {
286+
return err
287+
}
288+
cmd.Stderr = cmd.Stdout
289+
290+
// Run automount.
291+
292+
log.Infof("Starting automount daemon prog=%s args=%v", cmd.Path, cmd.Args)
293+
if err := cmd.Start(); err != nil {
294+
return err
295+
}
296+
log.Infof("Started automount daemon PID %d", cmd.Process.Pid)
297+
298+
scanner := bufio.NewScanner(outp)
299+
scanner.Split(bufio.ScanLines)
300+
301+
go func() {
302+
// Log and wait.
303+
304+
for scanner.Scan() {
305+
log.Infof("automount[%d]: %s", cmd.Process.Pid, scanner.Text())
306+
}
307+
308+
cmd.Wait()
309+
310+
if cmd.ProcessState.ExitCode() != 0 {
311+
panic(fmt.Sprintf("automount[%d] has exited unexpectedly: %s", cmd.Process.Pid, cmd.ProcessState))
312+
}
313+
314+
log.Infof("automount[%d] has exited: %s", cmd.Process.Pid, cmd.ProcessState)
315+
}()
316+
317+
// Wait until autofs is mounted under /cvmfs.
318+
319+
retryFor := func(attempts int, f func() (bool, error)) error {
320+
trial := 0
321+
for trial < attempts {
322+
res, err := f()
323+
324+
if err != nil {
325+
return err
326+
}
327+
328+
if res {
329+
return nil
330+
}
331+
332+
trial++
333+
time.Sleep(1 * time.Second)
334+
}
335+
336+
return fmt.Errorf("timed-out while waiting for autofs to be mounted")
337+
}
338+
339+
const autofsStatfsType = 0x187
340+
341+
err = retryFor(o.AutomountDaemonStartupTimeoutSeconds, func() (bool, error) {
342+
statfs := syscall.Statfs_t{}
343+
err = syscall.Statfs("/cvmfs", &statfs)
344+
if err != nil {
345+
return false, err
346+
}
347+
348+
return statfs.Type == autofsStatfsType, nil
349+
})
350+
351+
return nil
352+
}

internal/log/log.go

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const (
2828
ReqIDContextKey = "Req-ID"
2929

3030
// klog verbosity levels.
31-
levelInfo = 0
32-
levelDebug = 4
33-
levelTrace = 5
31+
LevelInfo = 0
32+
LevelDebug = 4
33+
LevelTrace = 5
3434
)
3535

3636
func tryPrependReqID(ctx context.Context, logMsg string) string {
@@ -42,59 +42,63 @@ func tryPrependReqID(ctx context.Context, logMsg string) string {
4242
return logMsg
4343
}
4444

45+
func LevelEnabled(level int) bool {
46+
return klog.V(klog.Level(level)).Enabled()
47+
}
48+
4549
func Infof(format string, args ...interface{}) {
46-
klog.V(levelInfo).InfofDepth(1, format, args...)
50+
klog.V(LevelInfo).InfofDepth(1, format, args...)
4751
}
4852

4953
func InfofWithContext(ctx context.Context, format string, args ...interface{}) {
50-
klog.V(levelInfo).InfofDepth(1, tryPrependReqID(ctx, format), args...)
54+
klog.V(LevelInfo).InfofDepth(1, tryPrependReqID(ctx, format), args...)
5155
}
5256

5357
func InfofDepth(depth int, format string, args ...interface{}) {
54-
klog.V(levelInfo).InfofDepth(depth, format, args...)
58+
klog.V(LevelInfo).InfofDepth(depth, format, args...)
5559
}
5660

5761
func InfofWithContextDepth(ctx context.Context, depth int, format string, args ...interface{}) {
58-
klog.V(levelInfo).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
62+
klog.V(LevelInfo).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
5963
}
6064

6165
func Debugf(format string, args ...interface{}) {
62-
klog.V(levelDebug).InfofDepth(1, format, args...)
66+
klog.V(LevelDebug).InfofDepth(1, format, args...)
6367
}
6468

6569
func DebugfWithContext(ctx context.Context, format string, args ...interface{}) {
66-
if klog.V(levelDebug).Enabled() {
67-
klog.V(levelDebug).InfofDepth(1, tryPrependReqID(ctx, format), args...)
70+
if klog.V(LevelDebug).Enabled() {
71+
klog.V(LevelDebug).InfofDepth(1, tryPrependReqID(ctx, format), args...)
6872
}
6973
}
7074

7175
func DebugfDepth(depth int, format string, args ...interface{}) {
72-
klog.V(levelDebug).InfofDepth(depth, format, args...)
76+
klog.V(LevelDebug).InfofDepth(depth, format, args...)
7377
}
7478

7579
func DebugfWithContextDepth(ctx context.Context, depth int, format string, args ...interface{}) {
76-
if klog.V(levelDebug).Enabled() {
77-
klog.V(levelDebug).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
80+
if klog.V(LevelDebug).Enabled() {
81+
klog.V(LevelDebug).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
7882
}
7983
}
8084

8185
func Tracef(format string, args ...interface{}) {
82-
klog.V(levelTrace).InfofDepth(1, format, args...)
86+
klog.V(LevelTrace).InfofDepth(1, format, args...)
8387
}
8488

8589
func TracefWithContext(ctx context.Context, format string, args ...interface{}) {
86-
if klog.V(levelDebug).Enabled() {
87-
klog.V(levelDebug).InfofDepth(1, tryPrependReqID(ctx, format), args...)
90+
if klog.V(LevelDebug).Enabled() {
91+
klog.V(LevelDebug).InfofDepth(1, tryPrependReqID(ctx, format), args...)
8892
}
8993
}
9094

9195
func TracefDepth(depth int, format string, args ...interface{}) {
92-
klog.V(levelTrace).InfofDepth(depth, format, args...)
96+
klog.V(LevelTrace).InfofDepth(depth, format, args...)
9397
}
9498

9599
func TracefWithContextDepth(ctx context.Context, depth int, format string, args ...interface{}) {
96-
if klog.V(levelDebug).Enabled() {
97-
klog.V(levelDebug).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
100+
if klog.V(LevelDebug).Enabled() {
101+
klog.V(LevelDebug).InfofDepth(depth, tryPrependReqID(ctx, format), args...)
98102
}
99103
}
100104

0 commit comments

Comments
 (0)