Skip to content

Commit

Permalink
UPSTREAM: 126326: Add end-to-end tests for Volume Group Snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
jsafrane committed Jan 6, 2025
1 parent 8ac36bf commit 1a16529
Show file tree
Hide file tree
Showing 13 changed files with 1,914 additions and 7 deletions.
4 changes: 4 additions & 0 deletions test/e2e/feature/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ var (
// TODO: document the feature (owning SIG, when to use this feature for a test)
VolumeSnapshotDataSource = framework.WithFeature(framework.ValidFeatures.Add("VolumeSnapshotDataSource"))

// Owner: sig-storage
// Volume group snapshot tests
VolumeGroupSnapshotDataSource = framework.WithFeature(framework.ValidFeatures.Add("volumegroupsnapshot"))

// TODO: document the feature (owning SIG, when to use this feature for a test)
VolumeSourceXFS = framework.WithFeature(framework.ValidFeatures.Add("VolumeSourceXFS"))

Expand Down
7 changes: 7 additions & 0 deletions test/e2e/storage/drivers/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func InitHostPathCSIDriver() storageframework.TestDriver {
storageframework.CapReadWriteOncePod: true,
storageframework.CapMultiplePVsSameID: true,
storageframework.CapFSResizeFromSourceNotSupported: true,
storageframework.CapVolumeGroupSnapshot: true,

// This is needed for the
// testsuites/volumelimits.go `should support volume limits`
Expand Down Expand Up @@ -223,6 +224,12 @@ func (h *hostpathCSIDriver) GetVolumeAttributesClass(_ context.Context, config *
},
}, config.Framework.Namespace.Name, "e2e-vac-hostpath")
}
func (h *hostpathCSIDriver) GetVolumeGroupSnapshotClass(ctx context.Context, config *storageframework.PerTestConfig, parameters map[string]string) *unstructured.Unstructured {
snapshotter := config.GetUniqueDriverName()
ns := config.Framework.Namespace.Name

return utils.GenerateVolumeGroupSnapshotClassSpec(snapshotter, parameters, ns)
}

func (h *hostpathCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
// Create secondary namespace which will be used for creating driver
Expand Down
21 changes: 14 additions & 7 deletions test/e2e/storage/framework/testdriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ type SnapshottableTestDriver interface {
GetSnapshotClass(ctx context.Context, config *PerTestConfig, parameters map[string]string) *unstructured.Unstructured
}

type VoulmeGroupSnapshottableTestDriver interface {
TestDriver
// GetVolumeGroupSnapshotClass returns a VolumeGroupSnapshotClass to create group snapshot.
GetVolumeGroupSnapshotClass(ctx context.Context, config *PerTestConfig, parameters map[string]string) *unstructured.Unstructured
}

// VolumeAttributesClassTestDriver represents an interface for a TestDriver that supports
// creating and modifying volumes via VolumeAttributesClass objects
type VolumeAttributesClassTestDriver interface {
Expand Down Expand Up @@ -159,13 +165,14 @@ type Capability string

// Constants related to capabilities and behavior of the driver.
const (
CapPersistence Capability = "persistence" // data is persisted across pod restarts
CapBlock Capability = "block" // raw block mode
CapFsGroup Capability = "fsGroup" // volume ownership via fsGroup
CapVolumeMountGroup Capability = "volumeMountGroup" // Driver has the VolumeMountGroup CSI node capability. Because this is a FSGroup feature, the fsGroup capability must also be set to true.
CapExec Capability = "exec" // exec a file in the volume
CapSnapshotDataSource Capability = "snapshotDataSource" // support populate data from snapshot
CapPVCDataSource Capability = "pvcDataSource" // support populate data from pvc
CapPersistence Capability = "persistence" // data is persisted across pod restarts
CapBlock Capability = "block" // raw block mode
CapFsGroup Capability = "fsGroup" // volume ownership via fsGroup
CapVolumeMountGroup Capability = "volumeMountGroup" // Driver has the VolumeMountGroup CSI node capability. Because this is a FSGroup feature, the fsGroup capability must also be set to true.
CapExec Capability = "exec" // exec a file in the volume
CapSnapshotDataSource Capability = "snapshotDataSource" // support populate data from snapshot
CapVolumeGroupSnapshot Capability = "groupSnapshot" // support group snapshot
CapPVCDataSource Capability = "pvcDataSource" // support populate data from pvc

// multiple pods on a node can use the same volume concurrently;
// for CSI, see:
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/storage/framework/testpattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ var (
DynamicCreatedSnapshot TestSnapshotType = "DynamicSnapshot"
// PreprovisionedCreatedSnapshot represents a snapshot type for pre-provisioned snapshot
PreprovisionedCreatedSnapshot TestSnapshotType = "PreprovisionedSnapshot"

VolumeGroupSnapshot TestSnapshotType = "VolumeGroupSnapshot"
)

// TestSnapshotDeletionPolicy represents the deletion policy of the snapshot class
Expand Down Expand Up @@ -318,6 +320,14 @@ var (
SnapshotDeletionPolicy: DeleteSnapshot,
VolType: DynamicPV,
}

// VolumeGroupSnapshotDelete is TestPattern for "VolumeGroupSnapshot"
VolumeGroupSnapshotDelete = TestPattern{
Name: " (delete policy)",
SnapshotType: VolumeGroupSnapshot,
SnapshotDeletionPolicy: DeleteSnapshot,
VolType: DynamicPV,
}
// PreprovisionedSnapshotDelete is TestPattern for "Pre-provisioned snapshot"
PreprovisionedSnapshotDelete = TestPattern{
Name: "Pre-provisioned Snapshot (delete policy)",
Expand Down
126 changes: 126 additions & 0 deletions test/e2e/storage/framework/volume_group_snapshot_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package framework

import (
"context"
"fmt"

"github.com/onsi/ginkgo/v2"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/storage/utils"
)

func getVolumeGroupSnapshot(labels map[string]interface{}, ns, snapshotClassName string) *unstructured.Unstructured {
snapshot := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "VolumeGroupSnapshot",
"apiVersion": utils.VolumeGroupSnapshotAPIVersion,
"metadata": map[string]interface{}{
"generateName": "group-snapshot-",
"namespace": ns,
},
"spec": map[string]interface{}{
"volumeGroupSnapshotClassName": snapshotClassName,
"source": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": labels,
},
},
},
},
}

return snapshot
}

// VolumeGroupSnapshotResource represents a volumegroupsnapshot class, a volumegroupsnapshot and its bound contents for a specific test case
type VolumeGroupSnapshotResource struct {
Config *PerTestConfig
Pattern TestPattern

Vgs *unstructured.Unstructured
Vgscontent *unstructured.Unstructured
Vgsclass *unstructured.Unstructured
}

// CreateVolumeGroupSnapshot creates a VolumeGroupSnapshotClass with given SnapshotDeletionPolicy and a VolumeGroupSnapshot
// from the VolumeGroupSnapshotClass using a dynamic client.
// Returns the unstructured VolumeGroupSnapshotClass and VolumeGroupSnapshot objects.
func CreateVolumeGroupSnapshot(ctx context.Context, sDriver VoulmeGroupSnapshottableTestDriver, config *PerTestConfig, pattern TestPattern, groupName string, pvcNamespace string, timeouts *framework.TimeoutContext, parameters map[string]string) (*unstructured.Unstructured, *unstructured.Unstructured) {
defer ginkgo.GinkgoRecover()
var err error
if pattern.SnapshotType != VolumeGroupSnapshot {
err = fmt.Errorf("SnapshotType must be set to VolumeGroupSnapshot")
framework.ExpectNoError(err, "SnapshotType is set to VolumeGroupSnapshot")
}
dc := config.Framework.DynamicClient

ginkgo.By("creating a VolumeGroupSnapshotClass")
gsclass := sDriver.GetVolumeGroupSnapshotClass(ctx, config, parameters)
if gsclass == nil {
framework.Failf("Failed to get volume group snapshot class based on test config")
}
gsclass.Object["deletionPolicy"] = pattern.SnapshotDeletionPolicy.String()

gsclass, err = dc.Resource(utils.VolumeGroupSnapshotClassGVR).Create(ctx, gsclass, metav1.CreateOptions{})
framework.ExpectNoError(err, "Failed to create volume group snapshot class")
gsclass, err = dc.Resource(utils.VolumeGroupSnapshotClassGVR).Get(ctx, gsclass.GetName(), metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to get volume group snapshot class")

ginkgo.By("creating a dynamic VolumeGroupSnapshot")
// Prepare a dynamically provisioned group volume snapshot with certain data
volumeGroupSnapshot := getVolumeGroupSnapshot(map[string]interface{}{
"group": groupName,
}, pvcNamespace, gsclass.GetName())

volumeGroupSnapshot, err = dc.Resource(utils.VolumeGroupSnapshotGVR).Namespace(volumeGroupSnapshot.GetNamespace()).Create(ctx, volumeGroupSnapshot, metav1.CreateOptions{})
framework.ExpectNoError(err, "Failed to create volume group snapshot")
ginkgo.By("Waiting for group snapshot to be ready")
err = utils.WaitForVolumeGroupSnapshotReady(ctx, dc, volumeGroupSnapshot.GetNamespace(), volumeGroupSnapshot.GetName(), framework.Poll, timeouts.SnapshotCreate*10)
framework.ExpectNoError(err, "Group snapshot is not ready to use within the timeout")
ginkgo.By("Getting group snapshot and content")
volumeGroupSnapshot, err = dc.Resource(utils.VolumeGroupSnapshotGVR).Namespace(volumeGroupSnapshot.GetNamespace()).Get(ctx, volumeGroupSnapshot.GetName(), metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to get volume group snapshot after creation")

return gsclass, volumeGroupSnapshot
}

// CleanupResource deletes the VolumeGroupSnapshotClass and VolumeGroupSnapshot objects using a dynamic client.
func (r *VolumeGroupSnapshotResource) CleanupResource(ctx context.Context, timeouts *framework.TimeoutContext) error {
defer ginkgo.GinkgoRecover()
dc := r.Config.Framework.DynamicClient
err := dc.Resource(utils.VolumeGroupSnapshotClassGVR).Delete(ctx, r.Vgsclass.GetName(), metav1.DeleteOptions{})
framework.ExpectNoError(err, "Failed to delete volume group snapshot class")
return nil
}

// CreateVolumeGroupSnapshotResource creates a VolumeGroupSnapshotResource object with the given parameters.
func CreateVolumeGroupSnapshotResource(ctx context.Context, sDriver VoulmeGroupSnapshottableTestDriver, config *PerTestConfig, pattern TestPattern, pvcName string, pvcNamespace string, timeouts *framework.TimeoutContext, parameters map[string]string) *VolumeGroupSnapshotResource {
vgsclass, snapshot := CreateVolumeGroupSnapshot(ctx, sDriver, config, pattern, pvcName, pvcNamespace, timeouts, parameters)
vgs := &VolumeGroupSnapshotResource{
Config: config,
Pattern: pattern,
Vgs: snapshot,
Vgsclass: vgsclass,
Vgscontent: nil,
}
return vgs
}
2 changes: 2 additions & 0 deletions test/e2e/storage/testsuites/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var BaseSuites = []func() storageframework.TestSuite{
InitTopologyTestSuite,
InitVolumeStressTestSuite,
InitFsGroupChangePolicyTestSuite,
InitVolumeGroupSnapshottableTestSuite,
func() storageframework.TestSuite {
return InitCustomEphemeralTestSuite(GenericEphemeralTestPatterns())
},
Expand All @@ -79,6 +80,7 @@ var CSISuites = append(BaseSuites,
return InitCustomEphemeralTestSuite(CSIEphemeralTestPatterns())
},
InitSnapshottableTestSuite,
InitVolumeGroupSnapshottableTestSuite,
InitSnapshottableStressTestSuite,
InitVolumePerformanceTestSuite,
InitReadWriteOncePodTestSuite,
Expand Down
Loading

0 comments on commit 1a16529

Please sign in to comment.