mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Add unit tests for progress tracking and remove fullpath from reporting
This commit is contained in:
		@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
@@ -39,8 +40,14 @@ const (
 | 
				
			|||||||
	rwMask   = os.FileMode(0660)
 | 
						rwMask   = os.FileMode(0660)
 | 
				
			||||||
	roMask   = os.FileMode(0440)
 | 
						roMask   = os.FileMode(0440)
 | 
				
			||||||
	execMask = os.FileMode(0110)
 | 
						execMask = os.FileMode(0110)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	progressReportDuration = 60 * time.Second
 | 
					var (
 | 
				
			||||||
 | 
						// function that will be used for changing file permissions on linux
 | 
				
			||||||
 | 
						// mainly stored here as a variable so as it can replaced in tests
 | 
				
			||||||
 | 
						filePermissionChangeFunc = changeFilePermission
 | 
				
			||||||
 | 
						progressReportDuration   = 60 * time.Second
 | 
				
			||||||
 | 
						firstEventReportDuration = 30 * time.Second
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewVolumeOwnership returns an interface that can be used to recursively change volume permissions and ownership
 | 
					// NewVolumeOwnership returns an interface that can be used to recursively change volume permissions and ownership
 | 
				
			||||||
@@ -75,7 +82,7 @@ func (vo *VolumeOwnership) ChangePermissions() error {
 | 
				
			|||||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	timer := time.AfterFunc(30*time.Second, func() {
 | 
						timer := time.AfterFunc(firstEventReportDuration, func() {
 | 
				
			||||||
		vo.initiateProgressMonitor(ctx)
 | 
							vo.initiateProgressMonitor(ctx)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	defer timer.Stop()
 | 
						defer timer.Stop()
 | 
				
			||||||
@@ -96,7 +103,7 @@ func (vo *VolumeOwnership) changePermissionsRecursively() error {
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		vo.fileCounter.Add(1)
 | 
							vo.fileCounter.Add(1)
 | 
				
			||||||
		return changeFilePermission(path, vo.fsGroup, vo.mounter.GetAttributes().ReadOnly, info)
 | 
							return filePermissionChangeFunc(path, vo.fsGroup, vo.mounter.GetAttributes().ReadOnly, info)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vo.completionCallback != nil {
 | 
						if vo.completionCallback != nil {
 | 
				
			||||||
@@ -108,7 +115,8 @@ func (vo *VolumeOwnership) changePermissionsRecursively() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
 | 
					func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
 | 
				
			||||||
	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)
 | 
						dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
	vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
 | 
						vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
 | 
				
			||||||
	ticker := time.NewTicker(progressReportDuration)
 | 
						ticker := time.NewTicker(progressReportDuration)
 | 
				
			||||||
	defer ticker.Stop()
 | 
						defer ticker.Stop()
 | 
				
			||||||
@@ -122,8 +130,18 @@ func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// report everything after podUID in dir string, including podUID
 | 
				
			||||||
 | 
					func getDirnameToReport(dir, podUID string) string {
 | 
				
			||||||
 | 
						podUIDIndex := strings.Index(dir, podUID)
 | 
				
			||||||
 | 
						if podUIDIndex == -1 {
 | 
				
			||||||
 | 
							return dir
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dir[podUIDIndex:]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (vo *VolumeOwnership) logWarning() {
 | 
					func (vo *VolumeOwnership) logWarning() {
 | 
				
			||||||
	msg := fmt.Sprintf("Setting volume ownership for %s, processed %d files.", vo.dir, vo.fileCounter.Load())
 | 
						dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
 | 
				
			||||||
 | 
						msg := fmt.Sprintf("Setting volume ownership for %s, processed %d files.", dirName, vo.fileCounter.Load())
 | 
				
			||||||
	klog.Warning(msg)
 | 
						klog.Warning(msg)
 | 
				
			||||||
	vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
 | 
						vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,11 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/client-go/tools/record"
 | 
				
			||||||
	utiltesting "k8s.io/client-go/util/testing"
 | 
						utiltesting "k8s.io/client-go/util/testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -286,6 +289,7 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			defer os.RemoveAll(tmpDir)
 | 
								defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			info, err := os.Lstat(tmpDir)
 | 
								info, err := os.Lstat(tmpDir)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatalf("error reading permission of tmpdir: %v", err)
 | 
									t.Fatalf("error reading permission of tmpdir: %v", err)
 | 
				
			||||||
@@ -296,7 +300,7 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
 | 
				
			|||||||
				t.Fatalf("error reading permission stats for tmpdir: %s", tmpDir)
 | 
									t.Fatalf("error reading permission stats for tmpdir: %s", tmpDir)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var expectedGid int64 = int64(stat.Gid)
 | 
								var expectedGid = int64(stat.Gid)
 | 
				
			||||||
			err = test.setupFunc(tmpDir)
 | 
								err = test.setupFunc(tmpDir)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Errorf("for %s error running setup with: %v", test.description, err)
 | 
									t.Errorf("for %s error running setup with: %v", test.description, err)
 | 
				
			||||||
@@ -316,6 +320,130 @@ func TestSetVolumeOwnershipMode(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestProgressTracking(t *testing.T) {
 | 
				
			||||||
 | 
						alwaysApplyPolicy := v1.FSGroupChangeAlways
 | 
				
			||||||
 | 
						var expectedGID int64 = 9999
 | 
				
			||||||
 | 
						var permissionSleepDuration = 5 * time.Millisecond
 | 
				
			||||||
 | 
						// how often to report the events
 | 
				
			||||||
 | 
						progressReportDuration = 200 * time.Millisecond
 | 
				
			||||||
 | 
						firstEventReportDuration = 50 * time.Millisecond
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						filePermissionChangeFunc = func(filename string, fsGroup *int64, _ bool, _ os.FileInfo) error {
 | 
				
			||||||
 | 
							t.Logf("Calling file permission change for %s with gid %d", filename, *fsGroup)
 | 
				
			||||||
 | 
							time.Sleep(permissionSleepDuration)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pod := &v1.Pod{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "pod1",
 | 
				
			||||||
 | 
								UID:  "pod1uid",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: v1.PodSpec{
 | 
				
			||||||
 | 
								Volumes: []v1.Volume{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Name: "volume-name",
 | 
				
			||||||
 | 
										VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
											GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
 | 
				
			||||||
 | 
												PDName: "fake-device1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name                             string
 | 
				
			||||||
 | 
							filePermissionChangeTimeDuration time.Duration
 | 
				
			||||||
 | 
							totalWaitTime                    time.Duration
 | 
				
			||||||
 | 
							currentPod                       *v1.Pod
 | 
				
			||||||
 | 
							expectedEvents                   []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                             "When permission change finishes quickly, no events should be logged",
 | 
				
			||||||
 | 
								filePermissionChangeTimeDuration: 30 * time.Millisecond,
 | 
				
			||||||
 | 
								totalWaitTime:                    1 * time.Second,
 | 
				
			||||||
 | 
								currentPod:                       pod,
 | 
				
			||||||
 | 
								expectedEvents:                   []string{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                             "When no pod is specified, no events should be logged",
 | 
				
			||||||
 | 
								filePermissionChangeTimeDuration: 300 * time.Millisecond,
 | 
				
			||||||
 | 
								totalWaitTime:                    1 * time.Second,
 | 
				
			||||||
 | 
								currentPod:                       nil,
 | 
				
			||||||
 | 
								expectedEvents:                   []string{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:                             "When permission change takes loo long and pod is specified",
 | 
				
			||||||
 | 
								filePermissionChangeTimeDuration: 300 * time.Millisecond,
 | 
				
			||||||
 | 
								totalWaitTime:                    1 * time.Second,
 | 
				
			||||||
 | 
								currentPod:                       pod,
 | 
				
			||||||
 | 
								expectedEvents: []string{
 | 
				
			||||||
 | 
									"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",
 | 
				
			||||||
 | 
									"Warning VolumePermissionChangeInProgress Setting volume ownership for pod1uid/volumes/faketype, processed 1 files.",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := range tests {
 | 
				
			||||||
 | 
							tc := tests[i]
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								tmpDir, err := utiltesting.MkTmpdir("volume_linux_ownership")
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatalf("error creating temp dir: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								podUID := "placeholder"
 | 
				
			||||||
 | 
								if tc.currentPod != nil {
 | 
				
			||||||
 | 
									podUID = string(tc.currentPod.UID)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								volumePath := filepath.Join(tmpDir, podUID, "volumes", "faketype")
 | 
				
			||||||
 | 
								err = os.MkdirAll(volumePath, 0770)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatalf("error creating volumePath %s: %v", volumePath, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer func() {
 | 
				
			||||||
 | 
									err := os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatalf("error removing tmpDir %s: %v", tmpDir, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								mounter := &localFakeMounter{path: "FAKE_DIR_DOESNT_EXIST"} // SetVolumeOwnership() must rely on tmpDir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fakeRecorder := record.NewFakeRecorder(100)
 | 
				
			||||||
 | 
								recordedEvents := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Set how long file permission change takes
 | 
				
			||||||
 | 
								permissionSleepDuration = tc.filePermissionChangeTimeDuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ownershipChanger := NewVolumeOwnership(mounter, volumePath, &expectedGID, &alwaysApplyPolicy, nil)
 | 
				
			||||||
 | 
								if tc.currentPod != nil {
 | 
				
			||||||
 | 
									ownershipChanger.AddProgressNotifier(tc.currentPod, fakeRecorder)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								err = ownershipChanger.ChangePermissions()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error: %+v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(tc.totalWaitTime)
 | 
				
			||||||
 | 
								actualEventCount := len(fakeRecorder.Events)
 | 
				
			||||||
 | 
								if len(tc.expectedEvents) == 0 && actualEventCount != len(tc.expectedEvents) {
 | 
				
			||||||
 | 
									t.Errorf("expected 0 events got %d", actualEventCount)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for range actualEventCount {
 | 
				
			||||||
 | 
									event := <-fakeRecorder.Events
 | 
				
			||||||
 | 
									recordedEvents = append(recordedEvents, event)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for i, event := range tc.expectedEvents {
 | 
				
			||||||
 | 
									if event != recordedEvents[i] {
 | 
				
			||||||
 | 
										t.Errorf("expected event %d to be %s, got: %s", i, event, recordedEvents[i])
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// verifyDirectoryPermission checks if given path has directory permissions
 | 
					// verifyDirectoryPermission checks if given path has directory permissions
 | 
				
			||||||
// that is expected by k8s. If returns true if it does otherwise false
 | 
					// that is expected by k8s. If returns true if it does otherwise false
 | 
				
			||||||
func verifyDirectoryPermission(path string, readonly bool) bool {
 | 
					func verifyDirectoryPermission(path string, readonly bool) bool {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user