Add pre provisioned snapshot tests

Make pre provisioned snapshots using CSI driver by
1. Take a dynamic snapshot with retain policy
2. Delete the dynamic snapshot and content
3. Create a preprovisioned snapshot with snapshotHandle

This commit adds a preprovisiond test pattern, all snapshots made using
create snapshot resource become prepv snapshots. All exisitng test cases
now run again with prepv snapshots.
This commit is contained in:
Andi Li 2020-07-14 22:29:07 +00:00
parent ed85129831
commit e78aee9965
4 changed files with 133 additions and 7 deletions

View File

@ -255,6 +255,10 @@ func (d *driverDefinition) SkipUnsupportedTest(pattern testpatterns.TestPattern)
if d.SnapshotClass.FromName || d.SnapshotClass.FromFile != "" || d.SnapshotClass.FromExistingClassName != "" {
supported = true
}
case testpatterns.PreprovisionedCreatedSnapshot:
if d.SnapshotClass.FromName || d.SnapshotClass.FromFile != "" || d.SnapshotClass.FromExistingClassName != "" {
supported = true
}
}
if !supported {
e2eskipper.Skipf("Driver %q does not support snapshot type %q - skipping", d.DriverInfo.Name, pattern.SnapshotType)

View File

@ -70,6 +70,10 @@ var (
RetainSnapshot TestSnapshotDeletionPolicy = "Retain"
)
func (t TestSnapshotDeletionPolicy) String() string {
return string(t)
}
// TestPattern represents a combination of parameters to be tested in a TestSuite
type TestPattern struct {
Name string // Name of TestPattern

View File

@ -18,6 +18,7 @@ package testsuites
import (
"context"
"crypto/sha1"
"flag"
"fmt"
"math"
@ -571,6 +572,64 @@ func getSnapshot(claimName string, ns, snapshotClassName string) *unstructured.U
return snapshot
}
func getPreProvisionedSnapshot(snapshotContentName, ns, snapshotHandle string) *unstructured.Unstructured {
snapshot := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "VolumeSnapshot",
"apiVersion": snapshotAPIVersion,
"metadata": map[string]interface{}{
"name": getPreProvisionedSnapshotName(snapshotHandle),
"namespace": ns,
},
"spec": map[string]interface{}{
"source": map[string]interface{}{
"volumeSnapshotContentName": snapshotContentName,
},
},
},
}
return snapshot
}
func getPreProvisionedSnapshotContent(snapshotName, snapshotNamespace, snapshotHandle, deletionPolicy, csiDriverName, volumeSnapshotClassName string) *unstructured.Unstructured {
snapshotContent := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "VolumeSnapshotContent",
"apiVersion": snapshotAPIVersion,
"metadata": map[string]interface{}{
"name": getPreProvisionedSnapshotContentName(snapshotHandle),
},
"spec": map[string]interface{}{
"source": map[string]interface{}{
"snapshotHandle": snapshotHandle,
},
"volumeSnapshotRef": map[string]interface{}{
"name": snapshotName,
"namespace": snapshotNamespace,
},
"driver": csiDriverName,
"deletionPolicy": deletionPolicy,
"volumeSnapshotClassName": volumeSnapshotClassName,
},
},
}
return snapshotContent
}
func genShortHash(s string) string {
h := sha1.New()
h.Write([]byte(s))
bs := h.Sum(nil)
return fmt.Sprintf("%x", bs)[:7]
}
func getPreProvisionedSnapshotContentName(snapshotHandle string) string {
return fmt.Sprintf("pre-provisioned-snapcontent-%s", genShortHash(snapshotHandle))
}
func getPreProvisionedSnapshotName(snapshotHandle string) string {
return fmt.Sprintf("pre-provisioned-snapshot-%s", genShortHash(snapshotHandle))
}
// StartPodLogs begins capturing log output and events from current
// and future pods running in the namespace of the framework. That

View File

@ -75,6 +75,8 @@ func InitSnapshottableTestSuite() TestSuite {
TestPatterns: []testpatterns.TestPattern{
testpatterns.DynamicSnapshotDelete,
testpatterns.DynamicSnapshotRetain,
testpatterns.PreprovisionedSnapshotDelete,
testpatterns.PreprovisionedSnapshotRetain,
},
SupportedSizeRange: e2evolume.SizeRange{
Min: "1Mi",
@ -94,7 +96,6 @@ func (s *snapshottableTestSuite) SkipRedundantSuite(driver TestDriver, pattern t
func (s *snapshottableTestSuite) DefineTests(driver TestDriver, pattern testpatterns.TestPattern) {
ginkgo.BeforeEach(func() {
// Check preconditions.
framework.ExpectEqual(pattern.SnapshotType, testpatterns.DynamicCreatedSnapshot)
dInfo := driver.GetDriverInfo()
ok := false
sDriver, ok = driver.(SnapshottableTestDriver)
@ -355,6 +356,10 @@ type SnapshotResource struct {
// different test pattern snapshot provisioning and deletion policy
func CreateSnapshotResource(sDriver SnapshottableTestDriver, config *PerTestConfig, pattern testpatterns.TestPattern, pvcName string, pvcNamespace string) *SnapshotResource {
var err error
if pattern.SnapshotType != testpatterns.DynamicCreatedSnapshot || pattern.SnapshotType != testpatterns.PreprovisionedCreatedSnapshot {
err = fmt.Errorf("SnapshotType must be set to either DynamicCreatedSnapshot or PreprovisionedCreatedSnapshot")
framework.ExpectNoError(err)
}
r := SnapshotResource{
Config: config,
Pattern: pattern,
@ -366,7 +371,7 @@ func CreateSnapshotResource(sDriver SnapshottableTestDriver, config *PerTestConf
if r.Vsclass == nil {
framework.Failf("Failed to get snapshot class based on test config")
}
r.Vsclass.Object["deletionPolicy"] = pattern.SnapshotDeletionPolicy
r.Vsclass.Object["deletionPolicy"] = pattern.SnapshotDeletionPolicy.String()
r.Vsclass, err = dc.Resource(SnapshotClassGVR).Create(context.TODO(), r.Vsclass, metav1.CreateOptions{})
framework.ExpectNoError(err)
@ -402,14 +407,68 @@ func CreateSnapshotResource(sDriver SnapshottableTestDriver, config *PerTestConf
// to pre-provision the snapshot as we normally would using their API.
// We instead dynamically take a snapshot and create another snapshot using
// the first snapshot's snapshot handle.
ginkgo.Skip("Preprovisioned test not implemented")
ginkgo.By("taking a snapshot with deletion policy retain")
ginkgo.By("recording the volume handle and status.snapshotHandle")
ginkgo.By("taking a dynamic snapshot")
r.Vs = getSnapshot(pvcName, pvcNamespace, r.Vsclass.GetName())
r.Vs, err = dc.Resource(SnapshotGVR).Namespace(r.Vs.GetNamespace()).Create(context.TODO(), r.Vs, metav1.CreateOptions{})
framework.ExpectNoError(err)
err = WaitForSnapshotReady(dc, r.Vs.GetNamespace(), r.Vs.GetName(), framework.Poll, framework.SnapshotCreateTimeout)
framework.ExpectNoError(err)
r.Vs, err = dc.Resource(SnapshotGVR).Namespace(r.Vs.GetNamespace()).Get(context.TODO(), r.Vs.GetName(), metav1.GetOptions{})
snapshotStatus := r.Vs.Object["status"].(map[string]interface{})
snapshotContentName := snapshotStatus["boundVolumeSnapshotContentName"].(string)
framework.Logf("received snapshotStatus %v", snapshotStatus)
framework.Logf("snapshotContentName %s", snapshotContentName)
framework.ExpectNoError(err)
ginkgo.By("updating the snapshot content deletion policy to retain")
r.Vscontent, err = dc.Resource(SnapshotContentGVR).Get(context.TODO(), snapshotContentName, metav1.GetOptions{})
framework.ExpectNoError(err)
r.Vscontent.Object["spec"].(map[string]interface{})["deletionPolicy"] = "Retain"
r.Vscontent, err = dc.Resource(SnapshotContentGVR).Update(context.TODO(), r.Vscontent, metav1.UpdateOptions{})
framework.ExpectNoError(err)
ginkgo.By("recording the volume handle and snapshotHandle")
snapshotHandle := r.Vscontent.Object["status"].(map[string]interface{})["snapshotHandle"].(string)
framework.Logf("Recording snapshot handle: %s", snapshotHandle)
csiDriverName := r.Vsclass.Object["driver"].(string)
ginkgo.By("deleting the snapshot and snapshot content") // TODO: test what happens when I have two snapshot content that refer to the same content
err = dc.Resource(SnapshotGVR).Namespace(r.Vs.GetNamespace()).Delete(context.TODO(), r.Vs.GetName(), metav1.DeleteOptions{})
framework.ExpectNoError(err)
ginkgo.By("checking the Snapshot has been deleted")
err = utils.WaitForNamespacedGVRDeletion(dc, SnapshotGVR, r.Vs.GetName(), r.Vs.GetNamespace(), framework.Poll, framework.SnapshotDeleteTimeout)
framework.ExpectNoError(err)
err = dc.Resource(SnapshotContentGVR).Delete(context.TODO(), r.Vscontent.GetName(), metav1.DeleteOptions{})
framework.ExpectNoError(err)
ginkgo.By("checking the Snapshot content has been deleted")
err = utils.WaitForGVRDeletion(dc, SnapshotContentGVR, r.Vscontent.GetName(), framework.Poll, framework.SnapshotDeleteTimeout)
framework.ExpectNoError(err)
ginkgo.By("creating a snapshot content with the snapshot handle")
r.Vscontent = getPreProvisionedSnapshotContent(getPreProvisionedSnapshotName(snapshotHandle), pvcNamespace, snapshotHandle, pattern.SnapshotDeletionPolicy.String(), csiDriverName, r.Vsclass.GetName())
r.Vscontent, err = dc.Resource(SnapshotContentGVR).Create(context.TODO(), r.Vscontent, metav1.CreateOptions{})
framework.ExpectNoError(err)
ginkgo.By("creating a snapshot with that snapshot content")
default:
err = fmt.Errorf("SnapshotType must be set to either DynamicCreatedSnapshot or PreprovisionedCreatedSnapshot")
r.Vs = getPreProvisionedSnapshot(getPreProvisionedSnapshotContentName(snapshotHandle), pvcNamespace, snapshotHandle)
r.Vs, err = dc.Resource(SnapshotGVR).Namespace(r.Vs.GetNamespace()).Create(context.TODO(), r.Vs, metav1.CreateOptions{})
framework.ExpectNoError(err)
err = WaitForSnapshotReady(dc, r.Vs.GetNamespace(), r.Vs.GetName(), framework.Poll, framework.SnapshotCreateTimeout)
framework.ExpectNoError(err)
ginkgo.By("getting the snapshot and snapshot content")
r.Vs, err = dc.Resource(SnapshotGVR).Namespace(r.Vs.GetNamespace()).Get(context.TODO(), r.Vs.GetName(), metav1.GetOptions{})
framework.ExpectNoError(err)
r.Vscontent, err = dc.Resource(SnapshotContentGVR).Get(context.TODO(), r.Vscontent.GetName(), metav1.GetOptions{})
framework.ExpectNoError(err)
}
return &r