fix: tests added, edge cases protection added

This commit is contained in:
robq99
2020-11-18 12:20:42 +01:00
parent 90bccf748b
commit c53cbac22c
3 changed files with 173 additions and 26 deletions
@@ -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
+28 -8
View File
@@ -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,
+145 -17
View File
@@ -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
}