Skip to content

Commit 3a88a81

Browse files
committed
Fix the issue where the BatchRelease gets stuck in Verifying when the Deployment's PodTemplate already contains pod-template-hash label
Signed-off-by: AiRanthem <zhongtianyun.zty@alibaba-inc.com>
1 parent 00aae12 commit 3a88a81

File tree

3 files changed

+119
-4
lines changed

3 files changed

+119
-4
lines changed

pkg/controller/batchrelease/labelpatch/patcher.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package labelpatch
1818

1919
import (
2020
"context"
21+
"errors"
2122
"fmt"
2223
"strconv"
2324

@@ -84,18 +85,31 @@ func (r *realPatcher) patchPodBatchLabel(pods []*corev1.Pod, ctx *batchcontext.B
8485
// on the pod-template-hash is not recommended. Thus, we use the CloneSet algorithm to compute the
8586
// controller-revision-hash: this method is fully defined by OpenKruise and can ensure that the same
8687
// ReplicaSet produces the same value.
87-
owner := metav1.GetControllerOf(pod)
88+
owner := metav1.GetControllerOfNoCopy(pod)
8889
if owner != nil && owner.Kind == "ReplicaSet" {
8990
var hash string
9091
if cache, ok := revisionHashCache[owner.UID]; ok {
9192
hash = cache
9293
} else {
9394
rs := &v1.ReplicaSet{}
9495
if err := r.Get(context.Background(), types.NamespacedName{Namespace: pod.Namespace, Name: owner.Name}, rs); err != nil {
95-
klog.ErrorS(err, "Failed to get ReplicaSet", "pod", klog.KObj(pod), "rollout", r.logKey, "owner", owner.Name, "namespace", pod.Namespace)
96+
klog.ErrorS(err, "Failed to get ReplicaSet when patching pod", "pod", klog.KObj(pod), "rollout", r.logKey, "owner", owner.Name, "namespace", pod.Namespace)
9697
return err
9798
}
98-
delete(rs.Spec.Template.ObjectMeta.Labels, v1.DefaultDeploymentUniqueLabelKey)
99+
rsOwner := metav1.GetControllerOfNoCopy(rs)
100+
if rsOwner == nil || rsOwner.Kind != "Deployment" {
101+
klog.ErrorS(nil, "ReplicaSet has no deployment owner, skip patching")
102+
return errors.New("ReplicaSet has no deployment owner")
103+
}
104+
deploy := &v1.Deployment{}
105+
if err := r.Get(context.Background(), types.NamespacedName{Namespace: pod.Namespace, Name: rsOwner.Name}, deploy); err != nil {
106+
klog.ErrorS(err, "Failed to get Deployment when patching pod", "pod", klog.KObj(pod), "rollout", r.logKey, "owner", owner.Name, "namespace", pod.Namespace)
107+
return err
108+
}
109+
110+
if deploy.Spec.Template.Labels[v1.DefaultDeploymentUniqueLabelKey] == "" {
111+
delete(rs.Spec.Template.ObjectMeta.Labels, v1.DefaultDeploymentUniqueLabelKey)
112+
}
99113
hash = util.ComputeHash(&rs.Spec.Template, nil)
100114
revisionHashCache[owner.UID] = hash
101115
}

pkg/controller/batchrelease/labelpatch/patcher_test.go

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ import (
2828
corev1 "k8s.io/api/core/v1"
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3030
"k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/types"
3132
"k8s.io/apimachinery/pkg/util/intstr"
3233
"k8s.io/apimachinery/pkg/util/rand"
3334
"k8s.io/klog/v2"
3435
"k8s.io/utils/pointer"
36+
"k8s.io/utils/ptr"
3537
"sigs.k8s.io/controller-runtime/pkg/client"
3638
"sigs.k8s.io/controller-runtime/pkg/client/fake"
3739

@@ -269,6 +271,16 @@ func TestDeploymentPatch(t *testing.T) {
269271
rs := &appsv1.ReplicaSet{
270272
ObjectMeta: metav1.ObjectMeta{
271273
Name: "rs-1",
274+
OwnerReferences: []metav1.OwnerReference{
275+
{
276+
APIVersion: "apps/v1",
277+
Kind: "Deployment",
278+
Name: "deploy-1",
279+
UID: types.UID("deploy-1"),
280+
Controller: ptr.To(true),
281+
BlockOwnerDeletion: ptr.To(true),
282+
},
283+
},
272284
},
273285
Spec: appsv1.ReplicaSetSpec{
274286
Template: corev1.PodTemplateSpec{
@@ -288,6 +300,34 @@ func TestDeploymentPatch(t *testing.T) {
288300
},
289301
},
290302
}
303+
deploy := &appsv1.Deployment{
304+
ObjectMeta: metav1.ObjectMeta{
305+
Name: "deploy-1",
306+
},
307+
Spec: appsv1.DeploymentSpec{
308+
Replicas: ptr.To(int32(10)),
309+
Selector: &metav1.LabelSelector{
310+
MatchLabels: map[string]string{
311+
"app": "nginx",
312+
},
313+
},
314+
Template: corev1.PodTemplateSpec{
315+
ObjectMeta: metav1.ObjectMeta{
316+
Labels: map[string]string{
317+
"app": "nginx",
318+
},
319+
},
320+
Spec: corev1.PodSpec{
321+
Containers: []corev1.Container{
322+
{
323+
Name: "nginx",
324+
Image: "nginx:1.14.2",
325+
},
326+
},
327+
},
328+
},
329+
},
330+
}
291331
revision := util.ComputeHash(&rs.Spec.Template, nil)
292332
// randomly inserted to test cases, pods with this revision should be skipped and
293333
// the result should not be influenced
@@ -296,6 +336,8 @@ func TestDeploymentPatch(t *testing.T) {
296336
batchContext func() *batchcontext.BatchContext
297337
Batches []v1beta1.ReleaseBatch
298338
expectedPatched []int
339+
modifier func(rs *appsv1.ReplicaSet, deploy *appsv1.Deployment)
340+
expectErr bool
299341
}{
300342
"10 pods, 0 patched, 5 new patched": {
301343
batchContext: func() *batchcontext.BatchContext {
@@ -476,6 +518,54 @@ func TestDeploymentPatch(t *testing.T) {
476518
},
477519
expectedPatched: []int{3, 2},
478520
},
521+
"rs with no controller": {
522+
batchContext: func() *batchcontext.BatchContext {
523+
ctx := &batchcontext.BatchContext{
524+
RolloutID: "rollout-1",
525+
UpdateRevision: revision,
526+
PlannedUpdatedReplicas: 5,
527+
CurrentBatch: 1,
528+
Replicas: 10,
529+
}
530+
pods := generateDeploymentPods(1, 5, 3,
531+
"rollout-1", strconv.Itoa(1))
532+
ctx.Pods = pods
533+
return ctx
534+
},
535+
Batches: []v1beta1.ReleaseBatch{
536+
{CanaryReplicas: intstr.FromInt(2)},
537+
{CanaryReplicas: intstr.FromInt(5)},
538+
},
539+
expectedPatched: []int{3, 2},
540+
expectErr: true,
541+
modifier: func(rs *appsv1.ReplicaSet, deploy *appsv1.Deployment) {
542+
rs.OwnerReferences = nil
543+
},
544+
},
545+
"failed to get deployment": {
546+
batchContext: func() *batchcontext.BatchContext {
547+
ctx := &batchcontext.BatchContext{
548+
RolloutID: "rollout-1",
549+
UpdateRevision: revision,
550+
PlannedUpdatedReplicas: 5,
551+
CurrentBatch: 1,
552+
Replicas: 10,
553+
}
554+
pods := generateDeploymentPods(1, 5, 3,
555+
"rollout-1", strconv.Itoa(1))
556+
ctx.Pods = pods
557+
return ctx
558+
},
559+
Batches: []v1beta1.ReleaseBatch{
560+
{CanaryReplicas: intstr.FromInt(2)},
561+
{CanaryReplicas: intstr.FromInt(5)},
562+
},
563+
expectedPatched: []int{3, 2},
564+
expectErr: true,
565+
modifier: func(rs *appsv1.ReplicaSet, deploy *appsv1.Deployment) {
566+
deploy.Name = "not-exist"
567+
},
568+
},
479569
}
480570
for name, cs := range cases {
481571
t.Run(name, func(t *testing.T) {
@@ -504,12 +594,22 @@ func TestDeploymentPatch(t *testing.T) {
504594
for _, pod := range ctx.Pods {
505595
objects = append(objects, pod)
506596
}
507-
objects = append(objects, rs)
597+
rsCopy, deployCopy := rs.DeepCopy(), deploy.DeepCopy()
598+
if cs.modifier != nil {
599+
cs.modifier(rsCopy, deployCopy)
600+
}
601+
objects = append(objects, rsCopy, deployCopy)
508602
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
509603
patcher := NewLabelPatcher(cli, klog.ObjectRef{Name: "test"}, cs.Batches)
510604
if err := patcher.patchPodBatchLabel(ctx.Pods, ctx); err != nil {
605+
if cs.expectErr {
606+
return
607+
}
511608
t.Fatalf("failed to patch pods: %v", err)
512609
}
610+
if cs.expectErr {
611+
t.Fatalf("errors should occur")
612+
}
513613

514614
podList := &corev1.PodList{}
515615
if err := cli.List(context.TODO(), podList); err != nil {

test/e2e/rollout_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ var _ = SIGDescribe("Rollout", func() {
414414
workload.Spec.Template.Annotations = map[string]string{
415415
"pod": "stable",
416416
}
417+
workload.Spec.Template.Labels[apps.DefaultDeploymentUniqueLabelKey] = "abcdefg"
417418
CreateObject(workload)
418419
WaitDeploymentAllPodsReady(workload)
419420

0 commit comments

Comments
 (0)