Skip to content

Commit 7555224

Browse files
committed
CloneSet supports to reuse PVC during upgrade
Signed-off-by: Siyu Wang <[email protected]>
1 parent cd23dc1 commit 7555224

File tree

6 files changed

+110
-10
lines changed

6 files changed

+110
-10
lines changed

apis/apps/v1alpha1/well_known_labels.go

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const (
1010
// SpecifiedDeleteKey indicates this object should be deleted, and the value could be the deletion option.
1111
SpecifiedDeleteKey = "apps.kruise.io/specified-delete"
1212

13+
// KeepPVCForDeletionKey indicates should keep PVC for reuse when specified delete the pod.
14+
KeepPVCForDeletionKey = "apps.kruise.io/keep-pvc-for-deletion"
15+
1316
// ImagePreDownloadCreatedKey indicates the images of this revision have been pre-downloaded
1417
ImagePreDownloadCreatedKey = "apps.kruise.io/pre-predownload-created"
1518

pkg/controller/cloneset/sync/cloneset_scale.go

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/openkruise/kruise/pkg/util/expectations"
3737
"github.com/openkruise/kruise/pkg/util/lifecycle"
3838
"github.com/openkruise/kruise/pkg/util/revision"
39+
"github.com/openkruise/kruise/pkg/util/specifieddelete"
3940
)
4041

4142
const (
@@ -289,6 +290,9 @@ func (r *realControl) deletePods(cs *appsv1alpha1.CloneSet, podsToDelete []*v1.P
289290
modified = true
290291
r.recorder.Event(cs, v1.EventTypeNormal, "SuccessfulDelete", fmt.Sprintf("succeed to delete pod %s", pod.Name))
291292

293+
if specifieddelete.ShouldKeepPVC(pod) {
294+
continue
295+
}
292296
// delete pvcs which have the same instance-id
293297
for _, pvc := range pvcs {
294298
if pvc.Labels[appsv1alpha1.CloneSetInstanceID] != pod.Labels[appsv1alpha1.CloneSetInstanceID] {

pkg/controller/cloneset/sync/cloneset_update.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ func (c *realControl) updatePod(cs *appsv1alpha1.CloneSet, coreControl clonesetc
308308

309309
klog.V(2).InfoS("CloneSet started to patch Pod specified-delete for update", "cloneSet", klog.KObj(cs), "pod", klog.KObj(pod), "updateRevision", klog.KObj(updateRevision))
310310

311-
if patched, err := specifieddelete.PatchPodSpecifiedDelete(c.Client, pod, "true"); err != nil {
311+
keepPVC := !cs.Spec.ScaleStrategy.DisablePVCReuse && utilfeature.DefaultFeatureGate.Enabled(features.CloneSetPVCReuseDuringUpdate)
312+
if patched, err := specifieddelete.PatchPodSpecifiedDelete(c.Client, pod, keepPVC); err != nil {
312313
c.recorder.Eventf(cs, v1.EventTypeWarning, "FailedUpdatePodReCreate",
313314
"failed to patch pod specified-delete %s for update(revision %s): %v", pod.Name, updateRevision.Name, err)
314315
return 0, err

pkg/features/kruise_features.go

+5
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ const (
138138
// InPlaceWorkloadVerticalScaling enable CloneSet/Advanced StatefulSet controller to support vertical scaling
139139
// of managed Pods.
140140
InPlaceWorkloadVerticalScaling featuregate.Feature = "InPlaceWorkloadVerticalScaling"
141+
142+
// CloneSetPVCReuseDuringUpdate enables CloneSet to reuse PVC during update, and it also depends on
143+
// the spec.scaleStrategy.disablePVCReuse not to be true.
144+
CloneSetPVCReuseDuringUpdate featuregate.Feature = "CloneSetPVCReuseDuringUpdate"
141145
)
142146

143147
var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
@@ -175,6 +179,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
175179
StatefulSetAutoResizePVCGate: {Default: false, PreRelease: featuregate.Alpha},
176180
ForceDeleteTimeoutExpectationFeatureGate: {Default: false, PreRelease: featuregate.Alpha},
177181
InPlaceWorkloadVerticalScaling: {Default: false, PreRelease: featuregate.Alpha},
182+
CloneSetPVCReuseDuringUpdate: {Default: false, PreRelease: featuregate.Alpha},
178183
}
179184

180185
func init() {

pkg/util/specifieddelete/specified_delete.go

+23-9
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,43 @@ package specifieddelete
1818

1919
import (
2020
"context"
21-
"fmt"
2221

23-
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
2422
v1 "k8s.io/api/core/v1"
2523
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2624
"k8s.io/apimachinery/pkg/types"
2725
"sigs.k8s.io/controller-runtime/pkg/client"
26+
27+
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
28+
"github.com/openkruise/kruise/pkg/util"
2829
)
2930

3031
func IsSpecifiedDelete(obj metav1.Object) bool {
3132
_, ok := obj.GetLabels()[appsv1alpha1.SpecifiedDeleteKey]
3233
return ok
3334
}
3435

35-
func PatchPodSpecifiedDelete(c client.Client, pod *v1.Pod, value string) (bool, error) {
36+
func ShouldKeepPVC(obj metav1.Object) bool {
37+
return obj.GetLabels()[appsv1alpha1.KeepPVCForDeletionKey] == "true"
38+
}
39+
40+
func PatchPodSpecifiedDelete(c client.Client, pod *v1.Pod, keepPVC bool) (bool, error) {
3641
if _, ok := pod.Labels[appsv1alpha1.SpecifiedDeleteKey]; ok {
3742
return false, nil
3843
}
3944

40-
body := fmt.Sprintf(
41-
`{"metadata":{"labels":{"%s":"%s"}}}`,
42-
appsv1alpha1.SpecifiedDeleteKey,
43-
value,
44-
)
45-
return true, c.Patch(context.TODO(), pod, client.RawPatch(types.StrategicMergePatchType, []byte(body)))
45+
body := patchBody{Metadata: patchMeta{Labels: map[string]string{
46+
appsv1alpha1.SpecifiedDeleteKey: "true",
47+
}}}
48+
if keepPVC {
49+
body.Metadata.Labels[appsv1alpha1.KeepPVCForDeletionKey] = "true"
50+
}
51+
return true, c.Patch(context.TODO(), pod, client.RawPatch(types.StrategicMergePatchType, []byte(util.DumpJSON(body))))
52+
}
53+
54+
type patchBody struct {
55+
Metadata patchMeta `json:"metadata"`
56+
}
57+
58+
type patchMeta struct {
59+
Labels map[string]string `json:"labels"`
4660
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package specifieddelete
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
corev1 "k8s.io/api/core/v1"
9+
apiequality "k8s.io/apimachinery/pkg/api/equality"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
13+
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
14+
)
15+
16+
func TestPatchPodSpecifiedDelete(t *testing.T) {
17+
tests := []struct {
18+
pod *corev1.Pod
19+
keepPVC bool
20+
expected *corev1.Pod
21+
}{
22+
{
23+
pod: &corev1.Pod{
24+
ObjectMeta: metav1.ObjectMeta{
25+
Name: "test-pod",
26+
},
27+
Spec: corev1.PodSpec{},
28+
},
29+
keepPVC: false,
30+
expected: &corev1.Pod{
31+
ObjectMeta: metav1.ObjectMeta{
32+
Name: "test-pod",
33+
Labels: map[string]string{
34+
appsv1alpha1.SpecifiedDeleteKey: "true",
35+
},
36+
},
37+
Spec: corev1.PodSpec{},
38+
},
39+
},
40+
{
41+
pod: &corev1.Pod{
42+
ObjectMeta: metav1.ObjectMeta{
43+
Name: "test-pod",
44+
},
45+
Spec: corev1.PodSpec{},
46+
},
47+
keepPVC: true,
48+
expected: &corev1.Pod{
49+
ObjectMeta: metav1.ObjectMeta{
50+
Name: "test-pod",
51+
Labels: map[string]string{
52+
appsv1alpha1.SpecifiedDeleteKey: "true",
53+
appsv1alpha1.KeepPVCForDeletionKey: "true",
54+
},
55+
},
56+
Spec: corev1.PodSpec{},
57+
},
58+
},
59+
}
60+
61+
for i, test := range tests {
62+
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
63+
cli := fake.NewClientBuilder().WithObjects(test.pod).Build()
64+
_, err := PatchPodSpecifiedDelete(cli, test.pod, test.keepPVC)
65+
assert.NoError(t, err)
66+
67+
// patch will write result object into the given test.pod
68+
if apiequality.Semantic.DeepEqual(test.expected, test.pod) {
69+
t.Fatalf("expected %v but got %v", test.expected, test.pod)
70+
}
71+
})
72+
}
73+
}

0 commit comments

Comments
 (0)