Skip to content

Commit 0d9e9b8

Browse files
authored
Merge pull request #616 from stuggi/depl_ready
[deployment/statefulset] add IsReady func
2 parents 5a4c5f4 + ddf9e62 commit 0d9e9b8

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

modules/common/deployment/deployment.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,14 @@ func GetDeploymentWithName(
127127

128128
return depl, nil
129129
}
130+
131+
// IsReady - validates when deployment is ready deployed to whats being requested
132+
// - the requested replicas in the spec matches the ReadyReplicas of the status
133+
// - the Status.Replicas match Status.ReadyReplicas. if a deployment update is in progress, Replicas > ReadyReplicas
134+
// - both when the Generatation of the object matches the ObservedGeneration of the Status
135+
func IsReady(deployment appsv1.Deployment) bool {
136+
return deployment.Spec.Replicas != nil &&
137+
*deployment.Spec.Replicas == deployment.Status.ReadyReplicas &&
138+
deployment.Status.Replicas == deployment.Status.ReadyReplicas &&
139+
deployment.Generation == deployment.Status.ObservedGeneration
140+
}

modules/common/statefulset/statefulset.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,12 @@ func (s *StatefulSet) Delete(
138138

139139
return nil
140140
}
141+
142+
// IsReady - validates when deployment is ready deployed to whats being requested
143+
// - the requested replicas in the spec matches the ReadyReplicas of the status
144+
// - both when the Generatation of the object matches the ObservedGeneration of the Status
145+
func IsReady(deployment appsv1.StatefulSet) bool {
146+
return deployment.Spec.Replicas != nil &&
147+
*deployment.Spec.Replicas == deployment.Status.ReadyReplicas &&
148+
deployment.Generation == deployment.Status.ObservedGeneration
149+
}

modules/common/test/helpers/deployment.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package helpers
1515

1616
import (
1717
"encoding/json"
18+
"fmt"
1819

1920
networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
2021
"github.com/onsi/gomega"
@@ -59,8 +60,10 @@ func (tc *TestHelper) SimulateDeploymentAnyNumberReplicaReady(name types.Namespa
5960
gomega.Eventually(func(g gomega.Gomega) {
6061
deployment := tc.GetDeployment(name)
6162

63+
deployment.Status.AvailableReplicas = replica
6264
deployment.Status.Replicas = replica
6365
deployment.Status.ReadyReplicas = replica
66+
deployment.Status.UpdatedReplicas = replica
6467
deployment.Status.ObservedGeneration = deployment.Generation
6568
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, deployment)).To(gomega.Succeed())
6669
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
@@ -77,8 +80,10 @@ func (tc *TestHelper) SimulateDeploymentReplicaReady(name types.NamespacedName)
7780
gomega.Eventually(func(g gomega.Gomega) {
7881
deployment := tc.GetDeployment(name)
7982

83+
deployment.Status.AvailableReplicas = *deployment.Spec.Replicas
8084
deployment.Status.Replicas = *deployment.Spec.Replicas
8185
deployment.Status.ReadyReplicas = *deployment.Spec.Replicas
86+
deployment.Status.UpdatedReplicas = *deployment.Spec.Replicas
8287
deployment.Status.ObservedGeneration = deployment.Generation
8388
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, deployment)).To(gomega.Succeed())
8489
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
@@ -139,8 +144,10 @@ func (tc *TestHelper) SimulateDeploymentReadyWithPods(name types.NamespacedName,
139144

140145
gomega.Eventually(func(g gomega.Gomega) {
141146
depl := tc.GetDeployment(name)
147+
depl.Status.AvailableReplicas = *depl.Spec.Replicas
142148
depl.Status.Replicas = *depl.Spec.Replicas
143149
depl.Status.ReadyReplicas = *depl.Spec.Replicas
150+
depl.Status.UpdatedReplicas = *depl.Spec.Replicas
144151
depl.Status.ObservedGeneration = depl.Generation
145152
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, depl)).To(gomega.Succeed())
146153

@@ -157,3 +164,106 @@ func (tc *TestHelper) AssertDeploymentDoesNotExist(name types.NamespacedName) {
157164
g.Expect(k8s_errors.IsNotFound(err)).To(gomega.BeTrue())
158165
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
159166
}
167+
168+
// SimulateDeploymentProgressing function retrieves the Deployment resource and
169+
// simulate that replicas are progressing
170+
// Example usage:
171+
//
172+
// th.SimulateDeploymentProgressing(ironicNames.INAName)
173+
func (tc *TestHelper) SimulateDeploymentProgressing(name types.NamespacedName) {
174+
gomega.Eventually(func(g gomega.Gomega) {
175+
deployment := tc.GetDeployment(name)
176+
177+
deployment.Status.AvailableReplicas = *deployment.Spec.Replicas
178+
deployment.Status.Replicas = *deployment.Spec.Replicas + 1
179+
deployment.Status.ReadyReplicas = *deployment.Spec.Replicas
180+
deployment.Status.UpdatedReplicas = *deployment.Spec.Replicas
181+
deployment.Status.ObservedGeneration = deployment.Generation
182+
183+
/*
184+
conditions:
185+
- lastTransitionTime: "2025-03-11T10:53:00Z"
186+
lastUpdateTime: "2025-03-11T10:53:00Z"
187+
message: Deployment has minimum availability.
188+
reason: MinimumReplicasAvailable
189+
status: "True"
190+
type: Available
191+
- lastTransitionTime: "2025-03-11T16:17:41Z"
192+
lastUpdateTime: "2025-03-11T16:27:31Z"
193+
message: ReplicaSet "keystone-869cb5d44c" is progressing.
194+
reason: ReplicaSetUpdated
195+
status: "True"
196+
type: Progressing
197+
*/
198+
199+
deployment.Status.Conditions = []appsv1.DeploymentCondition{
200+
{
201+
Message: "Deployment has minimum availability",
202+
Reason: "MinimumReplicasAvailable",
203+
Status: corev1.ConditionTrue,
204+
Type: appsv1.DeploymentAvailable,
205+
},
206+
{
207+
Message: fmt.Sprintf("ReplicaSet \"%s-869cb5d44c\" is progressing.", deployment.Name),
208+
Reason: "ReplicaSetUpdated",
209+
Status: corev1.ConditionTrue,
210+
Type: appsv1.DeploymentProgressing,
211+
}}
212+
213+
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, deployment)).To(gomega.Succeed())
214+
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
215+
216+
tc.Logger.Info("Simulated Deployment progressing", "on", name)
217+
}
218+
219+
// SimulateDeploymentProgressDeadlineExceeded function retrieves the Deployment resource and
220+
// simulate that it hit ProgressDeadlineExceeded
221+
// Example usage:
222+
//
223+
// th.SimulateDeploymentProgressDeadlineExceeded(ironicNames.INAName)
224+
func (tc *TestHelper) SimulateDeploymentProgressDeadlineExceeded(name types.NamespacedName) {
225+
gomega.Eventually(func(g gomega.Gomega) {
226+
deployment := tc.GetDeployment(name)
227+
228+
deployment.Status.AvailableReplicas = *deployment.Spec.Replicas
229+
deployment.Status.Replicas = *deployment.Spec.Replicas + 1
230+
deployment.Status.ReadyReplicas = *deployment.Spec.Replicas
231+
deployment.Status.UpdatedReplicas = *deployment.Spec.Replicas
232+
deployment.Status.ObservedGeneration = deployment.Generation
233+
deployment.Status.UnavailableReplicas = 1
234+
235+
/*
236+
conditions:
237+
- lastTransitionTime: "2025-03-14T11:09:42Z"
238+
lastUpdateTime: "2025-03-14T11:09:42Z"
239+
message: Deployment has minimum availability.
240+
reason: MinimumReplicasAvailable
241+
status: "True"
242+
type: Available
243+
- lastTransitionTime: "2025-03-18T13:49:18Z"
244+
lastUpdateTime: "2025-03-18T13:49:18Z"
245+
message: ReplicaSet "keystone-5d9c965546" has timed out progressing.
246+
reason: ProgressDeadlineExceeded
247+
status: "False"
248+
type: Progressing
249+
*/
250+
251+
deployment.Status.Conditions = []appsv1.DeploymentCondition{
252+
{
253+
Message: "Deployment has minimum availability",
254+
Reason: "MinimumReplicasAvailable",
255+
Status: corev1.ConditionTrue,
256+
Type: appsv1.DeploymentAvailable,
257+
},
258+
{
259+
Message: fmt.Sprintf("ReplicaSet \"%s-869cb5d44c\" has timed out progressing.", deployment.Name),
260+
Reason: "ProgressDeadlineExceeded",
261+
Status: corev1.ConditionFalse,
262+
Type: appsv1.DeploymentProgressing,
263+
}}
264+
265+
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, deployment)).To(gomega.Succeed())
266+
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
267+
268+
tc.Logger.Info("Simulated Deployment ProgressDeadlineExceeded", "on", name)
269+
}

modules/common/test/helpers/statefulset.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ func (tc *TestHelper) GetStatefulSet(name types.NamespacedName) *appsv1.Stateful
4747
func (tc *TestHelper) SimulateStatefulSetReplicaReady(name types.NamespacedName) {
4848
gomega.Eventually(func(g gomega.Gomega) {
4949
ss := tc.GetStatefulSet(name)
50+
ss.Status.AvailableReplicas = *ss.Spec.Replicas
5051
ss.Status.Replicas = *ss.Spec.Replicas
5152
ss.Status.ReadyReplicas = *ss.Spec.Replicas
53+
ss.Status.UpdatedReplicas = *ss.Spec.Replicas
5254
ss.Status.ObservedGeneration = ss.Generation
5355
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, ss)).To(gomega.Succeed())
5456

@@ -108,8 +110,10 @@ func (tc *TestHelper) SimulateStatefulSetReplicaReadyWithPods(name types.Namespa
108110

109111
gomega.Eventually(func(g gomega.Gomega) {
110112
ss := tc.GetStatefulSet(name)
113+
ss.Status.AvailableReplicas = *ss.Spec.Replicas
111114
ss.Status.Replicas = *ss.Spec.Replicas
112115
ss.Status.ReadyReplicas = *ss.Spec.Replicas
116+
ss.Status.UpdatedReplicas = *ss.Spec.Replicas
113117
ss.Status.ObservedGeneration = ss.Generation
114118
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, ss)).To(gomega.Succeed())
115119

@@ -126,3 +130,23 @@ func (tc *TestHelper) AssertStatefulSetDoesNotExist(name types.NamespacedName) {
126130
g.Expect(k8s_errors.IsNotFound(err)).To(gomega.BeTrue())
127131
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
128132
}
133+
134+
// SimulateStatefulSetProgressing function retrieves the StatefulSet resource and
135+
// simulate that replicas are progressing
136+
// Example usage:
137+
//
138+
// th.SimulateStatefulSetReplicaReady(types.NamespacedName{Name: "test-statefulset", Namespace: "test-namespace"})
139+
func (tc *TestHelper) SimulateStatefulSetProgressing(name types.NamespacedName) {
140+
gomega.Eventually(func(g gomega.Gomega) {
141+
ss := tc.GetStatefulSet(name)
142+
ss.Status.AvailableReplicas = *ss.Spec.Replicas - 1
143+
ss.Status.Replicas = *ss.Spec.Replicas
144+
ss.Status.ReadyReplicas = *ss.Spec.Replicas - 1
145+
ss.Status.UpdatedReplicas = *ss.Spec.Replicas
146+
ss.Status.ObservedGeneration = ss.Generation
147+
148+
g.Expect(tc.K8sClient.Status().Update(tc.Ctx, ss)).To(gomega.Succeed())
149+
150+
}, tc.Timeout, tc.Interval).Should(gomega.Succeed())
151+
tc.Logger.Info("Simulated statefulset progressing", "on", name)
152+
}

0 commit comments

Comments
 (0)