...

Source file src/k8s.io/kubernetes/test/e2e/storage/testsuites/fsgroupchangepolicy.go

Documentation: k8s.io/kubernetes/test/e2e/storage/testsuites

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     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 testsuites
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strconv"
    23  
    24  	"github.com/onsi/ginkgo/v2"
    25  	v1 "k8s.io/api/core/v1"
    26  	errors "k8s.io/apimachinery/pkg/util/errors"
    27  	"k8s.io/kubernetes/test/e2e/framework"
    28  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    29  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    30  	e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
    31  	storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
    32  	storageutils "k8s.io/kubernetes/test/e2e/storage/utils"
    33  	admissionapi "k8s.io/pod-security-admission/api"
    34  	utilpointer "k8s.io/utils/pointer"
    35  )
    36  
    37  const (
    38  	rootDir         = "/mnt/volume1"
    39  	rootDirFile     = "file1"
    40  	rootDirFilePath = rootDir + "/" + rootDirFile
    41  	subdir          = "/mnt/volume1/subdir"
    42  	subDirFile      = "file2"
    43  	subDirFilePath  = subdir + "/" + subDirFile
    44  )
    45  
    46  type fsGroupChangePolicyTestSuite struct {
    47  	tsInfo storageframework.TestSuiteInfo
    48  }
    49  
    50  var _ storageframework.TestSuite = &fsGroupChangePolicyTestSuite{}
    51  
    52  // InitCustomFsGroupChangePolicyTestSuite returns fsGroupChangePolicyTestSuite that implements TestSuite interface
    53  func InitCustomFsGroupChangePolicyTestSuite(patterns []storageframework.TestPattern) storageframework.TestSuite {
    54  	return &fsGroupChangePolicyTestSuite{
    55  		tsInfo: storageframework.TestSuiteInfo{
    56  			Name:         "fsgroupchangepolicy",
    57  			TestPatterns: patterns,
    58  			SupportedSizeRange: e2evolume.SizeRange{
    59  				Min: "1Mi",
    60  			},
    61  		},
    62  	}
    63  }
    64  
    65  // InitFsGroupChangePolicyTestSuite returns fsGroupChangePolicyTestSuite that implements TestSuite interface
    66  func InitFsGroupChangePolicyTestSuite() storageframework.TestSuite {
    67  	patterns := []storageframework.TestPattern{
    68  		storageframework.DefaultFsDynamicPV,
    69  	}
    70  	return InitCustomFsGroupChangePolicyTestSuite(patterns)
    71  }
    72  
    73  func (s *fsGroupChangePolicyTestSuite) GetTestSuiteInfo() storageframework.TestSuiteInfo {
    74  	return s.tsInfo
    75  }
    76  
    77  func (s *fsGroupChangePolicyTestSuite) SkipUnsupportedTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
    78  	skipVolTypePatterns(pattern, driver, storageframework.NewVolTypeMap(storageframework.CSIInlineVolume, storageframework.GenericEphemeralVolume))
    79  	dInfo := driver.GetDriverInfo()
    80  	if !dInfo.Capabilities[storageframework.CapFsGroup] {
    81  		e2eskipper.Skipf("Driver %q does not support FsGroup - skipping", dInfo.Name)
    82  	}
    83  
    84  	if pattern.VolMode == v1.PersistentVolumeBlock {
    85  		e2eskipper.Skipf("Test does not support non-filesystem volume mode - skipping")
    86  	}
    87  
    88  	if pattern.VolType != storageframework.DynamicPV {
    89  		e2eskipper.Skipf("Suite %q does not support %v", s.tsInfo.Name, pattern.VolType)
    90  	}
    91  
    92  	_, ok := driver.(storageframework.DynamicPVTestDriver)
    93  	if !ok {
    94  		e2eskipper.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, pattern.VolType)
    95  	}
    96  }
    97  
    98  func (s *fsGroupChangePolicyTestSuite) DefineTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
    99  	type local struct {
   100  		config   *storageframework.PerTestConfig
   101  		driver   storageframework.TestDriver
   102  		resource *storageframework.VolumeResource
   103  	}
   104  	var l local
   105  
   106  	// Beware that it also registers an AfterEach which renders f unusable. Any code using
   107  	// f must run inside an It or Context callback.
   108  	f := framework.NewFrameworkWithCustomTimeouts("fsgroupchangepolicy", storageframework.GetDriverTimeouts(driver))
   109  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
   110  
   111  	init := func(ctx context.Context) {
   112  		e2eskipper.SkipIfNodeOSDistroIs("windows")
   113  		l = local{}
   114  		l.driver = driver
   115  		l.config = driver.PrepareTest(ctx, f)
   116  		testVolumeSizeRange := s.GetTestSuiteInfo().SupportedSizeRange
   117  		l.resource = storageframework.CreateVolumeResource(ctx, l.driver, l.config, pattern, testVolumeSizeRange)
   118  	}
   119  
   120  	cleanup := func(ctx context.Context) {
   121  		var errs []error
   122  		if l.resource != nil {
   123  			if err := l.resource.CleanupResource(ctx); err != nil {
   124  				errs = append(errs, err)
   125  			}
   126  			l.resource = nil
   127  		}
   128  
   129  		framework.ExpectNoError(errors.NewAggregate(errs), "while cleanup resource")
   130  	}
   131  
   132  	tests := []struct {
   133  		name                              string // Test case name
   134  		podfsGroupChangePolicy            string // 'Always' or 'OnRootMismatch'
   135  		initialPodFsGroup                 int    // FsGroup of the initial pod
   136  		changedRootDirFileOwnership       int    // Change the ownership of the file in the root directory (/mnt/volume1/file1), as part of the initial pod
   137  		changedSubDirFileOwnership        int    // Change the ownership of the file in the sub directory (/mnt/volume1/subdir/file2), as part of the initial pod
   138  		secondPodFsGroup                  int    // FsGroup of the second pod
   139  		finalExpectedRootDirFileOwnership int    // Final expected ownership of the file in the root directory (/mnt/volume1/file1), as part of the second pod
   140  		finalExpectedSubDirFileOwnership  int    // Final expected ownership of the file in the sub directory (/mnt/volume1/subdir/file2), as part of the second pod
   141  		// Whether the test can run for drivers that support volumeMountGroup capability.
   142  		// For CSI drivers that support volumeMountGroup:
   143  		// * OnRootMismatch policy is not supported.
   144  		// * It may not be possible to chgrp after mounting a volume.
   145  		supportsVolumeMountGroup bool
   146  	}{
   147  		// Test cases for 'Always' policy
   148  		{
   149  			name:                              "pod created with an initial fsgroup, new pod fsgroup applied to volume contents",
   150  			podfsGroupChangePolicy:            "Always",
   151  			initialPodFsGroup:                 1000,
   152  			secondPodFsGroup:                  2000,
   153  			finalExpectedRootDirFileOwnership: 2000,
   154  			finalExpectedSubDirFileOwnership:  2000,
   155  			supportsVolumeMountGroup:          true,
   156  		},
   157  		{
   158  			name:                              "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with same fsgroup applied to the volume contents",
   159  			podfsGroupChangePolicy:            "Always",
   160  			initialPodFsGroup:                 1000,
   161  			changedRootDirFileOwnership:       2000,
   162  			changedSubDirFileOwnership:        3000,
   163  			secondPodFsGroup:                  1000,
   164  			finalExpectedRootDirFileOwnership: 1000,
   165  			finalExpectedSubDirFileOwnership:  1000,
   166  		},
   167  		{
   168  			name:                              "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with different fsgroup applied to the volume contents",
   169  			podfsGroupChangePolicy:            "Always",
   170  			initialPodFsGroup:                 1000,
   171  			changedRootDirFileOwnership:       2000,
   172  			changedSubDirFileOwnership:        3000,
   173  			secondPodFsGroup:                  4000,
   174  			finalExpectedRootDirFileOwnership: 4000,
   175  			finalExpectedSubDirFileOwnership:  4000,
   176  		},
   177  		// Test cases for 'OnRootMismatch' policy
   178  		{
   179  			name:                              "pod created with an initial fsgroup, new pod fsgroup applied to volume contents",
   180  			podfsGroupChangePolicy:            "OnRootMismatch",
   181  			initialPodFsGroup:                 1000,
   182  			secondPodFsGroup:                  2000,
   183  			finalExpectedRootDirFileOwnership: 2000,
   184  			finalExpectedSubDirFileOwnership:  2000,
   185  		},
   186  		{
   187  			name:                              "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with same fsgroup skips ownership changes to the volume contents",
   188  			podfsGroupChangePolicy:            "OnRootMismatch",
   189  			initialPodFsGroup:                 1000,
   190  			changedRootDirFileOwnership:       2000,
   191  			changedSubDirFileOwnership:        3000,
   192  			secondPodFsGroup:                  1000,
   193  			finalExpectedRootDirFileOwnership: 2000,
   194  			finalExpectedSubDirFileOwnership:  3000,
   195  		},
   196  		{
   197  			name:                              "pod created with an initial fsgroup, volume contents ownership changed via chgrp in first pod, new pod with different fsgroup applied to the volume contents",
   198  			podfsGroupChangePolicy:            "OnRootMismatch",
   199  			initialPodFsGroup:                 1000,
   200  			changedRootDirFileOwnership:       2000,
   201  			changedSubDirFileOwnership:        3000,
   202  			secondPodFsGroup:                  4000,
   203  			finalExpectedRootDirFileOwnership: 4000,
   204  			finalExpectedSubDirFileOwnership:  4000,
   205  		},
   206  	}
   207  
   208  	for _, t := range tests {
   209  		test := t
   210  		testCaseName := fmt.Sprintf("(%s)[LinuxOnly], %s", test.podfsGroupChangePolicy, test.name)
   211  		ginkgo.It(testCaseName, func(ctx context.Context) {
   212  			dInfo := driver.GetDriverInfo()
   213  			policy := v1.PodFSGroupChangePolicy(test.podfsGroupChangePolicy)
   214  
   215  			if dInfo.Capabilities[storageframework.CapVolumeMountGroup] &&
   216  				!test.supportsVolumeMountGroup {
   217  				e2eskipper.Skipf("Driver %q supports VolumeMountGroup, which is incompatible with this test - skipping", dInfo.Name)
   218  			}
   219  
   220  			init(ctx)
   221  			ginkgo.DeferCleanup(cleanup)
   222  			podConfig := e2epod.Config{
   223  				NS:                     f.Namespace.Name,
   224  				NodeSelection:          l.config.ClientNodeSelection,
   225  				PVCs:                   []*v1.PersistentVolumeClaim{l.resource.Pvc},
   226  				FsGroup:                utilpointer.Int64Ptr(int64(test.initialPodFsGroup)),
   227  				PodFSGroupChangePolicy: &policy,
   228  			}
   229  			// Create initial pod and create files in root and sub-directory and verify ownership.
   230  			pod := createPodAndVerifyContentGid(ctx, l.config.Framework, &podConfig, true /* createInitialFiles */, "" /* expectedRootDirFileOwnership */, "" /* expectedSubDirFileOwnership */)
   231  
   232  			// Change the ownership of files in the initial pod.
   233  			if test.changedRootDirFileOwnership != 0 {
   234  				ginkgo.By(fmt.Sprintf("Changing the root directory file ownership to %s", strconv.Itoa(test.changedRootDirFileOwnership)))
   235  				storageutils.ChangeFilePathGidInPod(f, rootDirFilePath, strconv.Itoa(test.changedRootDirFileOwnership), pod)
   236  			}
   237  
   238  			if test.changedSubDirFileOwnership != 0 {
   239  				ginkgo.By(fmt.Sprintf("Changing the sub-directory file ownership to %s", strconv.Itoa(test.changedSubDirFileOwnership)))
   240  				storageutils.ChangeFilePathGidInPod(f, subDirFilePath, strconv.Itoa(test.changedSubDirFileOwnership), pod)
   241  			}
   242  
   243  			ginkgo.By(fmt.Sprintf("Deleting Pod %s/%s", pod.Namespace, pod.Name))
   244  			framework.ExpectNoError(e2epod.DeletePodWithWait(ctx, f.ClientSet, pod))
   245  
   246  			// Create a second pod with existing volume and verify the contents ownership.
   247  			podConfig.FsGroup = utilpointer.Int64Ptr(int64(test.secondPodFsGroup))
   248  			pod = createPodAndVerifyContentGid(ctx, l.config.Framework, &podConfig, false /* createInitialFiles */, strconv.Itoa(test.finalExpectedRootDirFileOwnership), strconv.Itoa(test.finalExpectedSubDirFileOwnership))
   249  			ginkgo.By(fmt.Sprintf("Deleting Pod %s/%s", pod.Namespace, pod.Name))
   250  			framework.ExpectNoError(e2epod.DeletePodWithWait(ctx, f.ClientSet, pod))
   251  		})
   252  	}
   253  }
   254  
   255  func createPodAndVerifyContentGid(ctx context.Context, f *framework.Framework, podConfig *e2epod.Config, createInitialFiles bool, expectedRootDirFileOwnership, expectedSubDirFileOwnership string) *v1.Pod {
   256  	podFsGroup := strconv.FormatInt(*podConfig.FsGroup, 10)
   257  	ginkgo.By(fmt.Sprintf("Creating Pod in namespace %s with fsgroup %s", podConfig.NS, podFsGroup))
   258  	pod, err := e2epod.CreateSecPodWithNodeSelection(ctx, f.ClientSet, podConfig, f.Timeouts.PodStart)
   259  	framework.ExpectNoError(err)
   260  	framework.Logf("Pod %s/%s started successfully", pod.Namespace, pod.Name)
   261  
   262  	if createInitialFiles {
   263  		ginkgo.By(fmt.Sprintf("Creating a sub-directory and file, and verifying their ownership is %s", podFsGroup))
   264  		cmd := fmt.Sprintf("touch %s", rootDirFilePath)
   265  		var err error
   266  		_, _, err = e2evolume.PodExec(f, pod, cmd)
   267  		framework.ExpectNoError(err)
   268  		storageutils.VerifyFilePathGidInPod(f, rootDirFilePath, podFsGroup, pod)
   269  
   270  		cmd = fmt.Sprintf("mkdir %s", subdir)
   271  		_, _, err = e2evolume.PodExec(f, pod, cmd)
   272  		framework.ExpectNoError(err)
   273  		cmd = fmt.Sprintf("touch %s", subDirFilePath)
   274  		_, _, err = e2evolume.PodExec(f, pod, cmd)
   275  		framework.ExpectNoError(err)
   276  		storageutils.VerifyFilePathGidInPod(f, subDirFilePath, podFsGroup, pod)
   277  		return pod
   278  	}
   279  
   280  	// Verify existing contents of the volume
   281  	ginkgo.By(fmt.Sprintf("Verifying the ownership of root directory file is %s", expectedRootDirFileOwnership))
   282  	storageutils.VerifyFilePathGidInPod(f, rootDirFilePath, expectedRootDirFileOwnership, pod)
   283  	ginkgo.By(fmt.Sprintf("Verifying the ownership of sub directory file is %s", expectedSubDirFileOwnership))
   284  	storageutils.VerifyFilePathGidInPod(f, subDirFilePath, expectedSubDirFileOwnership, pod)
   285  	return pod
   286  }
   287  

View as plain text