Skip to content

Commit b0dc96e

Browse files
committed
Add unit tests for progress tracking and remove fullpath from reporting
1 parent f7c1799 commit b0dc96e

File tree

2 files changed

+152
-6
lines changed

2 files changed

+152
-6
lines changed

pkg/volume/volume_linux.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"context"
2424
"fmt"
2525
"path/filepath"
26+
"strings"
2627
"syscall"
2728

2829
"os"
@@ -39,8 +40,14 @@ const (
3940
rwMask = os.FileMode(0660)
4041
roMask = os.FileMode(0440)
4142
execMask = os.FileMode(0110)
43+
)
4244

43-
progressReportDuration = 60 * time.Second
45+
var (
46+
// function that will be used for changing file permissions on linux
47+
// mainly stored here as a variable so as it can replaced in tests
48+
filePermissionChangeFunc = changeFilePermission
49+
progressReportDuration = 60 * time.Second
50+
firstEventReportDuration = 30 * time.Second
4451
)
4552

4653
// NewVolumeOwnership returns an interface that can be used to recursively change volume permissions and ownership
@@ -75,7 +82,7 @@ func (vo *VolumeOwnership) ChangePermissions() error {
7582
ctx, cancel := context.WithCancel(context.Background())
7683
defer cancel()
7784

78-
timer := time.AfterFunc(30*time.Second, func() {
85+
timer := time.AfterFunc(firstEventReportDuration, func() {
7986
vo.initiateProgressMonitor(ctx)
8087
})
8188
defer timer.Stop()
@@ -96,7 +103,7 @@ func (vo *VolumeOwnership) changePermissionsRecursively() error {
96103
return err
97104
}
98105
vo.fileCounter.Add(1)
99-
return changeFilePermission(path, vo.fsGroup, vo.mounter.GetAttributes().ReadOnly, info)
106+
return filePermissionChangeFunc(path, vo.fsGroup, vo.mounter.GetAttributes().ReadOnly, info)
100107
})
101108

102109
if vo.completionCallback != nil {
@@ -108,7 +115,8 @@ func (vo *VolumeOwnership) changePermissionsRecursively() error {
108115
}
109116

110117
func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
111-
msg := fmt.Sprintf("Setting volume ownership for %s is taking longer than expected, consider using OnRootMismatch - https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods", vo.dir)
118+
dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
119+
msg := fmt.Sprintf("Setting volume ownership for %s is taking longer than expected, consider using OnRootMismatch - https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods", dirName)
112120
vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
113121
ticker := time.NewTicker(progressReportDuration)
114122
defer ticker.Stop()
@@ -122,8 +130,18 @@ func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
122130
}
123131
}
124132

133+
// report everything after podUID in dir string, including podUID
134+
func getDirnameToReport(dir, podUID string) string {
135+
podUIDIndex := strings.Index(dir, podUID)
136+
if podUIDIndex == -1 {
137+
return dir
138+
}
139+
return dir[podUIDIndex:]
140+
}
141+
125142
func (vo *VolumeOwnership) logWarning() {
126-
msg := fmt.Sprintf("Setting volume ownership for %s, processed %d files.", vo.dir, vo.fileCounter.Load())
143+
dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
144+
msg := fmt.Sprintf("Setting volume ownership for %s, processed %d files.", dirName, vo.fileCounter.Load())
127145
klog.Warning(msg)
128146
vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
129147
}

pkg/volume/volume_linux_test.go

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ import (
2525
"path/filepath"
2626
"syscall"
2727
"testing"
28+
"time"
2829

2930
v1 "k8s.io/api/core/v1"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/client-go/tools/record"
3033
utiltesting "k8s.io/client-go/util/testing"
3134
)
3235

@@ -286,6 +289,7 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
286289
}
287290

288291
defer os.RemoveAll(tmpDir)
292+
289293
info, err := os.Lstat(tmpDir)
290294
if err != nil {
291295
t.Fatalf("error reading permission of tmpdir: %v", err)
@@ -296,7 +300,7 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
296300
t.Fatalf("error reading permission stats for tmpdir: %s", tmpDir)
297301
}
298302

299-
var expectedGid int64 = int64(stat.Gid)
303+
var expectedGid = int64(stat.Gid)
300304
err = test.setupFunc(tmpDir)
301305
if err != nil {
302306
t.Errorf("for %s error running setup with: %v", test.description, err)
@@ -316,6 +320,130 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
316320
}
317321
}
318322

323+
func TestProgressTracking(t *testing.T) {
324+
alwaysApplyPolicy := v1.FSGroupChangeAlways
325+
var expectedGID int64 = 9999
326+
var permissionSleepDuration = 5 * time.Millisecond
327+
// how often to report the events
328+
progressReportDuration = 200 * time.Millisecond
329+
firstEventReportDuration = 50 * time.Millisecond
330+
331+
filePermissionChangeFunc = func(filename string, fsGroup *int64, _ bool, _ os.FileInfo) error {
332+
t.Logf("Calling file permission change for %s with gid %d", filename, *fsGroup)
333+
time.Sleep(permissionSleepDuration)
334+
return nil
335+
}
336+
pod := &v1.Pod{
337+
ObjectMeta: metav1.ObjectMeta{
338+
Name: "pod1",
339+
UID: "pod1uid",
340+
},
341+
Spec: v1.PodSpec{
342+
Volumes: []v1.Volume{
343+
{
344+
Name: "volume-name",
345+
VolumeSource: v1.VolumeSource{
346+
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
347+
PDName: "fake-device1",
348+
},
349+
},
350+
},
351+
},
352+
},
353+
}
354+
355+
tests := []struct {
356+
name string
357+
filePermissionChangeTimeDuration time.Duration
358+
totalWaitTime time.Duration
359+
currentPod *v1.Pod
360+
expectedEvents []string
361+
}{
362+
{
363+
name: "When permission change finishes quickly, no events should be logged",
364+
filePermissionChangeTimeDuration: 30 * time.Millisecond,
365+
totalWaitTime: 1 * time.Second,
366+
currentPod: pod,
367+
expectedEvents: []string{},
368+
},
369+
{
370+
name: "When no pod is specified, no events should be logged",
371+
filePermissionChangeTimeDuration: 300 * time.Millisecond,
372+
totalWaitTime: 1 * time.Second,
373+
currentPod: nil,
374+
expectedEvents: []string{},
375+
},
376+
{
377+
name: "When permission change takes loo long and pod is specified",
378+
filePermissionChangeTimeDuration: 300 * time.Millisecond,
379+
totalWaitTime: 1 * time.Second,
380+
currentPod: pod,
381+
expectedEvents: []string{
382+
"Warning VolumePermissionChangeInProgress Setting volume ownership for pod1uid/volumes/faketype is taking longer than expected, consider using OnRootMismatch - https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods",
383+
"Warning VolumePermissionChangeInProgress Setting volume ownership for pod1uid/volumes/faketype, processed 1 files.",
384+
},
385+
},
386+
}
387+
388+
for i := range tests {
389+
tc := tests[i]
390+
t.Run(tc.name, func(t *testing.T) {
391+
tmpDir, err := utiltesting.MkTmpdir("volume_linux_ownership")
392+
if err != nil {
393+
t.Fatalf("error creating temp dir: %v", err)
394+
}
395+
podUID := "placeholder"
396+
if tc.currentPod != nil {
397+
podUID = string(tc.currentPod.UID)
398+
}
399+
volumePath := filepath.Join(tmpDir, podUID, "volumes", "faketype")
400+
err = os.MkdirAll(volumePath, 0770)
401+
if err != nil {
402+
t.Fatalf("error creating volumePath %s: %v", volumePath, err)
403+
}
404+
defer func() {
405+
err := os.RemoveAll(tmpDir)
406+
if err != nil {
407+
t.Fatalf("error removing tmpDir %s: %v", tmpDir, err)
408+
}
409+
}()
410+
411+
mounter := &localFakeMounter{path: "FAKE_DIR_DOESNT_EXIST"} // SetVolumeOwnership() must rely on tmpDir
412+
413+
fakeRecorder := record.NewFakeRecorder(100)
414+
recordedEvents := []string{}
415+
416+
// Set how long file permission change takes
417+
permissionSleepDuration = tc.filePermissionChangeTimeDuration
418+
419+
ownershipChanger := NewVolumeOwnership(mounter, volumePath, &expectedGID, &alwaysApplyPolicy, nil)
420+
if tc.currentPod != nil {
421+
ownershipChanger.AddProgressNotifier(tc.currentPod, fakeRecorder)
422+
}
423+
err = ownershipChanger.ChangePermissions()
424+
if err != nil {
425+
t.Errorf("unexpected error: %+v", err)
426+
}
427+
time.Sleep(tc.totalWaitTime)
428+
actualEventCount := len(fakeRecorder.Events)
429+
if len(tc.expectedEvents) == 0 && actualEventCount != len(tc.expectedEvents) {
430+
t.Errorf("expected 0 events got %d", actualEventCount)
431+
}
432+
433+
for range actualEventCount {
434+
event := <-fakeRecorder.Events
435+
recordedEvents = append(recordedEvents, event)
436+
}
437+
438+
for i, event := range tc.expectedEvents {
439+
if event != recordedEvents[i] {
440+
t.Errorf("expected event %d to be %s, got: %s", i, event, recordedEvents[i])
441+
}
442+
}
443+
})
444+
}
445+
}
446+
319447
// verifyDirectoryPermission checks if given path has directory permissions
320448
// that is expected by k8s. If returns true if it does otherwise false
321449
func verifyDirectoryPermission(path string, readonly bool) bool {

0 commit comments

Comments
 (0)