Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: verify the weight of the alb at the end of the rollout #3627

Merged
merged 8 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions rollout/trafficrouting.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ func (c *rolloutContext) reconcileTrafficRouting() error {
} else {
c.log.Infof("Desired weight (stepIdx: %s) %d not yet verified", indexString, desiredWeight)
c.enqueueRolloutAfter(c.rollout, defaults.GetRolloutVerifyRetryInterval())
// At the end of the rollout we need to verify the weight is correct, and return an error if not because we don't want the rest of the
// reconcile process to continue. We don't need to do this if we are in the middle of the rollout because the rest of the reconcile
// process won't scale down the old replicasets yet due to being in the middle of some steps.
if desiredWeight == weightutil.MaxTrafficWeight(c.rollout) && len(c.rollout.Spec.Strategy.Canary.Steps) >= int(*c.rollout.Status.CurrentStepIndex) {
return fmt.Errorf("end of rollout, desired weight %d not yet verified", desiredWeight)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion rollout/trafficrouting/alb/alb.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func (r *Reconciler) VerifyWeight(desiredWeight int32, additionalDestinations ..
return nil, nil
}

if !rolloututil.ShouldVerifyWeight(r.cfg.Rollout) {
if !rolloututil.ShouldVerifyWeight(r.cfg.Rollout, desiredWeight) {
// If we should not verify weight but the ALB status has not been set yet due to a Rollout resource just being
// installed in the cluster we want to actually run the rest of the function, so we do not return if
// r.cfg.Rollout.Status.ALB is nil. However, if we should not verify, and we have already updated the status once
Expand Down
43 changes: 43 additions & 0 deletions rollout/trafficrouting/alb/alb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,7 @@ func TestVerifyWeight(t *testing.T) {
{
var status v1alpha1.RolloutStatus
r, fakeClient := newFakeReconciler(&status)

fakeClient.loadBalancers = []*elbv2types.LoadBalancer{
{
LoadBalancerName: pointer.StringPtr("lb-abc123-name"),
Expand Down Expand Up @@ -955,6 +956,48 @@ func TestVerifyWeight(t *testing.T) {
assert.Equal(t, *status.ALB, *fakeClient.getAlbStatus("ingress"))
}

// LoadBalancer found, at max weight, end of rollout
{
var status v1alpha1.RolloutStatus
status.CurrentStepIndex = pointer.Int32Ptr(2)
r, fakeClient := newFakeReconciler(&status)
fakeClient.loadBalancers = []*elbv2types.LoadBalancer{
{
LoadBalancerName: pointer.StringPtr("lb-abc123-name"),
LoadBalancerArn: pointer.StringPtr("arn:aws:elasticloadbalancing:us-east-2:123456789012:loadbalancer/app/lb-abc123-name/1234567890123456"),
DNSName: pointer.StringPtr("verify-weight-test-abc-123.us-west-2.elb.amazonaws.com"),
},
}
fakeClient.targetGroups = []aws.TargetGroupMeta{
{
TargetGroup: elbv2types.TargetGroup{
TargetGroupName: pointer.StringPtr("canary-tg-abc123-name"),
TargetGroupArn: pointer.StringPtr("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/canary-tg-abc123-name/1234567890123456"),
},
Weight: pointer.Int32Ptr(100),
Tags: map[string]string{
aws.AWSLoadBalancerV2TagKeyResourceID: "default/ingress-canary-svc:443",
},
},
{
TargetGroup: elbv2types.TargetGroup{
TargetGroupName: pointer.StringPtr("stable-tg-abc123-name"),
TargetGroupArn: pointer.StringPtr("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/stable-tg-abc123-name/1234567890123456"),
},
Weight: pointer.Int32Ptr(0),
Tags: map[string]string{
aws.AWSLoadBalancerV2TagKeyResourceID: "default/ingress-stable-svc:443",
},
},
}

weightVerified, err := r.VerifyWeight(100)
assert.NoError(t, err)
assert.True(t, *weightVerified)
assert.Equal(t, status.ALBs[0], *status.ALB)
assert.Equal(t, *status.ALB, *fakeClient.getAlbStatus("ingress"))
}

// LoadBalancer found, but ARNs are unparsable
{
var status v1alpha1.RolloutStatus
Expand Down
47 changes: 47 additions & 0 deletions rollout/trafficrouting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,53 @@ func TestReconcileTrafficRoutingVerifyWeightFalse(t *testing.T) {
assert.True(t, enqueued)
}

func TestReconcileTrafficRoutingVerifyWeightEndOfRollout(t *testing.T) {
f := newFixture(t)
defer f.Close()

steps := []v1alpha1.CanaryStep{
{
SetWeight: pointer.Int32Ptr(10),
},
{
Pause: &v1alpha1.RolloutPause{},
},
}
r1 := newCanaryRollout("foo", 10, nil, steps, pointer.Int32Ptr(2), intstr.FromInt(1), intstr.FromInt(0))
r2 := bumpVersion(r1)
r2.Spec.Strategy.Canary.TrafficRouting = &v1alpha1.RolloutTrafficRouting{}
r2.Spec.Strategy.Canary.CanaryService = "canary"
r2.Spec.Strategy.Canary.StableService = "stable"

rs1 := newReplicaSetWithStatus(r1, 10, 10)
rs2 := newReplicaSetWithStatus(r2, 10, 10)

rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
canarySelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs2PodHash}
stableSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs1PodHash}
canarySvc := newService("canary", 80, canarySelector, r2)
stableSvc := newService("stable", 80, stableSelector, r2)

f.kubeobjects = append(f.kubeobjects, rs1, rs2, canarySvc, stableSvc)
f.replicaSetLister = append(f.replicaSetLister, rs1, rs2)

r2 = updateCanaryRolloutStatus(r2, rs1PodHash, 10, 0, 10, false)
f.rolloutLister = append(f.rolloutLister, r2)
f.objects = append(f.objects, r2)

f.fakeTrafficRouting = newUnmockedFakeTrafficRoutingReconciler()
f.fakeTrafficRouting.On("UpdateHash", mock.Anything, mock.Anything, mock.Anything).Return(nil)
f.fakeTrafficRouting.On("SetWeight", mock.Anything, mock.Anything).Return(func(desiredWeight int32, additionalDestinations ...v1alpha1.WeightDestination) error {
// make sure SetWeight was called with correct value
assert.Equal(t, int32(100), desiredWeight)
return nil
})
f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil)
f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(false), nil)
f.runExpectError(getKey(r2, t), true)
}

func TestRolloutUseDesiredWeight(t *testing.T) {
f := newFixture(t)
defer f.Close()
Expand Down
10 changes: 6 additions & 4 deletions utils/rollout/rolloututil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strconv"

"github.com/argoproj/argo-rollouts/utils/weightutil"

replicasetutil "github.com/argoproj/argo-rollouts/utils/replicaset"

"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
Expand Down Expand Up @@ -184,13 +186,13 @@ func CanaryStepString(c v1alpha1.CanaryStep) string {

// ShouldVerifyWeight We use this to test if we should verify weights because weight verification could involve
// API calls to the cloud provider which could incur rate limiting
func ShouldVerifyWeight(ro *v1alpha1.Rollout) bool {
func ShouldVerifyWeight(ro *v1alpha1.Rollout, desiredWeight int32) bool {
currentStep, _ := replicasetutil.GetCurrentCanaryStep(ro)
// If we are in the middle of an update at a setWeight step, also perform weight verification.
// Note that we don't do this every reconciliation because weight verification typically involves
// API calls to the cloud provider which could incur rate limitingq
shouldVerifyWeight := ro.Status.StableRS != "" &&
!IsFullyPromoted(ro) &&
currentStep != nil && currentStep.SetWeight != nil
shouldVerifyWeight := (ro.Status.StableRS != "" && !IsFullyPromoted(ro) && currentStep != nil && currentStep.SetWeight != nil) ||
(ro.Status.StableRS != "" && !IsFullyPromoted(ro) && currentStep == nil && desiredWeight == weightutil.MaxTrafficWeight(ro)) // We are at end of rollout

return shouldVerifyWeight
}
12 changes: 9 additions & 3 deletions utils/rollout/rolloututil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,21 @@ func TestShouldVerifyWeight(t *testing.T) {
ro.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{
SetWeight: pointer.Int32Ptr(20),
}}
assert.Equal(t, true, ShouldVerifyWeight(ro))
assert.Equal(t, true, ShouldVerifyWeight(ro, 20))

ro.Status.StableRS = ""
assert.Equal(t, false, ShouldVerifyWeight(ro))
assert.Equal(t, false, ShouldVerifyWeight(ro, 20))

ro.Status.StableRS = "34feab23f"
ro.Status.CurrentStepIndex = nil
ro.Spec.Strategy.Canary.Steps = nil
assert.Equal(t, false, ShouldVerifyWeight(ro))
assert.Equal(t, false, ShouldVerifyWeight(ro, 20))

// Test when the weight is 100, because we are at end of rollout
ro.Status.StableRS = "34feab23f"
ro.Status.CurrentStepIndex = nil
ro.Spec.Strategy.Canary.Steps = nil
assert.Equal(t, true, ShouldVerifyWeight(ro, 100))
}

func Test_isGenerationObserved(t *testing.T) {
Expand Down
Loading