Skip to content

Commit 8b10f6b

Browse files
committed
init
1 parent 116d1c3 commit 8b10f6b

File tree

2 files changed

+228
-9
lines changed

2 files changed

+228
-9
lines changed

controllers/apps/rollout/transformer_rollout_create.go

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ package rollout
2121

2222
import (
2323
"fmt"
24+
"time"
2425

2526
"github.com/pkg/errors"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2628
"k8s.io/apimachinery/pkg/util/intstr"
2729
"k8s.io/utils/ptr"
2830

@@ -165,7 +167,134 @@ func (t *rolloutCreateTransformer) promote(transCtx *rolloutTransformContext,
165167
return nil
166168
}
167169

168-
// TODO: promote
170+
// Find the canary instance template
171+
prefix := replaceInstanceTemplateNamePrefix(transCtx.Rollout)
172+
var canaryTpl *appsv1.InstanceTemplate
173+
for i := range spec.Instances {
174+
if spec.Instances[i].Name == prefix {
175+
canaryTpl = &spec.Instances[i]
176+
break
177+
}
178+
}
179+
if canaryTpl == nil {
180+
// Canary instance template not found, nothing to promote
181+
return nil
182+
}
183+
184+
// Check if canary replicas have reached target
185+
// This should be guaranteed by the caller (promote is only called when replicas+targetReplicas <= spec.Replicas)
186+
// but we check anyway
187+
if canaryTpl.Replicas == nil || *canaryTpl.Replicas < targetReplicas {
188+
// Canary replicas not yet reached target, should still be in rolling phase
189+
return nil
190+
}
191+
192+
// Find the component status to check promotion timestamps
193+
rollout := transCtx.Rollout
194+
var compStatus *appsv1alpha1.RolloutComponentStatus
195+
for i := range rollout.Status.Components {
196+
if rollout.Status.Components[i].Name == comp.Name {
197+
compStatus = &rollout.Status.Components[i]
198+
break
199+
}
200+
}
201+
if compStatus == nil {
202+
// Component status not found, should not happen
203+
return nil
204+
}
205+
206+
// Check promotion delay
207+
promotion := comp.Strategy.Create.Promotion
208+
delaySeconds := ptr.Deref(promotion.DelaySeconds, 30)
209+
210+
// Use LastScaleUpTimestamp as promotion start time
211+
// When canary replicas first reach target, LastScaleUpTimestamp should be set
212+
if !compStatus.LastScaleUpTimestamp.IsZero() {
213+
elapsed := time.Since(compStatus.LastScaleUpTimestamp.Time)
214+
if elapsed < time.Duration(delaySeconds)*time.Second {
215+
// Delay not yet passed, requeue
216+
remaining := time.Duration(delaySeconds)*time.Second - elapsed
217+
return controllerutil.NewDelayedRequeueError(remaining, fmt.Sprintf("waiting for promotion delay: %v remaining", remaining))
218+
}
219+
} else {
220+
// First time reaching target, set the timestamp
221+
compStatus.LastScaleUpTimestamp = metav1.Now()
222+
return controllerutil.NewDelayedRequeueError(time.Second, "setting promotion start time")
223+
}
224+
225+
// Check pre-promotion condition if specified
226+
if promotion.Condition != nil && promotion.Condition.Prev != nil {
227+
// TODO: implement condition checking
228+
// For now, just log that condition checking is not implemented
229+
// return fmt.Errorf("pre-promotion condition checking not implemented yet")
230+
}
231+
232+
// Execute promotion: mark canary instance template as non-canary
233+
canaryTpl.Canary = ptr.To(false)
234+
235+
// Check if we need to scale down old instances
236+
scaleDownDelaySeconds := ptr.Deref(promotion.ScaleDownDelaySeconds, 30)
237+
if scaleDownDelaySeconds > 0 {
238+
// Check if scale down delay has passed since promotion started
239+
// We use LastScaleUpTimestamp as promotion start time
240+
elapsedSincePromotion := time.Since(compStatus.LastScaleUpTimestamp.Time)
241+
if elapsedSincePromotion < time.Duration(scaleDownDelaySeconds)*time.Second {
242+
// Scale down delay not yet passed
243+
remaining := time.Duration(scaleDownDelaySeconds)*time.Second - elapsedSincePromotion
244+
return controllerutil.NewDelayedRequeueError(remaining, fmt.Sprintf("waiting for scale down delay: %v remaining", remaining))
245+
}
246+
}
247+
248+
// Scale down old instances: reduce original replicas
249+
// The original replicas are stored in 'replicas' parameter
250+
// We need to find the original instance template(s) and reduce their replicas
251+
// For simplicity, we assume there's a default instance template (without the prefix)
252+
// or we need to identify which instances are old
253+
254+
// For now, we'll reduce the total replicas to match targetReplicas (canary replicas)
255+
// since canary instances are now promoted to stable
256+
// This assumes canary instances replace old instances one-to-one
257+
// spec.Replicas should already include canary replicas, so we need to reduce it by (replicas - targetReplicas)
258+
// where 'replicas' is the original stable replicas count
259+
scaleDownCount := replicas - targetReplicas
260+
if scaleDownCount > 0 {
261+
// Reduce total replicas
262+
spec.Replicas -= scaleDownCount
263+
264+
// Also need to reduce replicas in the original instance template(s)
265+
// For now, we assume there's a default instance template
266+
for i := range spec.Instances {
267+
if spec.Instances[i].Name != prefix && spec.Instances[i].Replicas != nil && *spec.Instances[i].Replicas > 0 {
268+
// Reduce replicas of this old instance template
269+
oldReplicas := *spec.Instances[i].Replicas
270+
reduceBy := scaleDownCount
271+
if reduceBy > oldReplicas {
272+
reduceBy = oldReplicas
273+
}
274+
spec.Instances[i].Replicas = ptr.To(oldReplicas - reduceBy)
275+
scaleDownCount -= reduceBy
276+
277+
// Add to scale down instances in status
278+
// TODO: track which specific instances are scaled down
279+
if compStatus != nil {
280+
// For simplicity, just mark that scaling down happened
281+
compStatus.LastScaleDownTimestamp = metav1.Now()
282+
}
283+
284+
if scaleDownCount <= 0 {
285+
break
286+
}
287+
}
288+
}
289+
}
290+
291+
// Check post-promotion condition if specified
292+
if promotion.Condition != nil && promotion.Condition.Post != nil {
293+
// TODO: implement condition checking
294+
// For now, just log that condition checking is not implemented
295+
// return fmt.Errorf("post-promotion condition checking not implemented yet")
296+
}
169297

298+
// Promotion completed
170299
return nil
171300
}

controllers/apps/rollout/transformer_rollout_status.go

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package rollout
2222
import (
2323
"slices"
2424
"strings"
25+
"time"
2526

2627
corev1 "k8s.io/api/core/v1"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -271,16 +272,105 @@ func (t *rolloutStatusTransformer) compCreate(transCtx *rolloutTransformContext,
271272

272273
// All canary pods are ready and reached target count
273274
// Check promotion strategy
274-
if comp.Strategy.Create != nil && comp.Strategy.Create.Promotion != nil && ptr.Deref(comp.Strategy.Create.Promotion.Auto, false) {
275-
// Auto promotion enabled
276-
// For now, if all canary pods are ready and reached target, consider it succeed
277-
// TODO: implement promotion delay and conditions
278-
return appsv1alpha1.SucceedRolloutState, nil
275+
if comp.Strategy.Create == nil || comp.Strategy.Create.Promotion == nil {
276+
// No promotion configured, stay in rolling state until manual promotion
277+
return appsv1alpha1.RollingRolloutState, nil
279278
}
280279

281-
// No auto promotion or promotion not configured
282-
// Stay in rolling state until manual promotion
283-
return appsv1alpha1.RollingRolloutState, nil
280+
promotion := comp.Strategy.Create.Promotion
281+
282+
// Find component status
283+
var compStatus *appsv1alpha1.RolloutComponentStatus
284+
for i := range rollout.Status.Components {
285+
if rollout.Status.Components[i].Name == comp.Name {
286+
compStatus = &rollout.Status.Components[i]
287+
break
288+
}
289+
}
290+
if compStatus == nil {
291+
// Component status not found, should not happen
292+
return appsv1alpha1.RollingRolloutState, nil
293+
}
294+
295+
// Check if auto promotion is enabled
296+
if !ptr.Deref(promotion.Auto, false) {
297+
// Auto promotion not enabled, stay in rolling state
298+
return appsv1alpha1.RollingRolloutState, nil
299+
}
300+
301+
// Auto promotion enabled
302+
// Check promotion delay
303+
delaySeconds := ptr.Deref(promotion.DelaySeconds, 30)
304+
if !compStatus.LastScaleUpTimestamp.IsZero() {
305+
elapsed := time.Since(compStatus.LastScaleUpTimestamp.Time)
306+
if elapsed < time.Duration(delaySeconds)*time.Second {
307+
// Promotion delay not yet passed
308+
return appsv1alpha1.RollingRolloutState, nil
309+
}
310+
} else {
311+
// LastScaleUpTimestamp not set yet, promotion hasn't started
312+
return appsv1alpha1.RollingRolloutState, nil
313+
}
314+
315+
// Check pre-promotion condition if specified
316+
if promotion.Condition != nil && promotion.Condition.Prev != nil {
317+
// TODO: implement condition checking
318+
// For now, assume condition is not met if specified
319+
return appsv1alpha1.RollingRolloutState, nil
320+
}
321+
322+
// Check if canary instance template is still marked as canary
323+
// Find the canary instance template
324+
var canaryTpl *appsv1.InstanceTemplate
325+
for i := range spec.Instances {
326+
if strings.HasPrefix(spec.Instances[i].Name, prefix) {
327+
canaryTpl = &spec.Instances[i]
328+
break
329+
}
330+
}
331+
if canaryTpl != nil && ptr.Deref(canaryTpl.Canary, false) {
332+
// Canary instance template is still marked as canary
333+
// Promotion hasn't been executed yet
334+
return appsv1alpha1.RollingRolloutState, nil
335+
}
336+
337+
// Check scale down delay
338+
scaleDownDelaySeconds := ptr.Deref(promotion.ScaleDownDelaySeconds, 30)
339+
if scaleDownDelaySeconds > 0 {
340+
// Check if scale down delay has passed since promotion started
341+
// We use LastScaleUpTimestamp as promotion start time
342+
elapsedSincePromotion := time.Since(compStatus.LastScaleUpTimestamp.Time)
343+
if elapsedSincePromotion < time.Duration(scaleDownDelaySeconds)*time.Second {
344+
// Scale down delay not yet passed
345+
return appsv1alpha1.RollingRolloutState, nil
346+
}
347+
}
348+
349+
// Check if old instances have been scaled down
350+
// Count total replicas from all instance templates
351+
totalReplicasFromTemplates := int32(0)
352+
for _, tpl := range spec.Instances {
353+
if tpl.Replicas != nil {
354+
totalReplicasFromTemplates += *tpl.Replicas
355+
}
356+
}
357+
358+
// Check if total replicas match the target (canary replicas count)
359+
// After promotion, total replicas should equal canaryPodCnt (promoted replicas)
360+
if totalReplicasFromTemplates != canaryPodCnt {
361+
// Old instances not fully scaled down yet
362+
return appsv1alpha1.RollingRolloutState, nil
363+
}
364+
365+
// Check post-promotion condition if specified
366+
if promotion.Condition != nil && promotion.Condition.Post != nil {
367+
// TODO: implement condition checking
368+
// For now, assume condition is not met if specified
369+
return appsv1alpha1.RollingRolloutState, nil
370+
}
371+
372+
// All promotion steps completed
373+
return appsv1alpha1.SucceedRolloutState, nil
284374
}
285375

286376
func (t *rolloutStatusTransformer) compSpec(transCtx *rolloutTransformContext, compName string) *appsv1.ClusterComponentSpec {

0 commit comments

Comments
 (0)