Skip to content

Commit 9d63e57

Browse files
authored
Merge pull request kubernetes#124151 from ConnorJC3/add-vac-e2e
Add Happy Path VolumeAttributesClass CSI E2E Tests
2 parents c49b140 + ea58abf commit 9d63e57

File tree

13 files changed

+483
-21
lines changed

13 files changed

+483
-21
lines changed

test/e2e/feature/feature.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,13 @@ var (
363363
// TODO: document the feature (owning SIG, when to use this feature for a test)
364364
ValidatingAdmissionPolicy = framework.WithFeature(framework.ValidFeatures.Add("ValidatingAdmissionPolicy"))
365365

366+
// Owner: sig-storage
367+
// Tests related to VolumeAttributesClass (https://kep.k8s.io/3751)
368+
//
369+
// TODO: This label only requires the API storage.k8s.io/v1alpha1 and the VolumeAttributesClass feature-gate enabled.
370+
// It should be removed after k/k #124350 is merged.
371+
VolumeAttributesClass = framework.WithFeature(framework.ValidFeatures.Add("VolumeAttributesClass"))
372+
366373
// TODO: document the feature (owning SIG, when to use this feature for a test)
367374
Volumes = framework.WithFeature(framework.ValidFeatures.Add("Volumes"))
368375

test/e2e/framework/pv/pv.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ type PersistentVolumeClaimConfig struct {
127127
// unspecified
128128
ClaimSize string
129129
// AccessModes defaults to RWO if unspecified
130-
AccessModes []v1.PersistentVolumeAccessMode
131-
Annotations map[string]string
132-
Selector *metav1.LabelSelector
133-
StorageClassName *string
130+
AccessModes []v1.PersistentVolumeAccessMode
131+
Annotations map[string]string
132+
Selector *metav1.LabelSelector
133+
StorageClassName *string
134+
VolumeAttributesClassName *string
134135
// VolumeMode defaults to nil if unspecified or specified as the empty
135136
// string
136137
VolumeMode *v1.PersistentVolumeMode
@@ -661,8 +662,9 @@ func MakePersistentVolumeClaim(cfg PersistentVolumeClaimConfig, ns string) *v1.P
661662
v1.ResourceStorage: resource.MustParse(cfg.ClaimSize),
662663
},
663664
},
664-
StorageClassName: cfg.StorageClassName,
665-
VolumeMode: cfg.VolumeMode,
665+
StorageClassName: cfg.StorageClassName,
666+
VolumeAttributesClassName: cfg.VolumeAttributesClassName,
667+
VolumeMode: cfg.VolumeMode,
666668
},
667669
}
668670
}

test/e2e/storage/drivers/csi.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import (
5454
v1 "k8s.io/api/core/v1"
5555
rbacv1 "k8s.io/api/rbac/v1"
5656
storagev1 "k8s.io/api/storage/v1"
57+
storagev1alpha1 "k8s.io/api/storage/v1alpha1"
5758
apierrors "k8s.io/apimachinery/pkg/api/errors"
5859
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5960
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -85,6 +86,11 @@ const (
8586

8687
// Prefix of the mock driver grpc log
8788
grpcCallPrefix = "gRPCCall:"
89+
90+
// Parameter to use in hostpath CSI driver VolumeAttributesClass
91+
// Must be passed to the driver via --accepted-mutable-parameter-names
92+
hostpathCSIDriverMutableParameterName = "e2eVacTest"
93+
hostpathCSIDriverMutableParameterValue = "test-value"
8894
)
8995

9096
// hostpathCSI
@@ -209,6 +215,15 @@ func (h *hostpathCSIDriver) GetSnapshotClass(ctx context.Context, config *storag
209215
return utils.GenerateSnapshotClassSpec(snapshotter, parameters, ns)
210216
}
211217

218+
func (h *hostpathCSIDriver) GetVolumeAttributesClass(_ context.Context, config *storageframework.PerTestConfig) *storagev1alpha1.VolumeAttributesClass {
219+
return storageframework.CopyVolumeAttributesClass(&storagev1alpha1.VolumeAttributesClass{
220+
DriverName: config.GetUniqueDriverName(),
221+
Parameters: map[string]string{
222+
hostpathCSIDriverMutableParameterName: hostpathCSIDriverMutableParameterValue,
223+
},
224+
}, config.Framework.Namespace.Name, "e2e-vac-hostpath")
225+
}
226+
212227
func (h *hostpathCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
213228
// Create secondary namespace which will be used for creating driver
214229
driverNamespace := utils.CreateDriverNamespace(ctx, f)
@@ -230,7 +245,9 @@ func (h *hostpathCSIDriver) PrepareTest(ctx context.Context, f *framework.Framew
230245
DriverNamespace: driverNamespace,
231246
}
232247

233-
o := utils.PatchCSIOptions{
248+
patches := []utils.PatchCSIOptions{}
249+
250+
patches = append(patches, utils.PatchCSIOptions{
234251
OldDriverName: h.driverInfo.Name,
235252
NewDriverName: config.GetUniqueDriverName(),
236253
DriverContainerName: "hostpath",
@@ -246,11 +263,31 @@ func (h *hostpathCSIDriver) PrepareTest(ctx context.Context, f *framework.Framew
246263
ProvisionerContainerName: "csi-provisioner",
247264
SnapshotterContainerName: "csi-snapshotter",
248265
NodeName: node.Name,
249-
}
266+
})
267+
268+
// VAC E2E HostPath patch
269+
// Enables ModifyVolume support in the hostpath CSI driver, and adds an enabled parameter name
270+
patches = append(patches, utils.PatchCSIOptions{
271+
DriverContainerName: "hostpath",
272+
DriverContainerArguments: []string{"--enable-controller-modify-volume=true", "--accepted-mutable-parameter-names=e2eVacTest"},
273+
})
274+
275+
// VAC E2E FeatureGate patches
276+
// TODO: These can be removed after the VolumeAttributesClass feature is default enabled
277+
patches = append(patches, utils.PatchCSIOptions{
278+
DriverContainerName: "csi-provisioner",
279+
DriverContainerArguments: []string{"--feature-gates=VolumeAttributesClass=true"},
280+
})
281+
patches = append(patches, utils.PatchCSIOptions{
282+
DriverContainerName: "csi-resizer",
283+
DriverContainerArguments: []string{"--feature-gates=VolumeAttributesClass=true"},
284+
})
250285

251286
err = utils.CreateFromManifests(ctx, config.Framework, driverNamespace, func(item interface{}) error {
252-
if err := utils.PatchCSIDeployment(config.Framework, o, item); err != nil {
253-
return err
287+
for _, o := range patches {
288+
if err := utils.PatchCSIDeployment(config.Framework, o, item); err != nil {
289+
return err
290+
}
254291
}
255292

256293
// Remove csi-external-health-monitor-agent and

test/e2e/storage/external/external.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"time"
2626

2727
storagev1 "k8s.io/api/storage/v1"
28+
storagev1alpha1 "k8s.io/api/storage/v1alpha1"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3031
"k8s.io/apimachinery/pkg/runtime"
@@ -79,6 +80,30 @@ type driverDefinition struct {
7980
FromExistingClassName string
8081
}
8182

83+
// VolumeAttributesClass must be set to enable volume modification tests.
84+
// The default is to not run those tests.
85+
VolumeAttributesClass struct {
86+
// FromName set to true enables the usage of a
87+
// VolumeAttributesClass with DriverInfo.Name as
88+
// provisioner and no parameters.
89+
FromName bool
90+
91+
// FromFile is used only when FromName is false. It
92+
// loads a storage class from the given .yaml or .json
93+
// file. File names are resolved by the
94+
// framework.testfiles package, which typically means
95+
// that they can be absolute or relative to the test
96+
// suite's --repo-root parameter.
97+
//
98+
// This can be used when the VolumeAttributesClass
99+
// is meant to have additional parameters.
100+
FromFile string
101+
102+
// FromExistingClassName specifies the name of a pre-installed
103+
// VolumeAttributesClass that will be copied and used for the tests.
104+
FromExistingClassName string
105+
}
106+
82107
// SnapshotClass must be set to enable snapshotting tests.
83108
// The default is to not run those tests.
84109
SnapshotClass struct {
@@ -405,6 +430,43 @@ func (d *driverDefinition) GetSnapshotClass(ctx context.Context, e2econfig *stor
405430
return utils.GenerateSnapshotClassSpec(snapshotter, parameters, ns)
406431
}
407432

433+
func (d *driverDefinition) GetVolumeAttributesClass(ctx context.Context, e2econfig *storageframework.PerTestConfig) *storagev1alpha1.VolumeAttributesClass {
434+
if !d.VolumeAttributesClass.FromName && d.VolumeAttributesClass.FromFile == "" && d.VolumeAttributesClass.FromExistingClassName == "" {
435+
e2eskipper.Skipf("Driver %q has no configured VolumeAttributesClass - skipping", d.DriverInfo.Name)
436+
return nil
437+
}
438+
439+
var (
440+
vac *storagev1alpha1.VolumeAttributesClass
441+
err error
442+
)
443+
444+
f := e2econfig.Framework
445+
switch {
446+
case d.VolumeAttributesClass.FromName:
447+
vac = &storagev1alpha1.VolumeAttributesClass{DriverName: d.DriverInfo.Name}
448+
case d.VolumeAttributesClass.FromExistingClassName != "":
449+
vac, err = f.ClientSet.StorageV1alpha1().VolumeAttributesClasses().Get(ctx, d.VolumeAttributesClass.FromExistingClassName, metav1.GetOptions{})
450+
framework.ExpectNoError(err, "getting VolumeAttributesClass %s", d.VolumeAttributesClass.FromExistingClassName)
451+
case d.VolumeAttributesClass.FromFile != "":
452+
var ok bool
453+
items, err := utils.LoadFromManifests(d.VolumeAttributesClass.FromFile)
454+
framework.ExpectNoError(err, "load VolumeAttributesClass from %s", d.VolumeAttributesClass.FromFile)
455+
gomega.Expect(items).To(gomega.HaveLen(1), "exactly one item from %s", d.VolumeAttributesClass.FromFile)
456+
err = utils.PatchItems(f, f.Namespace, items...)
457+
framework.ExpectNoError(err, "patch VolumeAttributesClass from %s", d.VolumeAttributesClass.FromFile)
458+
459+
vac, ok = items[0].(*storagev1alpha1.VolumeAttributesClass)
460+
if !ok {
461+
framework.Failf("cast VolumeAttributesClass from %s", d.VolumeAttributesClass.FromFile)
462+
}
463+
}
464+
465+
gomega.Expect(vac).ToNot(gomega.BeNil(), "VolumeAttributesClass is unexpectantly nil")
466+
467+
return storageframework.CopyVolumeAttributesClass(vac, f.Namespace.Name, "e2e-vac")
468+
}
469+
408470
func (d *driverDefinition) GetVolume(e2econfig *storageframework.PerTestConfig, volumeNumber int) (map[string]string, bool, bool) {
409471
if len(d.InlineVolumes) == 0 {
410472
e2eskipper.Skipf("%s does not have any InlineVolumeAttributes defined", d.DriverInfo.Name)

test/e2e/storage/external/testdata/example.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
StorageClass:
22
FromExistingClassName: example
3+
VolumeAttributesClass:
4+
FromExistingClassName: example-vac
35
DriverInfo:
46
Name: example
57
RequiredAccessModes:

test/e2e/storage/framework/driver_operations.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222

2323
storagev1 "k8s.io/api/storage/v1"
24+
storagev1alpha1 "k8s.io/api/storage/v1alpha1"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"k8s.io/apiserver/pkg/storage/names"
2627
"k8s.io/kubernetes/pkg/volume/util"
@@ -92,3 +93,13 @@ func GetStorageClass(
9293
VolumeBindingMode: bindingMode,
9394
}
9495
}
96+
97+
// CopyVolumeAttributesClass constructs a new VolumeAttributesClass instance
98+
// with a unique name that is based on namespace + suffix
99+
// using the VolumeAttributesClass passed in as a parameter
100+
func CopyVolumeAttributesClass(vac *storagev1alpha1.VolumeAttributesClass, ns string, suffix string) *storagev1alpha1.VolumeAttributesClass {
101+
copy := vac.DeepCopy()
102+
copy.ObjectMeta.Name = names.SimpleNameGenerator.GenerateName(ns + "-" + suffix)
103+
copy.ResourceVersion = ""
104+
return copy
105+
}

test/e2e/storage/framework/testdriver.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
v1 "k8s.io/api/core/v1"
2424
storagev1 "k8s.io/api/storage/v1"
25+
storagev1alpha1 "k8s.io/api/storage/v1alpha1"
2526
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2627
"k8s.io/apimachinery/pkg/util/sets"
2728
"k8s.io/kubernetes/test/e2e/framework"
@@ -130,6 +131,15 @@ type SnapshottableTestDriver interface {
130131
GetSnapshotClass(ctx context.Context, config *PerTestConfig, parameters map[string]string) *unstructured.Unstructured
131132
}
132133

134+
// VolumeAttributesClassTestDriver represents an interface for a TestDriver that supports
135+
// creating and modifying volumes via VolumeAttributesClass objects
136+
type VolumeAttributesClassTestDriver interface {
137+
TestDriver
138+
// GetVolumeAttributesClass returns a VolumeAttributesClass to create/modify PVCs
139+
// It will return nil if the TestDriver does not support VACs
140+
GetVolumeAttributesClass(ctx context.Context, config *PerTestConfig) *storagev1alpha1.VolumeAttributesClass
141+
}
142+
133143
// CustomTimeoutsTestDriver represents an interface fo a TestDriver that supports custom timeouts.
134144
type CustomTimeoutsTestDriver interface {
135145
TestDriver

test/e2e/storage/framework/volume_resource.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,19 @@ type VolumeResource struct {
5353
// CreateVolumeResource constructs a VolumeResource for the current test. It knows how to deal with
5454
// different test pattern volume types.
5555
func CreateVolumeResource(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange) *VolumeResource {
56-
return CreateVolumeResourceWithAccessModes(ctx, driver, config, pattern, testVolumeSizeRange, driver.GetDriverInfo().RequiredAccessModes)
56+
return CreateVolumeResourceWithAccessModes(ctx, driver, config, pattern, testVolumeSizeRange, driver.GetDriverInfo().RequiredAccessModes, nil)
57+
}
58+
59+
// CreateVolumeResource constructs a VolumeResource for the current test using the specified VAC name.
60+
func CreateVolumeResourceWithVAC(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange, vacName *string) *VolumeResource {
61+
if pattern.VolType != DynamicPV {
62+
framework.Failf("Creating volume with VAC only supported on dynamic PV tests")
63+
}
64+
return CreateVolumeResourceWithAccessModes(ctx, driver, config, pattern, testVolumeSizeRange, driver.GetDriverInfo().RequiredAccessModes, vacName)
5765
}
5866

5967
// CreateVolumeResourceWithAccessModes constructs a VolumeResource for the current test with the provided access modes.
60-
func CreateVolumeResourceWithAccessModes(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange, accessModes []v1.PersistentVolumeAccessMode) *VolumeResource {
68+
func CreateVolumeResourceWithAccessModes(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange, accessModes []v1.PersistentVolumeAccessMode, vacName *string) *VolumeResource {
6169
r := VolumeResource{
6270
Config: config,
6371
Pattern: pattern,
@@ -107,7 +115,7 @@ func CreateVolumeResourceWithAccessModes(ctx context.Context, driver TestDriver,
107115
switch pattern.VolType {
108116
case DynamicPV:
109117
r.Pv, r.Pvc = createPVCPVFromDynamicProvisionSC(
110-
ctx, f, dInfo.Name, claimSize, r.Sc, pattern.VolMode, accessModes)
118+
ctx, f, dInfo.Name, claimSize, r.Sc, pattern.VolMode, accessModes, vacName)
111119
r.VolSource = storageutils.CreateVolumeSource(r.Pvc.Name, false /* readOnly */)
112120
case GenericEphemeralVolume:
113121
driverVolumeSizeRange := dDriver.GetDriverInfo().SupportedSizeRange
@@ -287,17 +295,19 @@ func createPVCPVFromDynamicProvisionSC(
287295
sc *storagev1.StorageClass,
288296
volMode v1.PersistentVolumeMode,
289297
accessModes []v1.PersistentVolumeAccessMode,
298+
vacName *string,
290299
) (*v1.PersistentVolume, *v1.PersistentVolumeClaim) {
291300
cs := f.ClientSet
292301
ns := f.Namespace.Name
293302

294303
ginkgo.By("creating a claim")
295304
pvcCfg := e2epv.PersistentVolumeClaimConfig{
296-
NamePrefix: name,
297-
ClaimSize: claimSize,
298-
StorageClassName: &(sc.Name),
299-
AccessModes: accessModes,
300-
VolumeMode: &volMode,
305+
NamePrefix: name,
306+
ClaimSize: claimSize,
307+
StorageClassName: &(sc.Name),
308+
VolumeAttributesClassName: vacName,
309+
AccessModes: accessModes,
310+
VolumeMode: &volMode,
301311
}
302312

303313
pvc := e2epv.MakePersistentVolumeClaim(pvcCfg, ns)

test/e2e/storage/testsuites/base.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ var CSISuites = append(BaseSuites,
8282
InitSnapshottableStressTestSuite,
8383
InitVolumePerformanceTestSuite,
8484
InitReadWriteOncePodTestSuite,
85+
InitVolumeModifyTestSuite,
8586
)
8687

8788
func getVolumeOpsFromMetricsForPlugin(ms testutil.Metrics, pluginName string) opCounts {

test/e2e/storage/testsuites/disruptive.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ func (s *disruptiveTestSuite) DefineTests(driver storageframework.TestDriver, pa
116116
l.config,
117117
pattern,
118118
testVolumeSizeRange,
119-
accessModes)
119+
accessModes,
120+
nil)
120121
}
121122
}
122123

0 commit comments

Comments
 (0)