mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
fix: tests added, edge cases protection added
This commit is contained in:
@@ -24,7 +24,6 @@ spec:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
maxWeight: 50
|
||||
stepWeights: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
|
||||
@@ -14,20 +14,27 @@ import (
|
||||
"github.com/weaveworks/flagger/pkg/router"
|
||||
)
|
||||
|
||||
func (c *Controller) min(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *Controller) maxWeight(canary *flaggerv1.Canary) int {
|
||||
var stepWeightsLen = len(canary.GetAnalysis().StepWeights)
|
||||
if stepWeightsLen > 0 {
|
||||
return canary.GetAnalysis().StepWeights[stepWeightsLen-1]
|
||||
return c.min(c.totalWeight(canary), canary.GetAnalysis().StepWeights[stepWeightsLen-1])
|
||||
}
|
||||
if canary.GetAnalysis().MaxWeight > 0 {
|
||||
return canary.GetAnalysis().MaxWeight
|
||||
}
|
||||
// set max weight default value to 100%
|
||||
return 100
|
||||
// set max weight default value to total weight
|
||||
return c.totalWeight(canary)
|
||||
}
|
||||
|
||||
func (c *Controller) totalWeight(canary *flaggerv1.Canary) int {
|
||||
// set max weight default value to 100%
|
||||
// set total weight default value to 100%
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -37,17 +44,30 @@ func (c *Controller) nextStepWeight(canary *flaggerv1.Canary, canaryWeight int)
|
||||
return canary.GetAnalysis().StepWeight
|
||||
}
|
||||
|
||||
if canaryWeight == 0 {
|
||||
return canary.GetAnalysis().StepWeights[0]
|
||||
totalWeight := c.totalWeight(canary)
|
||||
maxStep := totalWeight - canaryWeight
|
||||
|
||||
// If maxStep is zero we need to promote, so any non zero step weight will move the canary to promotion.
|
||||
// This is the same use case as the last step via StepWeight.
|
||||
if maxStep == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
// return min of maxStep and the calculated step to avoid going above totalWeight
|
||||
|
||||
// initial step
|
||||
if canaryWeight == 0 {
|
||||
return c.min(maxStep, canary.GetAnalysis().StepWeights[0])
|
||||
}
|
||||
|
||||
// find the current step and return the difference in weight
|
||||
for i := 0; i < stepWeightsLen-1; i++ {
|
||||
if canary.GetAnalysis().StepWeights[i] == canaryWeight {
|
||||
return canary.GetAnalysis().StepWeights[i+1] - canaryWeight
|
||||
return c.min(maxStep, canary.GetAnalysis().StepWeights[i+1]-canaryWeight)
|
||||
}
|
||||
}
|
||||
|
||||
return c.totalWeight(canary) - canaryWeight
|
||||
return maxStep
|
||||
}
|
||||
|
||||
// scheduleCanaries synchronises the canary map with the jobs map,
|
||||
|
||||
@@ -11,8 +11,28 @@ import (
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
totalWeight = 100
|
||||
)
|
||||
|
||||
func TestScheduler_ServicePromotion(t *testing.T) {
|
||||
mocks := newDeploymentFixture(newTestServiceCanary())
|
||||
testServicePromotion(t, newTestServiceCanary(), []int{totalWeight, 80, 60, 40})
|
||||
}
|
||||
|
||||
func TestScheduler_ServicePromotionMaxWeight(t *testing.T) {
|
||||
testServicePromotion(t, newTestServiceCanaryMaxWeight(), []int{totalWeight, 50, 0})
|
||||
}
|
||||
|
||||
func TestScheduler_ServicePromotionWithWeightsHappyCase(t *testing.T) {
|
||||
testServicePromotion(t, newTestServiceCanaryWithWeightsHappyCase(), []int{totalWeight, 99, 98, 90, 20})
|
||||
}
|
||||
|
||||
func TestScheduler_ServicePromotionWithWeightsOverflow(t *testing.T) {
|
||||
testServicePromotion(t, newTestServiceCanaryWithWeightsOverflow(), []int{totalWeight, 99, 98, 90, 0})
|
||||
}
|
||||
|
||||
func testServicePromotion(t *testing.T, canary *flaggerv1.Canary, expectedPrimaryWeigths []int) {
|
||||
mocks := newDeploymentFixture(canary)
|
||||
|
||||
// init
|
||||
mocks.ctrl.advanceCanary("podinfo", "default")
|
||||
@@ -27,19 +47,15 @@ func TestScheduler_ServicePromotion(t *testing.T) {
|
||||
_, err = mocks.kubeClient.CoreV1().Services("default").Update(context.TODO(), svc2, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// detect service spec changes
|
||||
mocks.ctrl.advanceCanary("podinfo", "default")
|
||||
|
||||
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||
require.NoError(t, err)
|
||||
|
||||
primaryWeight = 60
|
||||
canaryWeight = 40
|
||||
err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight, mirrored)
|
||||
require.NoError(t, err)
|
||||
|
||||
// advance
|
||||
mocks.ctrl.advanceCanary("podinfo", "default")
|
||||
for _, expectedPrimaryWeigth := range expectedPrimaryWeigths {
|
||||
mocks.ctrl.advanceCanary("podinfo", "default")
|
||||
expectedCanaryWeight := totalWeight - expectedPrimaryWeigth
|
||||
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedPrimaryWeigth, primaryWeight)
|
||||
assert.Equal(t, expectedCanaryWeight, canaryWeight)
|
||||
assert.False(t, mirrored)
|
||||
}
|
||||
|
||||
// check progressing status
|
||||
c, err = mocks.flaggerClient.FlaggerV1beta1().Canaries("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
|
||||
@@ -57,9 +73,9 @@ func TestScheduler_ServicePromotion(t *testing.T) {
|
||||
// finalise
|
||||
mocks.ctrl.advanceCanary("podinfo", "default")
|
||||
|
||||
primaryWeight, canaryWeight, mirrored, err = mocks.router.GetRoutes(mocks.canary)
|
||||
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 100, primaryWeight)
|
||||
assert.Equal(t, totalWeight, primaryWeight)
|
||||
assert.Equal(t, 0, canaryWeight)
|
||||
assert.False(t, mirrored)
|
||||
|
||||
@@ -101,7 +117,7 @@ func newTestServiceCanary() *flaggerv1.Canary {
|
||||
},
|
||||
Analysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
StepWeight: 20,
|
||||
MaxWeight: 50,
|
||||
Metrics: []flaggerv1.CanaryMetric{
|
||||
{
|
||||
@@ -120,3 +136,115 @@ func newTestServiceCanary() *flaggerv1.Canary {
|
||||
}
|
||||
return cd
|
||||
}
|
||||
|
||||
func newTestServiceCanaryMaxWeight() *flaggerv1.Canary {
|
||||
cd := &flaggerv1.Canary{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: flaggerv1.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "podinfo",
|
||||
},
|
||||
Spec: flaggerv1.CanarySpec{
|
||||
TargetRef: flaggerv1.CrossNamespaceObjectReference{
|
||||
Name: "podinfo",
|
||||
APIVersion: "core/v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
},
|
||||
Analysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 50,
|
||||
MaxWeight: totalWeight,
|
||||
Metrics: []flaggerv1.CanaryMetric{
|
||||
{
|
||||
Name: "request-success-rate",
|
||||
Threshold: 99,
|
||||
Interval: "1m",
|
||||
},
|
||||
{
|
||||
Name: "request-duration",
|
||||
Threshold: 500000,
|
||||
Interval: "1m",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return cd
|
||||
}
|
||||
|
||||
func newTestServiceCanaryWithWeightsHappyCase() *flaggerv1.Canary {
|
||||
cd := &flaggerv1.Canary{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: flaggerv1.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "podinfo",
|
||||
},
|
||||
Spec: flaggerv1.CanarySpec{
|
||||
TargetRef: flaggerv1.CrossNamespaceObjectReference{
|
||||
Name: "podinfo",
|
||||
APIVersion: "core/v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
},
|
||||
Analysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeights: []int{1, 2, 10, 80},
|
||||
Metrics: []flaggerv1.CanaryMetric{
|
||||
{
|
||||
Name: "request-success-rate",
|
||||
Threshold: 99,
|
||||
Interval: "1m",
|
||||
},
|
||||
{
|
||||
Name: "request-duration",
|
||||
Threshold: 500000,
|
||||
Interval: "1m",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return cd
|
||||
}
|
||||
|
||||
func newTestServiceCanaryWithWeightsOverflow() *flaggerv1.Canary {
|
||||
cd := &flaggerv1.Canary{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: flaggerv1.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "podinfo",
|
||||
},
|
||||
Spec: flaggerv1.CanarySpec{
|
||||
TargetRef: flaggerv1.CrossNamespaceObjectReference{
|
||||
Name: "podinfo",
|
||||
APIVersion: "core/v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
},
|
||||
Analysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeights: []int{1, 2, 10, totalWeight + 100},
|
||||
Metrics: []flaggerv1.CanaryMetric{
|
||||
{
|
||||
Name: "request-success-rate",
|
||||
Threshold: 99,
|
||||
Interval: "1m",
|
||||
},
|
||||
{
|
||||
Name: "request-duration",
|
||||
Threshold: 500000,
|
||||
Interval: "1m",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return cd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user