diff --git a/test/e2e/framework/pv_util.go b/test/e2e/framework/pv_util.go index 3b4784f848c..b4576fc02a1 100644 --- a/test/e2e/framework/pv_util.go +++ b/test/e2e/framework/pv_util.go @@ -182,7 +182,7 @@ func DeletePVCandValidatePV(c clientset.Interface, ns string, pvc *v1.Persistent // Wait for the PV's phase to return to be `expectPVPhase` Logf("Waiting for reclaim process to complete.") - err = WaitForPersistentVolumePhase(expectPVPhase, c, pv.Name, 1*time.Second, 300*time.Second) + err = WaitForPersistentVolumePhase(expectPVPhase, c, pv.Name, Poll, PVReclaimingTimeout) if err != nil { return fmt.Errorf("pv %q phase did not become %v: %v", pv.Name, expectPVPhase, err) } @@ -392,14 +392,14 @@ func CreatePVsPVCs(numpvs, numpvcs int, c clientset.Interface, ns string, pvConf func WaitOnPVandPVC(c clientset.Interface, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) error { // Wait for newly created PVC to bind to the PV Logf("Waiting for PV %v to bind to PVC %v", pv.Name, pvc.Name) - err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, pvc.Name, 3*time.Second, 300*time.Second) + err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, pvc.Name, Poll, ClaimBindingTimeout) if err != nil { return fmt.Errorf("PVC %q did not become Bound: %v", pvc.Name, err) } // Wait for PersistentVolume.Status.Phase to be Bound, which it should be // since the PVC is already bound. - err = WaitForPersistentVolumePhase(v1.VolumeBound, c, pv.Name, 3*time.Second, 300*time.Second) + err = WaitForPersistentVolumePhase(v1.VolumeBound, c, pv.Name, Poll, PVBindingTimeout) if err != nil { return fmt.Errorf("PV %q did not become Bound: %v", pv.Name, err) } @@ -445,7 +445,7 @@ func WaitAndVerifyBinds(c clientset.Interface, ns string, pvols PVMap, claims PV } for pvName := range pvols { - err := WaitForPersistentVolumePhase(v1.VolumeBound, c, pvName, 3*time.Second, 180*time.Second) + err := WaitForPersistentVolumePhase(v1.VolumeBound, c, pvName, Poll, PVBindingTimeout) if err != nil && len(pvols) > len(claims) { Logf("WARN: pv %v is not bound after max wait", pvName) Logf(" This may be ok since there are more pvs than pvcs") @@ -468,7 +468,7 @@ func WaitAndVerifyBinds(c clientset.Interface, ns string, pvols PVMap, claims PV return fmt.Errorf("internal: claims map is missing pvc %q", pvcKey) } - err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, cr.Name, 3*time.Second, 180*time.Second) + err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, cr.Name, Poll, ClaimBindingTimeout) if err != nil { return fmt.Errorf("PVC %q did not become Bound: %v", cr.Name, err) } diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index a1e4cd5f1c3..8d68b12af2a 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -154,6 +154,21 @@ const ( // How long claims have to become dynamically provisioned ClaimProvisionTimeout = 5 * time.Minute + // How long claims have to become bound + ClaimBindingTimeout = 3 * time.Minute + + // How long claims have to become deleted + ClaimDeletingTimeout = 3 * time.Minute + + // How long PVs have to beome reclaimed + PVReclaimingTimeout = 3 * time.Minute + + // How long PVs have to become bound + PVBindingTimeout = 3 * time.Minute + + // How long PVs have to become deleted + PVDeletingTimeout = 3 * time.Minute + // How long a node is allowed to become "Ready" after it is restarted before // the test is considered failed. RestartNodeReadyAgainTimeout = 5 * time.Minute diff --git a/test/e2e/storage/BUILD b/test/e2e/storage/BUILD index c5c0f372a1f..4affc7c0dd0 100644 --- a/test/e2e/storage/BUILD +++ b/test/e2e/storage/BUILD @@ -17,6 +17,7 @@ go_library( "persistent_volumes-disruptive.go", "persistent_volumes-gce.go", "persistent_volumes-local.go", + "pv_protection.go", "pvc_protection.go", "volume_expand.go", "volume_io.go", diff --git a/test/e2e/storage/pv_protection.go b/test/e2e/storage/pv_protection.go new file mode 100644 index 00000000000..502adf76529 --- /dev/null +++ b/test/e2e/storage/pv_protection.go @@ -0,0 +1,127 @@ +/* +Copyright 2018 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 storage + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/pkg/util/slice" + volumeutil "k8s.io/kubernetes/pkg/volume/util" + "k8s.io/kubernetes/test/e2e/framework" + "k8s.io/kubernetes/test/e2e/storage/utils" +) + +var _ = utils.SIGDescribe("PV Protection [Feature:StorageProtection]", func() { + var ( + client clientset.Interface + nameSpace string + err error + pvc *v1.PersistentVolumeClaim + pv *v1.PersistentVolume + pvConfig framework.PersistentVolumeConfig + pvcConfig framework.PersistentVolumeClaimConfig + volLabel labels.Set + selector *metav1.LabelSelector + ) + + f := framework.NewDefaultFramework("pv-protection") + BeforeEach(func() { + client = f.ClientSet + nameSpace = f.Namespace.Name + framework.ExpectNoError(framework.WaitForAllNodesSchedulable(client, framework.TestContext.NodeSchedulableTimeout)) + + // Enforce binding only within test space via selector labels + volLabel = labels.Set{framework.VolumeSelectorKey: nameSpace} + selector = metav1.SetAsLabelSelector(volLabel) + + pvConfig = framework.PersistentVolumeConfig{ + NamePrefix: "hostpath-", + Labels: volLabel, + PVSource: v1.PersistentVolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/tmp/data", + }, + }, + } + + pvcConfig = framework.PersistentVolumeClaimConfig{ + Annotations: map[string]string{ + v1.BetaStorageClassAnnotation: "", + }, + Selector: selector, + } + + By("Creating a PV") + // make the pv definitions + pv = framework.MakePersistentVolume(pvConfig) + // create the PV + pv, err = client.CoreV1().PersistentVolumes().Create(pv) + Expect(err).NotTo(HaveOccurred(), "Error creating PV") + + By("Checking that PV Protection finalizer is set") + pv, err = client.CoreV1().PersistentVolumes().Get(pv.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred(), "While getting PV status") + Expect(slice.ContainsString(pv.ObjectMeta.Finalizers, volumeutil.PVProtectionFinalizer, nil)).To(BeTrue()) + }) + + AfterEach(func() { + framework.Logf("AfterEach: Cleaning up test resources.") + if errs := framework.PVPVCCleanup(client, nameSpace, pv, pvc); len(errs) > 0 { + framework.Failf("AfterEach: Failed to delete PVC and/or PV. Errors: %v", utilerrors.NewAggregate(errs)) + } + }) + + It("Verify \"immediate\" deletion of a PV that is not bound to a PVC", func() { + By("Deleting the PV") + err = client.CoreV1().PersistentVolumes().Delete(pv.Name, metav1.NewDeleteOptions(0)) + Expect(err).NotTo(HaveOccurred(), "Error deleting PV") + framework.WaitForPersistentVolumeDeleted(client, pv.Name, framework.Poll, framework.PVDeletingTimeout) + }) + + It("Verify that PV bound to a PVC is not removed immediately", func() { + By("Creating a PVC") + pvc = framework.MakePersistentVolumeClaim(pvcConfig, nameSpace) + pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc) + Expect(err).NotTo(HaveOccurred(), "Error creating PVC") + + By("Waiting for PVC to become Bound") + err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, nameSpace, pvc.Name, framework.Poll, framework.ClaimBindingTimeout) + Expect(err).NotTo(HaveOccurred(), "Failed waiting for PVC to be bound %v", err) + + By("Deleting the PV, however, the PV must not be removed from the system as it's bound to a PVC") + err = client.CoreV1().PersistentVolumes().Delete(pv.Name, metav1.NewDeleteOptions(0)) + Expect(err).NotTo(HaveOccurred(), "Error deleting PV") + + By("Checking that the PV status is Terminating") + pv, err = client.CoreV1().PersistentVolumes().Get(pv.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred(), "While checking PV status") + Expect(pv.ObjectMeta.DeletionTimestamp).NotTo(Equal(nil)) + + By("Deleting the PVC that is bound to the PV") + err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, metav1.NewDeleteOptions(0)) + Expect(err).NotTo(HaveOccurred(), "Error deleting PVC") + + By("Checking that the PV is automatically removed from the system because it's no longer bound to a PVC") + framework.WaitForPersistentVolumeDeleted(client, pv.Name, framework.Poll, framework.PVDeletingTimeout) + }) +}) diff --git a/test/e2e/storage/pvc_protection.go b/test/e2e/storage/pvc_protection.go index 131999e510b..5b968a6ee22 100644 --- a/test/e2e/storage/pvc_protection.go +++ b/test/e2e/storage/pvc_protection.go @@ -57,7 +57,7 @@ var _ = utils.SIGDescribe("PVC Protection [Feature:StorageProtection]", func() { pvcCreatedAndNotDeleted = true By("Waiting for PVC to become Bound") - err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, nameSpace, pvc.Name, framework.Poll, framework.ClaimProvisionTimeout) + err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, nameSpace, pvc.Name, framework.Poll, framework.ClaimBindingTimeout) Expect(err).NotTo(HaveOccurred(), "Failed waiting for PVC to be bound %v", err) By("Checking that PVC Protection finalizer is set") @@ -76,7 +76,7 @@ var _ = utils.SIGDescribe("PVC Protection [Feature:StorageProtection]", func() { By("Deleting the PVC") err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, metav1.NewDeleteOptions(0)) Expect(err).NotTo(HaveOccurred(), "Error deleting PVC") - framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimProvisionTimeout) + framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout) pvcCreatedAndNotDeleted = false }) @@ -100,7 +100,7 @@ var _ = utils.SIGDescribe("PVC Protection [Feature:StorageProtection]", func() { Expect(err).NotTo(HaveOccurred(), "Error terminating and deleting pod") By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod") - framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimProvisionTimeout) + framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout) pvcCreatedAndNotDeleted = false }) @@ -137,7 +137,7 @@ var _ = utils.SIGDescribe("PVC Protection [Feature:StorageProtection]", func() { Expect(err).NotTo(HaveOccurred(), "Error terminating and deleting pod") By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod") - framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimProvisionTimeout) + framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout) pvcCreatedAndNotDeleted = false }) })