Skip to content

Commit

Permalink
add ut
Browse files Browse the repository at this point in the history
  • Loading branch information
ColdsteelRail committed Feb 7, 2025
1 parent e258568 commit 988932b
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 6 deletions.
27 changes: 21 additions & 6 deletions pkg/controllers/collaset/pvccontrol/pvc_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,21 @@ func (pc *RealPvcControl) DeletePodUnusedPvcs(ctx context.Context, cls *appsv1al
return err
}

mountedPvcNames := sets.String{}
for _, container := range pod.Spec.Containers {
for _, v := range container.VolumeMounts {
mountedPvcNames.Insert(v.Name)
}
}

//delete pvc which is not claimed in templates
if err := deleteUnclaimedPvcs(pc.client, ctx, cls, oldPvcs); err != nil {
if err := deleteUnclaimedPvcs(pc.client, ctx, cls, oldPvcs, mountedPvcNames); err != nil {
return err
}

// delete old pvc if new pvc is provisioned and WhenScaled is "Delete"
if collasetutils.PvcPolicyWhenScaled(cls) == appsv1alpha1.DeletePersistentVolumeClaimRetentionPolicyType {
return deleteOldPvcs(pc.client, ctx, cls, newPvcs, oldPvcs)
return deleteOldPvcs(pc.client, ctx, cls, newPvcs, oldPvcs, mountedPvcNames)
}
return nil
}
Expand Down Expand Up @@ -258,7 +265,7 @@ func classifyPodPvcs(cls *appsv1alpha1.CollaSet, id string, existingPvcs []*core
}

// classify into updated and old pvcs
if val, exist := newTmpHash[pvcTmpName]; exist && val == hash {
if newTmpHash[pvcTmpName] == hash {
newPvcs[pvcTmpName] = pvc
} else {
oldPvcs[pvcTmpName] = pvc
Expand Down Expand Up @@ -304,16 +311,20 @@ func IsPodPvcTmpChanged(cls *appsv1alpha1.CollaSet, pod *corev1.Pod, existingPvc
return false, nil
}

func deleteUnclaimedPvcs(c client.Client, ctx context.Context, cls *appsv1alpha1.CollaSet, oldPvcs *map[string]*corev1.PersistentVolumeClaim) error {
func deleteUnclaimedPvcs(c client.Client, ctx context.Context, cls *appsv1alpha1.CollaSet, oldPvcs *map[string]*corev1.PersistentVolumeClaim, mountedPvcNames sets.String) error {
expectedNames := sets.String{}
for _, pvcTmp := range cls.Spec.VolumeClaimTemplates {
expectedNames.Insert(pvcTmp.Name)
}
for pvcTmpName, pvc := range *oldPvcs {
// if pvc is still mounted on pod, keep it
if mountedPvcNames.Has(pvcTmpName) {
continue
}
// if pvc is claimed in pvc templates, keep it
if expectedNames.Has(pvcTmpName) {
continue
}
// if pvc is not claimed in pvc templates, delete it
if err := c.Delete(ctx, pvc); err != nil {
return err
} else if err := collasetutils.ActiveExpectations.ExpectDelete(cls, expectations.Pvc, pvc.Name); err != nil {
Expand All @@ -323,8 +334,12 @@ func deleteUnclaimedPvcs(c client.Client, ctx context.Context, cls *appsv1alpha1
return nil
}

func deleteOldPvcs(c client.Client, ctx context.Context, cls *appsv1alpha1.CollaSet, newPvcs, oldPvcs *map[string]*corev1.PersistentVolumeClaim) error {
func deleteOldPvcs(c client.Client, ctx context.Context, cls *appsv1alpha1.CollaSet, newPvcs, oldPvcs *map[string]*corev1.PersistentVolumeClaim, mountedPvcNames sets.String) error {
for pvcTmpName, pvc := range *oldPvcs {
// if pvc is still mounted on pod, keep it
if mountedPvcNames.Has(pvcTmpName) {
continue
}
// if new pvc is not ready, keep this pvc
if _, newPvcExist := (*newPvcs)[pvcTmpName]; !newPvcExist {
continue
Expand Down
178 changes: 178 additions & 0 deletions test/e2e/apps/collaset.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,184 @@ var _ = SIGDescribe("CollaSet", func() {
Expect(errors.IsNotFound(err)).To(Equal(true))
})

framework.ConformanceIt("Include pod with different pvc template", func() {
cls1 := tester.NewCollaSet("collaset-inc-"+randStr, 1, appsv1alpha1.UpdateStrategy{RollingUpdate: &appsv1alpha1.RollingUpdateCollaSetStrategy{ByLabel: &appsv1alpha1.ByLabel{}}})
cls1.Spec.VolumeClaimTemplates = []v1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-test1",
},
Spec: v1.PersistentVolumeClaimSpec{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"storage": resource.MustParse("100m"),
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
}
cls1.Spec.Template.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
{
MountPath: "/path/to/mount",
Name: "pvc-test1",
},
}
Expect(tester.CreateCollaSet(cls1)).NotTo(HaveOccurred())
By("Wait for CollaSet1 status replicas satisfied")
Eventually(func() error { return tester.ExpectedStatusReplicas(cls1, 1, 1, 1, 1, 1) }, 30*time.Second, 3*time.Second).ShouldNot(HaveOccurred())

cls2 := tester.NewCollaSet("collaset-exc-"+randStr, 1, appsv1alpha1.UpdateStrategy{})
cls2.Spec.VolumeClaimTemplates = []v1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc-test2",
},
Spec: v1.PersistentVolumeClaimSpec{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"storage": resource.MustParse("100m"),
},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
},
},
}
cls2.Spec.Template.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{
{
MountPath: "/path/to/mount",
Name: "pvc-test2",
},
}
Expect(tester.CreateCollaSet(cls2)).NotTo(HaveOccurred())
By("Wait for CollaSet2 with different pvc status replicas satisfied")
Eventually(func() error { return tester.ExpectedStatusReplicas(cls2, 1, 1, 1, 1, 1) }, 30*time.Second, 3*time.Second).ShouldNot(HaveOccurred())

By("exclude pod and scale in 1 replicas from CollaSet2")
pvcs, err := tester.ListPVCForCollaSet(cls2)

Check failure on line 801 in test/e2e/apps/collaset.go

View workflow job for this annotation

GitHub Actions / Golang Lint

ineffectual assignment to err (ineffassign)
pods, err := tester.ListPodsForCollaSet(cls2)
Expect(err).NotTo(HaveOccurred())
PodToExclude := pods[0]
PvcToExclude := pvcs[0]
Expect(tester.UpdateCollaSet(cls2, func(cls *appsv1alpha1.CollaSet) {
cls.Spec.Replicas = int32Pointer(0)
cls.Spec.ScaleStrategy = appsv1alpha1.ScaleStrategy{
PodToExclude: []string{PodToExclude.Name},
}
})).NotTo(HaveOccurred())

By("Wait for CollaSet2 reconciled")
Eventually(func() bool {
if err := tester.GetCollaSet(cls2); err != nil {
return false
}
return cls2.Generation == cls2.Status.ObservedGeneration
}, 10*time.Second, 3*time.Second).Should(Equal(true))

By("Check pod is excluded")
excludedPodID := PodToExclude.Labels[appsv1alpha1.PodInstanceIDLabelKey]
Eventually(func() bool {
pods, err = tester.ListPodsForCollaSet(cls2)
Expect(err).Should(BeNil())
for i := range pods {
pod := pods[i]
if pod.Name == PodToExclude.Name {
return false
}
}
return true
}, 10*time.Second, 1*time.Second).Should(BeTrue())

By("Check pvc is excluded")
Eventually(func() bool {
pvcs, err := tester.ListPVCForCollaSet(cls2)
Expect(err).Should(BeNil())
for i := range pvcs {
pvc := pvcs[i]
if pvc.Labels[appsv1alpha1.PodInstanceIDLabelKey] == excludedPodID {
return false
}
}
return true
}, 10*time.Second, 1*time.Second).Should(BeTrue())

By("Wait for CollaSet2 reconciled")
Eventually(func() error { return tester.ExpectedStatusReplicas(cls2, 0, 0, 0, 0, 0) }, 30*time.Second, 3*time.Second).ShouldNot(HaveOccurred())

By("include pod and scale out 1 replicas in CollaSet1")
pods, err = tester.ListPodsForCollaSet(cls1)
Expect(err).NotTo(HaveOccurred())
PodToInclude := PodToExclude
PvcToInclude := PvcToExclude
Expect(tester.UpdatePod(PodToInclude, func(pod *v1.Pod) {
pod.Labels["owner"] = cls1.Name
})).NotTo(HaveOccurred())
Expect(tester.UpdatePvc(PvcToInclude, func(pvc *v1.PersistentVolumeClaim) {
pvc.Labels["owner"] = cls1.Name
})).NotTo(HaveOccurred())
Expect(tester.UpdateCollaSet(cls1, func(cls *appsv1alpha1.CollaSet) {
cls.Spec.Replicas = int32Pointer(2)
cls.Spec.ScaleStrategy = appsv1alpha1.ScaleStrategy{
PodToInclude: []string{PodToInclude.Name},
}
})).NotTo(HaveOccurred())

By("Wait for CollaSet1 reconciled")
Eventually(func() bool {
if err := tester.GetCollaSet(cls1); err != nil {
return false
}
return cls1.Generation == cls1.Status.ObservedGeneration
}, 10*time.Second, 3*time.Second).Should(Equal(true))

By("Check pod is included")
Eventually(func() bool {
pods, err = tester.ListPodsForCollaSet(cls1)
Expect(err).Should(BeNil())
for i := range pods {
pod := pods[i]
if pod.Name == PodToInclude.Name {
return true
}
}
return false
}, 30*time.Second, 1*time.Second).Should(BeTrue())

By("Check pvc is included")
Eventually(func() bool {
pvcs, err := tester.ListPVCForCollaSet(cls1)
Expect(err).Should(BeNil())
for i := range pvcs {
pvc := pvcs[i]
if pvc.Labels[appsv1alpha1.PvcTemplateLabelKey] == cls2.Spec.VolumeClaimTemplates[0].Name {
return true
}
}
return false
}, 30*time.Second, 1*time.Second).Should(BeTrue())

By("Update included pod from CollaSet1")
Expect(tester.UpdateCollaSet(cls1, func(cls *appsv1alpha1.CollaSet) {
cls.Spec.UpdateStrategy.RollingUpdate = &appsv1alpha1.RollingUpdateCollaSetStrategy{}
})).NotTo(HaveOccurred())

By("Wait for CollaSet1 reconciled")
Eventually(func() error { return tester.ExpectedStatusReplicas(cls1, 2, 2, 2, 2, 2) }, 30*time.Second, 3*time.Second).ShouldNot(HaveOccurred())

By("Check pvc from CollaSet2 is deleted")
Eventually(func() bool {
pvcs, err := tester.ListPVCForCollaSet(cls1)
Expect(err).Should(BeNil())
for i := range pvcs {
pvc := pvcs[i]
if pvc.Labels[appsv1alpha1.PvcTemplateLabelKey] == cls2.Spec.VolumeClaimTemplates[0].Name {
return false
}
}
return len(pvcs) == 2
}, 1000*time.Second, 1*time.Second).Should(BeTrue())
})

framework.ConformanceIt("PVC retention policy with scale in pods", func() {
cls := tester.NewCollaSet("collaset-"+randStr, 2, appsv1alpha1.UpdateStrategy{})
cls.Spec.ScaleStrategy.PersistentVolumeClaimRetentionPolicy = &appsv1alpha1.PersistentVolumeClaimRetentionPolicy{
Expand Down
13 changes: 13 additions & 0 deletions test/e2e/framework/collaset_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ func (t *CollaSetTester) UpdatePod(pod *v1.Pod, fn func(pod *v1.Pod)) error {
})
}

func (t *CollaSetTester) UpdatePvc(pvc *v1.PersistentVolumeClaim, fn func(pvc *v1.PersistentVolumeClaim)) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
err := t.client.Get(context.TODO(), types.NamespacedName{Namespace: pvc.Namespace, Name: pvc.Name}, pvc)
if err != nil {
return err
}

fn(pvc)
err = t.client.Update(context.TODO(), pvc)
return err
})
}

func (t *CollaSetTester) ExpectedStatusReplicas(cls *appsv1alpha1.CollaSet, replicas, readyReplicas, availableReplicas, updatedReplicas, totalReplicas int32) error {
if err := t.client.Get(context.TODO(), types.NamespacedName{Namespace: cls.Namespace, Name: cls.Name}, cls); err != nil {
return err
Expand Down

0 comments on commit 988932b

Please sign in to comment.