Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPBUGS-48403: Bring groupsnapshot beta feature to 4.18 #2169

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions openshift-hack/e2e/annotate/generated/zz_generated.annotations.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions openshift-hack/e2e/annotate/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var (
`\[Feature:UserNamespacesSupport\]`, // disabled Beta
`\[Feature:DynamicResourceAllocation\]`,
`\[Feature:VolumeAttributesClass\]`, // disabled Beta
`\[Feature:volumegroupsnapshot\]`, // disabled Beta
},
// tests for features that are not implemented in openshift
"[Disabled:Unimplemented]": {
Expand Down
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
133 changes: 133 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,133 @@
/*
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"
"github.com/onsi/gomega"
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, *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")
status := volumeGroupSnapshot.Object["status"]
err = framework.Gomega().Expect(status).NotTo(gomega.BeNil())
framework.ExpectNoError(err, "Failed to get status of volume group snapshot")
vgscName := status.(map[string]interface{})["boundVolumeGroupSnapshotContentName"].(string)
err = framework.Gomega().Expect(vgscName).NotTo(gomega.BeNil())
framework.ExpectNoError(err, "Failed to get content name of volume group snapshot")
vgsc, err := dc.Resource(utils.VolumeGroupSnapshotContentGVR).Get(ctx, vgscName, metav1.GetOptions{})
framework.ExpectNoError(err, "failed to get content of group snapshot")
return gsclass, volumeGroupSnapshot, vgsc
}

// 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, vgsc := CreateVolumeGroupSnapshot(ctx, sDriver, config, pattern, pvcName, pvcNamespace, timeouts, parameters)
vgs := &VolumeGroupSnapshotResource{
Config: config,
Pattern: pattern,
VGS: snapshot,
VGSClass: vgsClass,
VGSContent: vgsc,
}
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