Skip to content

Commit 7b84eb9

Browse files
Merge pull request #2034 from jsafrane/progressing-deployment
OCPBUGS-62634, OCPBUGS-62624: Fix DeploymentController Progressing
2 parents e9c2485 + 54061ce commit 7b84eb9

File tree

3 files changed

+85
-14
lines changed

3 files changed

+85
-14
lines changed

pkg/operator/deploymentcontroller/deployment_controller.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/openshift/library-go/pkg/operator/resource/resourceread"
1818
"github.com/openshift/library-go/pkg/operator/v1helpers"
1919
appsv1 "k8s.io/api/apps/v1"
20+
corev1 "k8s.io/api/core/v1"
2021
apierrors "k8s.io/apimachinery/pkg/api/errors"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223
"k8s.io/apimachinery/pkg/util/errors"
@@ -256,7 +257,6 @@ func (c *DeploymentController) syncManaged(ctx context.Context, opSpec *opv1.Ope
256257
if err != nil {
257258
return err
258259
}
259-
260260
// Create an OperatorStatusApplyConfiguration with generations
261261
status := applyoperatorv1.OperatorStatus().
262262
WithGenerations(&applyoperatorv1.GenerationStatusApplyConfiguration{
@@ -357,6 +357,7 @@ func (c *DeploymentController) getDeployment(opSpec *opv1.OperatorSpec) (*appsv1
357357
}
358358

359359
func isProgressing(deployment *appsv1.Deployment) (bool, string) {
360+
360361
var deploymentExpectedReplicas int32
361362
if deployment.Spec.Replicas != nil {
362363
deploymentExpectedReplicas = *deployment.Spec.Replicas
@@ -365,6 +366,8 @@ func isProgressing(deployment *appsv1.Deployment) (bool, string) {
365366
switch {
366367
case deployment.Generation != deployment.Status.ObservedGeneration:
367368
return true, "Waiting for Deployment to act on changes"
369+
case hasFinishedProgressing(deployment):
370+
return false, ""
368371
case deployment.Status.UnavailableReplicas > 0:
369372
return true, "Waiting for Deployment to deploy pods"
370373
case deployment.Status.UpdatedReplicas < deploymentExpectedReplicas:
@@ -374,3 +377,15 @@ func isProgressing(deployment *appsv1.Deployment) (bool, string) {
374377
}
375378
return false, ""
376379
}
380+
381+
func hasFinishedProgressing(deployment *appsv1.Deployment) bool {
382+
// Deployment whose rollout is complete gets Progressing condition with Reason NewReplicaSetAvailable condition.
383+
// https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#complete-deployment
384+
// Any subsequent missing replicas (e.g. caused by a node reboot) must not not change the Progressing condition.
385+
for _, cond := range deployment.Status.Conditions {
386+
if cond.Type == appsv1.DeploymentProgressing {
387+
return cond.Status == corev1.ConditionTrue && cond.Reason == "NewReplicaSetAvailable"
388+
}
389+
}
390+
return false
391+
}

pkg/operator/deploymentcontroller/deployment_controller_test.go

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,33 @@ package deploymentcontroller
22

33
import (
44
"context"
5-
clocktesting "k8s.io/utils/clock/testing"
65
"os"
76
"sort"
87
"time"
98

10-
"github.com/google/go-cmp/cmp"
11-
opv1 "github.com/openshift/api/operator/v1"
12-
"github.com/openshift/library-go/pkg/operator/management"
13-
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
14-
appsv1 "k8s.io/api/apps/v1"
15-
"k8s.io/apimachinery/pkg/api/equality"
16-
"k8s.io/apimachinery/pkg/api/errors"
17-
core "k8s.io/client-go/testing"
18-
199
"testing"
2010

11+
"github.com/google/go-cmp/cmp"
2112
configv1 "github.com/openshift/api/config/v1"
13+
opv1 "github.com/openshift/api/operator/v1"
2214
fakeconfig "github.com/openshift/client-go/config/clientset/versioned/fake"
2315
configinformers "github.com/openshift/client-go/config/informers/externalversions"
2416
"github.com/openshift/library-go/pkg/controller/factory"
2517
"github.com/openshift/library-go/pkg/operator/events"
18+
"github.com/openshift/library-go/pkg/operator/management"
19+
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
2620
"github.com/openshift/library-go/pkg/operator/v1helpers"
27-
21+
appsv1 "k8s.io/api/apps/v1"
22+
corev1 "k8s.io/api/core/v1"
2823
v1 "k8s.io/api/core/v1"
24+
"k8s.io/apimachinery/pkg/api/equality"
25+
"k8s.io/apimachinery/pkg/api/errors"
2926
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3027
"k8s.io/apimachinery/pkg/runtime"
3128
coreinformers "k8s.io/client-go/informers"
3229
fakecore "k8s.io/client-go/kubernetes/fake"
30+
core "k8s.io/client-go/testing"
31+
clocktesting "k8s.io/utils/clock/testing"
3332
)
3433

3534
const (
@@ -454,6 +453,7 @@ func sanitizeObjectMeta(meta *metav1.ObjectMeta) {
454453
}
455454

456455
func TestSync(t *testing.T) {
456+
const replica0 = 0
457457
const replica1 = 1
458458
testCases := []testCase{
459459
{
@@ -535,20 +535,65 @@ func TestSync(t *testing.T) {
535535
initialObjects: testObjects{
536536
deployment: makeDeployment(
537537
withDeploymentGeneration(1, 1),
538-
withDeploymentStatus(replica1, replica1, replica1)),
538+
withDeploymentStatus(replica1, replica1, replica1),
539+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionTrue)), // Deployment is fully deployed
539540
operator: makeFakeOperatorInstance(withGenerations(1)),
540541
},
541542
expectedObjects: testObjects{
542543
deployment: makeDeployment(
543544
withDeploymentGeneration(1, 1),
544-
withDeploymentStatus(replica1, replica1, replica1)),
545+
withDeploymentStatus(replica1, replica1, replica1),
546+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionTrue)), // Deployment is fully deployed
545547
operator: makeFakeOperatorInstance(
546548
// withStatus(replica1),
547549
withGenerations(1),
548550
withTrueConditions(conditionAvailable),
549551
withFalseConditions(conditionProgressing)),
550552
},
551553
},
554+
{
555+
// Deployment is fully deployed with a missing pod and its status is synced to CR
556+
name: "pod missing after fully deployed",
557+
initialObjects: testObjects{
558+
deployment: makeDeployment(
559+
withDeploymentGeneration(1, 1),
560+
withDeploymentStatus(replica0, replica0, replica0),
561+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionTrue)), // Deployment is fully deployed
562+
operator: makeFakeOperatorInstance(withGenerations(1)),
563+
},
564+
expectedObjects: testObjects{
565+
deployment: makeDeployment(
566+
withDeploymentGeneration(1, 1),
567+
withDeploymentStatus(replica0, replica0, replica0),
568+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionTrue)), // Deployment is fully deployed
569+
operator: makeFakeOperatorInstance(
570+
// withStatus(replica1),
571+
withGenerations(1),
572+
withFalseConditions(conditionAvailable), // No pod is running
573+
withFalseConditions(conditionProgressing)), // Despite missing pod, the operator is not progressing
574+
},
575+
},
576+
{
577+
name: "pod missing before fully deployed",
578+
initialObjects: testObjects{
579+
deployment: makeDeployment(
580+
withDeploymentGeneration(1, 1),
581+
withDeploymentStatus(replica0, replica0, replica0),
582+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionFalse)), // Deployment is not fully deployed
583+
operator: makeFakeOperatorInstance(withGenerations(1)),
584+
},
585+
expectedObjects: testObjects{
586+
deployment: makeDeployment(
587+
withDeploymentGeneration(1, 1),
588+
withDeploymentStatus(replica0, replica0, replica0),
589+
withDeploymentConditions(appsv1.DeploymentProgressing, "NewReplicaSetAvailable", corev1.ConditionFalse)), // Deployment is not fully deployed
590+
operator: makeFakeOperatorInstance(
591+
// withStatus(replica1),
592+
withGenerations(1),
593+
withFalseConditions(conditionAvailable), // No pod is running
594+
withTrueConditions(conditionProgressing)), // A pod is missing, the operator is progressing
595+
},
596+
},
552597
{
553598
// Deployment has wrong nr. of replicas, modified by user, and gets replaced by the operator.
554599
name: "deployment modified by user",

pkg/operator/deploymentcontroller/helpers_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ func withDeploymentGeneration(generations ...int64) deploymentModifier {
6666
}
6767
}
6868

69+
func withDeploymentConditions(conditionType appsv1.DeploymentConditionType, reason string, status v1.ConditionStatus) deploymentModifier {
70+
return func(instance *appsv1.Deployment) *appsv1.Deployment {
71+
instance.Status.Conditions = append(instance.Status.Conditions, appsv1.DeploymentCondition{
72+
Type: conditionType,
73+
Status: status,
74+
Reason: reason,
75+
})
76+
return instance
77+
}
78+
}
79+
6980
func TestWithReplicasHook(t *testing.T) {
7081
var (
7182
masterNodeLabels = map[string]string{"node-role.kubernetes.io/master": ""}

0 commit comments

Comments
 (0)