mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
Merge pull request #463 from weaveworks/analysis-v1beta1
Rename spec.canaryAnalysis to spec.analysis
This commit is contained in:
@@ -14,8 +14,8 @@ jobs:
|
||||
name: Run go mod download
|
||||
command: go mod download
|
||||
- run:
|
||||
name: Run go fmt
|
||||
command: make test-fmt
|
||||
name: Check code formatting
|
||||
command: go install golang.org/x/tools/cmd/goimports && make test-fmt
|
||||
- run:
|
||||
name: Build Flagger
|
||||
command: |
|
||||
|
||||
@@ -2,7 +2,6 @@ TAG?=latest
|
||||
VERSION?=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"')
|
||||
VERSION_MINOR:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"' | rev | cut -d'.' -f2- | rev)
|
||||
PATCH:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"' | awk -F. '{print $$NF}')
|
||||
SOURCE_DIRS = cmd pkg/apis pkg/controller pkg/server pkg/canary pkg/metrics pkg/router pkg/notifier
|
||||
LT_VERSION?=$(shell grep 'VERSION' cmd/loadtester/main.go | awk '{ print $$4 }' | tr -d '"' | head -n1)
|
||||
TS=$(shell date +%Y-%m-%d_%H-%M-%S)
|
||||
|
||||
@@ -43,10 +42,12 @@ push:
|
||||
docker push weaveworks/flagger:$(VERSION)
|
||||
|
||||
fmt:
|
||||
gofmt -l -s -w $(SOURCE_DIRS)
|
||||
gofmt -l -s -w ./
|
||||
goimports -l -w ./
|
||||
|
||||
test-fmt:
|
||||
gofmt -l -s $(SOURCE_DIRS) | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi
|
||||
gofmt -l -s ./ | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi
|
||||
goimports -l ./ | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi
|
||||
|
||||
test-codegen:
|
||||
./hack/verify-codegen.sh
|
||||
|
||||
@@ -42,19 +42,19 @@ spec:
|
||||
priority: 1
|
||||
- name: Interval
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.interval
|
||||
JSONPath: .spec.analysis.interval
|
||||
priority: 1
|
||||
- name: Mirror
|
||||
type: boolean
|
||||
JSONPath: .spec.canaryAnalysis.mirror
|
||||
JSONPath: .spec.analysis.mirror
|
||||
priority: 1
|
||||
- name: StepWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||
JSONPath: .spec.analysis.stepWeight
|
||||
priority: 1
|
||||
- name: MaxWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.maxWeight
|
||||
JSONPath: .spec.analysis.maxWeight
|
||||
priority: 1
|
||||
- name: LastTransitionTime
|
||||
type: string
|
||||
@@ -66,7 +66,7 @@ spec:
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- canaryAnalysis
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
description: Traffic managent provider
|
||||
@@ -502,9 +502,12 @@ spec:
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
description: Canary analysis for this canary
|
||||
type: object
|
||||
oneOf:
|
||||
- required: ["interval", "threshold", "iterations"]
|
||||
- required: ["interval", "threshold", "stepWeight"]
|
||||
properties:
|
||||
interval:
|
||||
description: Schedule interval for this canary
|
||||
@@ -523,7 +526,7 @@ spec:
|
||||
description: Incremental traffic percentage step
|
||||
type: number
|
||||
mirror:
|
||||
description: Mirror traffic to canary before shifting
|
||||
description: Mirror traffic to canary
|
||||
type: boolean
|
||||
match:
|
||||
description: A/B testing match conditions
|
||||
|
||||
@@ -42,19 +42,19 @@ spec:
|
||||
priority: 1
|
||||
- name: Interval
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.interval
|
||||
JSONPath: .spec.analysis.interval
|
||||
priority: 1
|
||||
- name: Mirror
|
||||
type: boolean
|
||||
JSONPath: .spec.canaryAnalysis.mirror
|
||||
JSONPath: .spec.analysis.mirror
|
||||
priority: 1
|
||||
- name: StepWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||
JSONPath: .spec.analysis.stepWeight
|
||||
priority: 1
|
||||
- name: MaxWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.maxWeight
|
||||
JSONPath: .spec.analysis.maxWeight
|
||||
priority: 1
|
||||
- name: LastTransitionTime
|
||||
type: string
|
||||
@@ -66,7 +66,7 @@ spec:
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- canaryAnalysis
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
description: Traffic managent provider
|
||||
@@ -502,9 +502,12 @@ spec:
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
description: Canary analysis for this canary
|
||||
type: object
|
||||
oneOf:
|
||||
- required: ["interval", "threshold", "iterations"]
|
||||
- required: ["interval", "threshold", "stepWeight"]
|
||||
properties:
|
||||
interval:
|
||||
description: Schedule interval for this canary
|
||||
@@ -523,7 +526,7 @@ spec:
|
||||
description: Incremental traffic percentage step
|
||||
type: number
|
||||
mirror:
|
||||
description: Mirror traffic to canary before shifting
|
||||
description: Mirror traffic to canary
|
||||
type: boolean
|
||||
match:
|
||||
description: A/B testing match conditions
|
||||
|
||||
@@ -2,12 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/flagger/pkg/loadtester"
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
"github.com/weaveworks/flagger/pkg/signals"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
var VERSION = "0.12.1"
|
||||
|
||||
@@ -42,19 +42,19 @@ spec:
|
||||
priority: 1
|
||||
- name: Interval
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.interval
|
||||
JSONPath: .spec.analysis.interval
|
||||
priority: 1
|
||||
- name: Mirror
|
||||
type: boolean
|
||||
JSONPath: .spec.canaryAnalysis.mirror
|
||||
JSONPath: .spec.analysis.mirror
|
||||
priority: 1
|
||||
- name: StepWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||
JSONPath: .spec.analysis.stepWeight
|
||||
priority: 1
|
||||
- name: MaxWeight
|
||||
type: string
|
||||
JSONPath: .spec.canaryAnalysis.maxWeight
|
||||
JSONPath: .spec.analysis.maxWeight
|
||||
priority: 1
|
||||
- name: LastTransitionTime
|
||||
type: string
|
||||
@@ -66,7 +66,7 @@ spec:
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- canaryAnalysis
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
description: Traffic managent provider
|
||||
@@ -502,9 +502,12 @@ spec:
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
description: Canary analysis for this canary
|
||||
type: object
|
||||
oneOf:
|
||||
- required: ["interval", "threshold", "iterations"]
|
||||
- required: ["interval", "threshold", "stepWeight"]
|
||||
properties:
|
||||
interval:
|
||||
description: Schedule interval for this canary
|
||||
@@ -523,7 +526,7 @@ spec:
|
||||
description: Incremental traffic percentage step
|
||||
type: number
|
||||
mirror:
|
||||
description: Mirror traffic to canary before shifting
|
||||
description: Mirror traffic to canary
|
||||
type: boolean
|
||||
match:
|
||||
description: A/B testing match conditions
|
||||
|
||||
@@ -80,8 +80,11 @@ type CanarySpec struct {
|
||||
// Service defines how ClusterIP services, service mesh or ingress routing objects are generated
|
||||
Service CanaryService `json:"service"`
|
||||
|
||||
// CanaryAnalysis defines how the analysis should be performed
|
||||
CanaryAnalysis CanaryAnalysis `json:"canaryAnalysis"`
|
||||
// Analysis defines the validation process of a release
|
||||
Analysis *CanaryAnalysis `json:"analysis,omitempty"`
|
||||
|
||||
// Deprecated: replaced by Analysis
|
||||
CanaryAnalysis *CanaryAnalysis `json:"canaryAnalysis,omitempty"`
|
||||
|
||||
// ProgressDeadlineSeconds represents the maximum time in seconds for a
|
||||
// canary deployment to make progress before it is considered to be failed
|
||||
@@ -352,13 +355,22 @@ func (c *Canary) GetProgressDeadlineSeconds() int {
|
||||
return ProgressDeadlineSeconds
|
||||
}
|
||||
|
||||
// GetAnalysis returns the analysis v1beta1 or v1alpha3
|
||||
// to be removed along with spec.canaryAnalysis in v1
|
||||
func (c *Canary) GetAnalysis() *CanaryAnalysis {
|
||||
if c.Spec.Analysis != nil {
|
||||
return c.Spec.Analysis
|
||||
}
|
||||
return c.Spec.CanaryAnalysis
|
||||
}
|
||||
|
||||
// GetAnalysisInterval returns the canary analysis interval (default 60s)
|
||||
func (c *Canary) GetAnalysisInterval() time.Duration {
|
||||
if c.Spec.CanaryAnalysis.Interval == "" {
|
||||
if c.GetAnalysis().Interval == "" {
|
||||
return AnalysisInterval
|
||||
}
|
||||
|
||||
interval, err := time.ParseDuration(c.Spec.CanaryAnalysis.Interval)
|
||||
interval, err := time.ParseDuration(c.GetAnalysis().Interval)
|
||||
if err != nil {
|
||||
return AnalysisInterval
|
||||
}
|
||||
@@ -370,7 +382,24 @@ func (c *Canary) GetAnalysisInterval() time.Duration {
|
||||
return interval
|
||||
}
|
||||
|
||||
// GetAnalysisThreshold returns the canary threshold (default 1)
|
||||
func (c *Canary) GetAnalysisThreshold() int {
|
||||
if c.GetAnalysis().Threshold > 0 {
|
||||
return c.GetAnalysis().Threshold
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// GetMetricInterval returns the metric interval default value (1m)
|
||||
func (c *Canary) GetMetricInterval() string {
|
||||
return MetricInterval
|
||||
}
|
||||
|
||||
// SkipAnalysis returns true if the analysis is nil
|
||||
// or if spec.SkipAnalysis is true
|
||||
func (c *Canary) SkipAnalysis() bool {
|
||||
if c.Spec.Analysis == nil && c.Spec.CanaryAnalysis == nil {
|
||||
return true
|
||||
}
|
||||
return c.Spec.SkipAnalysis
|
||||
}
|
||||
|
||||
@@ -392,7 +392,16 @@ func (in *CanarySpec) DeepCopyInto(out *CanarySpec) {
|
||||
**out = **in
|
||||
}
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
in.CanaryAnalysis.DeepCopyInto(&out.CanaryAnalysis)
|
||||
if in.Analysis != nil {
|
||||
in, out := &in.Analysis, &out.Analysis
|
||||
*out = new(CanaryAnalysis)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CanaryAnalysis != nil {
|
||||
in, out := &in.CanaryAnalysis, &out.CanaryAnalysis
|
||||
*out = new(CanaryAnalysis)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ProgressDeadlineSeconds != nil {
|
||||
in, out := &in.ProgressDeadlineSeconds, &out.ProgressDeadlineSeconds
|
||||
*out = new(int32)
|
||||
|
||||
@@ -91,7 +91,7 @@ func (c *DaemonSetController) Initialize(cd *flaggerv1.Canary, skipLivenessCheck
|
||||
}
|
||||
|
||||
if cd.Status.Phase == "" || cd.Status.Phase == flaggerv1.CanaryPhaseInitializing {
|
||||
if !skipLivenessChecks && !cd.Spec.SkipAnalysis {
|
||||
if !skipLivenessChecks && !cd.SkipAnalysis() {
|
||||
_, readyErr := c.IsPrimaryReady(cd)
|
||||
if readyErr != nil {
|
||||
return readyErr
|
||||
|
||||
@@ -37,7 +37,7 @@ func (c *DeploymentController) Initialize(cd *flaggerv1.Canary, skipLivenessChec
|
||||
}
|
||||
|
||||
if cd.Status.Phase == "" || cd.Status.Phase == flaggerv1.CanaryPhaseInitializing {
|
||||
if !skipLivenessChecks && !cd.Spec.SkipAnalysis {
|
||||
if !skipLivenessChecks && !cd.SkipAnalysis() {
|
||||
_, readyErr := c.IsPrimaryReady(cd)
|
||||
if readyErr != nil {
|
||||
return readyErr
|
||||
|
||||
@@ -207,7 +207,7 @@ func newDeploymentControllerTestCanary() *flaggerv1.Canary {
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
}, Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
|
||||
+12
-11
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -30,8 +31,8 @@ func (c *Controller) recordEventWarningf(r *flaggerv1.Canary, template string, a
|
||||
|
||||
func (c *Controller) sendEventToWebhook(r *flaggerv1.Canary, eventType, template string, args []interface{}) {
|
||||
webhookOverride := false
|
||||
if len(r.Spec.CanaryAnalysis.Webhooks) > 0 {
|
||||
for _, canaryWebhook := range r.Spec.CanaryAnalysis.Webhooks {
|
||||
if len(r.GetAnalysis().Webhooks) > 0 {
|
||||
for _, canaryWebhook := range r.GetAnalysis().Webhooks {
|
||||
if canaryWebhook.Type == flaggerv1.EventHook {
|
||||
webhookOverride = true
|
||||
err := CallEventWebhook(r, canaryWebhook.URL, fmt.Sprintf(template, args...), eventType)
|
||||
@@ -51,7 +52,7 @@ func (c *Controller) sendEventToWebhook(r *flaggerv1.Canary, eventType, template
|
||||
}
|
||||
|
||||
func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bool, severity flaggerv1.AlertSeverity) {
|
||||
if c.notifier == nil && len(canary.Spec.CanaryAnalysis.Alerts) == 0 {
|
||||
if c.notifier == nil && len(canary.GetAnalysis().Alerts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
|
||||
}
|
||||
|
||||
// send alert with the global notifier
|
||||
if len(canary.Spec.CanaryAnalysis.Alerts) == 0 {
|
||||
if len(canary.GetAnalysis().Alerts) == 0 {
|
||||
err := c.notifier.Post(canary.Name, canary.Namespace, message, fields, string(severity))
|
||||
if err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
|
||||
@@ -72,7 +73,7 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
|
||||
}
|
||||
|
||||
// send canary alerts
|
||||
for _, alert := range canary.Spec.CanaryAnalysis.Alerts {
|
||||
for _, alert := range canary.GetAnalysis().Alerts {
|
||||
// determine if alert should be sent based on severity level
|
||||
shouldAlert := false
|
||||
if alert.Severity == flaggerv1.SeverityInfo {
|
||||
@@ -161,7 +162,7 @@ func alertMetadata(canary *flaggerv1.Canary) []notifier.Field {
|
||||
},
|
||||
notifier.Field{
|
||||
Name: "Failed checks threshold",
|
||||
Value: fmt.Sprintf("%v", canary.Spec.CanaryAnalysis.Threshold),
|
||||
Value: fmt.Sprintf("%v", canary.GetAnalysisThreshold()),
|
||||
},
|
||||
notifier.Field{
|
||||
Name: "Progress deadline",
|
||||
@@ -169,19 +170,19 @@ func alertMetadata(canary *flaggerv1.Canary) []notifier.Field {
|
||||
},
|
||||
)
|
||||
|
||||
if canary.Spec.CanaryAnalysis.StepWeight > 0 {
|
||||
if canary.GetAnalysis().StepWeight > 0 {
|
||||
fields = append(fields, notifier.Field{
|
||||
Name: "Traffic routing",
|
||||
Value: fmt.Sprintf("Weight step: %v max: %v",
|
||||
canary.Spec.CanaryAnalysis.StepWeight,
|
||||
canary.Spec.CanaryAnalysis.MaxWeight),
|
||||
canary.GetAnalysis().StepWeight,
|
||||
canary.GetAnalysis().MaxWeight),
|
||||
})
|
||||
} else if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
} else if len(canary.GetAnalysis().Match) > 0 {
|
||||
fields = append(fields, notifier.Field{
|
||||
Name: "Traffic routing",
|
||||
Value: "A/B Testing",
|
||||
})
|
||||
} else if canary.Spec.CanaryAnalysis.Iterations > 0 {
|
||||
} else if canary.GetAnalysis().Iterations > 0 {
|
||||
fields = append(fields, notifier.Field{
|
||||
Name: "Traffic routing",
|
||||
Value: "Blue/Green",
|
||||
|
||||
+36
-36
@@ -157,12 +157,12 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
|
||||
// set max weight default value to 100%
|
||||
maxWeight := 100
|
||||
if cd.Spec.CanaryAnalysis.MaxWeight > 0 {
|
||||
maxWeight = cd.Spec.CanaryAnalysis.MaxWeight
|
||||
if cd.GetAnalysis().MaxWeight > 0 {
|
||||
maxWeight = cd.GetAnalysis().MaxWeight
|
||||
}
|
||||
|
||||
// check primary status
|
||||
if !skipLivenessChecks && !cd.Spec.SkipAnalysis {
|
||||
if !skipLivenessChecks && !cd.SkipAnalysis() {
|
||||
if _, err := canaryController.IsPrimaryReady(cd); err != nil {
|
||||
c.recordEventWarningf(cd, "%v", err)
|
||||
return
|
||||
@@ -278,7 +278,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
|
||||
// check if the number of failed checks reached the threshold
|
||||
if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing &&
|
||||
(!retriable || cd.Status.FailedChecks >= cd.Spec.CanaryAnalysis.Threshold) {
|
||||
(!retriable || cd.Status.FailedChecks >= cd.GetAnalysisThreshold()) {
|
||||
if !retriable {
|
||||
c.recordEventWarningf(cd, "Rolling back %s.%s progress deadline exceeded %v",
|
||||
cd.Name, cd.Namespace, err)
|
||||
@@ -297,7 +297,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
// check if the canary success rate is above the threshold
|
||||
// skip check if no traffic is routed or mirrored to canary
|
||||
if canaryWeight == 0 && cd.Status.Iterations == 0 &&
|
||||
(cd.Spec.CanaryAnalysis.Mirror == false || mirrored == false) {
|
||||
(cd.GetAnalysis().Mirror == false || mirrored == false) {
|
||||
c.recordEventInfof(cd, "Starting canary analysis for %s.%s", cd.Spec.TargetRef.Name, cd.Namespace)
|
||||
|
||||
// run pre-rollout web hooks
|
||||
@@ -320,31 +320,31 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
|
||||
// use blue/green strategy for kubernetes provider
|
||||
if provider == "kubernetes" {
|
||||
if len(cd.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(cd.GetAnalysis().Match) > 0 {
|
||||
c.recordEventWarningf(cd, "A/B testing is not supported when using the kubernetes provider")
|
||||
cd.Spec.CanaryAnalysis.Match = nil
|
||||
cd.GetAnalysis().Match = nil
|
||||
}
|
||||
if cd.Spec.CanaryAnalysis.Iterations < 1 {
|
||||
if cd.GetAnalysis().Iterations < 1 {
|
||||
c.recordEventWarningf(cd, "Progressive traffic is not supported when using the kubernetes provider")
|
||||
c.recordEventWarningf(cd, "Setting canaryAnalysis.iterations: 10")
|
||||
cd.Spec.CanaryAnalysis.Iterations = 10
|
||||
cd.GetAnalysis().Iterations = 10
|
||||
}
|
||||
}
|
||||
|
||||
// strategy: A/B testing
|
||||
if len(cd.Spec.CanaryAnalysis.Match) > 0 && cd.Spec.CanaryAnalysis.Iterations > 0 {
|
||||
if len(cd.GetAnalysis().Match) > 0 && cd.GetAnalysis().Iterations > 0 {
|
||||
c.runAB(cd, canaryController, meshRouter, provider)
|
||||
return
|
||||
}
|
||||
|
||||
// strategy: Blue/Green
|
||||
if cd.Spec.CanaryAnalysis.Iterations > 0 {
|
||||
if cd.GetAnalysis().Iterations > 0 {
|
||||
c.runBlueGreen(cd, canaryController, meshRouter, provider, mirrored)
|
||||
return
|
||||
}
|
||||
|
||||
// strategy: Canary progressive traffic increase
|
||||
if cd.Spec.CanaryAnalysis.StepWeight > 0 {
|
||||
if cd.GetAnalysis().StepWeight > 0 {
|
||||
c.runCanary(cd, canaryController, meshRouter, provider, mirrored, canaryWeight, primaryWeight, maxWeight)
|
||||
}
|
||||
|
||||
@@ -358,25 +358,25 @@ func (c *Controller) runCanary(canary *flaggerv1.Canary, canaryController canary
|
||||
// If in "mirror" mode, do one step of mirroring before shifting traffic to canary.
|
||||
// When mirroring, all requests go to primary and canary, but only responses from
|
||||
// primary go back to the user.
|
||||
if canary.Spec.CanaryAnalysis.Mirror && canaryWeight == 0 {
|
||||
if canary.GetAnalysis().Mirror && canaryWeight == 0 {
|
||||
if mirrored == false {
|
||||
mirrored = true
|
||||
primaryWeight = 100
|
||||
canaryWeight = 0
|
||||
} else {
|
||||
mirrored = false
|
||||
primaryWeight = 100 - canary.Spec.CanaryAnalysis.StepWeight
|
||||
canaryWeight = canary.Spec.CanaryAnalysis.StepWeight
|
||||
primaryWeight = 100 - canary.GetAnalysis().StepWeight
|
||||
canaryWeight = canary.GetAnalysis().StepWeight
|
||||
}
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
|
||||
Infof("Running mirror step %d/%d/%t", primaryWeight, canaryWeight, mirrored)
|
||||
} else {
|
||||
|
||||
primaryWeight -= canary.Spec.CanaryAnalysis.StepWeight
|
||||
primaryWeight -= canary.GetAnalysis().StepWeight
|
||||
if primaryWeight < 0 {
|
||||
primaryWeight = 0
|
||||
}
|
||||
canaryWeight += canary.Spec.CanaryAnalysis.StepWeight
|
||||
canaryWeight += canary.GetAnalysis().StepWeight
|
||||
if canaryWeight > 100 {
|
||||
canaryWeight = 100
|
||||
}
|
||||
@@ -424,7 +424,7 @@ func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Con
|
||||
primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name)
|
||||
|
||||
// route traffic to canary and increment iterations
|
||||
if canary.Spec.CanaryAnalysis.Iterations > canary.Status.Iterations {
|
||||
if canary.GetAnalysis().Iterations > canary.Status.Iterations {
|
||||
if err := meshRouter.SetRoutes(canary, 0, 100, false); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
return
|
||||
@@ -436,7 +436,7 @@ func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Con
|
||||
return
|
||||
}
|
||||
c.recordEventInfof(canary, "Advance %s.%s canary iteration %v/%v",
|
||||
canary.Name, canary.Namespace, canary.Status.Iterations+1, canary.Spec.CanaryAnalysis.Iterations)
|
||||
canary.Name, canary.Namespace, canary.Status.Iterations+1, canary.GetAnalysis().Iterations)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Con
|
||||
}
|
||||
|
||||
// promote canary - max iterations reached
|
||||
if canary.Spec.CanaryAnalysis.Iterations == canary.Status.Iterations {
|
||||
if canary.GetAnalysis().Iterations == canary.Status.Iterations {
|
||||
c.recordEventInfof(canary, "Copying %s.%s template spec to %s.%s",
|
||||
canary.Spec.TargetRef.Name, canary.Namespace, primaryName, canary.Namespace)
|
||||
if err := canaryController.Promote(canary); err != nil {
|
||||
@@ -466,10 +466,10 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name)
|
||||
|
||||
// increment iterations
|
||||
if canary.Spec.CanaryAnalysis.Iterations > canary.Status.Iterations {
|
||||
if canary.GetAnalysis().Iterations > canary.Status.Iterations {
|
||||
// If in "mirror" mode, mirror requests during the entire B/G canary test
|
||||
if provider != "kubernetes" &&
|
||||
canary.Spec.CanaryAnalysis.Mirror == true && mirrored == false {
|
||||
canary.GetAnalysis().Mirror == true && mirrored == false {
|
||||
if err := meshRouter.SetRoutes(canary, 100, 0, true); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
}
|
||||
@@ -481,7 +481,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
return
|
||||
}
|
||||
c.recordEventInfof(canary, "Advance %s.%s canary iteration %v/%v",
|
||||
canary.Name, canary.Namespace, canary.Status.Iterations+1, canary.Spec.CanaryAnalysis.Iterations)
|
||||
canary.Name, canary.Namespace, canary.Status.Iterations+1, canary.GetAnalysis().Iterations)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -491,9 +491,9 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
}
|
||||
|
||||
// route all traffic to canary - max iterations reached
|
||||
if canary.Spec.CanaryAnalysis.Iterations == canary.Status.Iterations {
|
||||
if canary.GetAnalysis().Iterations == canary.Status.Iterations {
|
||||
if provider != "kubernetes" {
|
||||
if canary.Spec.CanaryAnalysis.Mirror {
|
||||
if canary.GetAnalysis().Mirror {
|
||||
c.recordEventInfof(canary, "Stop traffic mirroring and route all traffic to canary")
|
||||
} else {
|
||||
c.recordEventInfof(canary, "Routing all traffic to canary")
|
||||
@@ -514,7 +514,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
}
|
||||
|
||||
// promote canary - max iterations reached
|
||||
if canary.Spec.CanaryAnalysis.Iterations < canary.Status.Iterations {
|
||||
if canary.GetAnalysis().Iterations < canary.Status.Iterations {
|
||||
c.recordEventInfof(canary, "Copying %s.%s template spec to %s.%s",
|
||||
canary.Spec.TargetRef.Name, canary.Namespace, primaryName, canary.Namespace)
|
||||
if err := canaryController.Promote(canary); err != nil {
|
||||
@@ -532,7 +532,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
}
|
||||
|
||||
func (c *Controller) shouldSkipAnalysis(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface, primaryWeight int, canaryWeight int) bool {
|
||||
if !canary.Spec.SkipAnalysis {
|
||||
if !canary.SkipAnalysis() {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -656,7 +656,7 @@ func (c *Controller) hasCanaryRevisionChanged(canary *flaggerv1.Canary, canaryCo
|
||||
}
|
||||
|
||||
func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryController canary.Controller) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == flaggerv1.ConfirmRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
@@ -685,7 +685,7 @@ func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryCont
|
||||
}
|
||||
|
||||
func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == flaggerv1.ConfirmPromotionHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
@@ -702,7 +702,7 @@ func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary) bool {
|
||||
}
|
||||
|
||||
func (c *Controller) runPreRolloutHooks(canary *flaggerv1.Canary) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == flaggerv1.PreRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
@@ -718,7 +718,7 @@ func (c *Controller) runPreRolloutHooks(canary *flaggerv1.Canary) bool {
|
||||
}
|
||||
|
||||
func (c *Controller) runPostRolloutHooks(canary *flaggerv1.Canary, phase flaggerv1.CanaryPhase) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == flaggerv1.PostRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, phase, webhook)
|
||||
if err != nil {
|
||||
@@ -733,7 +733,7 @@ func (c *Controller) runPostRolloutHooks(canary *flaggerv1.Canary, phase flagger
|
||||
}
|
||||
|
||||
func (c *Controller) runRollbackHooks(canary *flaggerv1.Canary, phase flaggerv1.CanaryPhase) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == flaggerv1.RollbackHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, phase, webhook)
|
||||
if err != nil {
|
||||
@@ -749,7 +749,7 @@ func (c *Controller) runRollbackHooks(canary *flaggerv1.Canary, phase flaggerv1.
|
||||
|
||||
func (c *Controller) runAnalysis(canary *flaggerv1.Canary) bool {
|
||||
// run external checks
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
for _, webhook := range canary.GetAnalysis().Webhooks {
|
||||
if webhook.Type == "" || webhook.Type == flaggerv1.RolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
@@ -812,7 +812,7 @@ func (c *Controller) runBuiltinMetricChecks(canary *flaggerv1.Canary) bool {
|
||||
observer := observerFactory.Observer(metricsProvider)
|
||||
|
||||
// run metrics checks
|
||||
for _, metric := range canary.Spec.CanaryAnalysis.Metrics {
|
||||
for _, metric := range canary.GetAnalysis().Metrics {
|
||||
if metric.Interval == "" {
|
||||
metric.Interval = canary.GetMetricInterval()
|
||||
}
|
||||
@@ -914,7 +914,7 @@ func (c *Controller) runBuiltinMetricChecks(canary *flaggerv1.Canary) bool {
|
||||
}
|
||||
|
||||
func (c *Controller) runMetricChecks(canary *flaggerv1.Canary) bool {
|
||||
for _, metric := range canary.Spec.CanaryAnalysis.Metrics {
|
||||
for _, metric := range canary.GetAnalysis().Metrics {
|
||||
if metric.TemplateRef != nil {
|
||||
namespace := canary.Namespace
|
||||
if metric.TemplateRef.Namespace != "" {
|
||||
@@ -1007,7 +1007,7 @@ func toMetricModel(r *flaggerv1.Canary, interval string) flaggerv1.MetricTemplat
|
||||
}
|
||||
|
||||
func (c *Controller) rollback(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface) {
|
||||
if canary.Status.FailedChecks >= canary.Spec.CanaryAnalysis.Threshold {
|
||||
if canary.Status.FailedChecks >= canary.GetAnalysisThreshold() {
|
||||
c.recordEventWarningf(canary, "Rolling back %s.%s failed checks threshold reached %v",
|
||||
canary.Name, canary.Namespace, canary.Status.FailedChecks)
|
||||
c.alert(canary, fmt.Sprintf("Failed checks threshold reached %v", canary.Status.FailedChecks),
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
||||
clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func assertPhase(flaggerClient clientset.Interface, canary string, phase flaggerv1.CanaryPhase) error {
|
||||
c, err := flaggerClient.FlaggerV1beta1().Canaries("default").Get(canary, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Status.Phase != phase {
|
||||
return fmt.Errorf("Got canary state %v wanted %v", c.Status.Phase, phase)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func alwaysReady() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ func newDaemonSetTestCanary() *flaggerv1.Canary {
|
||||
Kind: "DaemonSet",
|
||||
}, Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
@@ -307,7 +307,7 @@ func newDaemonSetTestCanaryAB() *flaggerv1.Canary {
|
||||
Kind: "DaemonSet",
|
||||
}, Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
Iterations: 10,
|
||||
Match: []istiov1alpha3.HTTPMatchRequest{
|
||||
|
||||
@@ -257,7 +257,7 @@ func newDeploymentTestCanary() *flaggerv1.Canary {
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
}, Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
@@ -319,7 +319,7 @@ func newDeploymentTestCanaryAB() *flaggerv1.Canary {
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
}, Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
Iterations: 10,
|
||||
Match: []istiov1alpha3.HTTPMatchRequest{
|
||||
|
||||
@@ -144,6 +144,116 @@ func TestScheduler_DeploymentSkipAnalysis(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScheduler_DeploymentAnalysisPhases(t *testing.T) {
|
||||
cd := newDeploymentTestCanary()
|
||||
cd.Spec.Analysis = &flaggerv1.CanaryAnalysis{
|
||||
Interval: "1m",
|
||||
StepWeight: 100,
|
||||
}
|
||||
mocks := newDeploymentFixture(cd)
|
||||
|
||||
// init
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseInitialized); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// update
|
||||
dep2 := newDeploymentTestDeploymentV2()
|
||||
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// detect changes
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// progressing
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// promoting
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhasePromoting); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// finalising
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseFinalising); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// succeeded
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseSucceeded); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestScheduler_DeploymentBlueGreenAnalysisPhases(t *testing.T) {
|
||||
cd := newDeploymentTestCanary()
|
||||
cd.Spec.Analysis = &flaggerv1.CanaryAnalysis{
|
||||
Interval: "1m",
|
||||
Iterations: 1,
|
||||
}
|
||||
mocks := newDeploymentFixture(cd)
|
||||
|
||||
// init
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseInitialized); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// update
|
||||
dep2 := newDeploymentTestDeploymentV2()
|
||||
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// detect changes (progressing)
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// advance (progressing)
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// route traffic to primary (progressing)
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// promoting
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhasePromoting); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// finalising
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseFinalising); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// succeeded
|
||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||
if err := assertPhase(mocks.flaggerClient, "podinfo", flaggerv1.CanaryPhaseSucceeded); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestScheduler_DeploymentNewRevisionReset(t *testing.T) {
|
||||
mocks := newDeploymentFixture(nil)
|
||||
// init
|
||||
|
||||
@@ -142,7 +142,7 @@ func newTestServiceCanary() *flaggerv1.Canary {
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
},
|
||||
CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"k8s.io/utils/clock"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package loadtester
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type TaskRunner struct {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package loadtester
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
)
|
||||
|
||||
func TestTaskRunner_Start(t *testing.T) {
|
||||
|
||||
@@ -179,7 +179,6 @@ func ListenAndServe(port string, timeout time.Duration, logger *zap.SugaredLogge
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
canaryName := fmt.Sprintf("rollback.%s.%s", canary.Name, canary.Namespace)
|
||||
gate.close(canaryName)
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ package loadtester
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"go.uber.org/zap"
|
||||
"hash/fnv"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Modeling a loadtester task
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const TaskTypeNGrinder = "ngrinder"
|
||||
|
||||
@@ -3,10 +3,11 @@ package loadtester
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
)
|
||||
|
||||
func TestTaskNGrinder(t *testing.T) {
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const TaskTypeShell = "cmd"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
|
||||
@@ -203,7 +203,7 @@ func (ar *AppMeshRouter) reconcileVirtualService(canary *flaggerv1.Canary, name
|
||||
}
|
||||
|
||||
// A/B testing - header based routing
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 && canaryWeight == 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 && canaryWeight == 0 {
|
||||
routes = []appmeshv1.Route{
|
||||
{
|
||||
Name: fmt.Sprintf("%s-a", apexName),
|
||||
@@ -451,7 +451,7 @@ func makeRetryPolicy(canary *flaggerv1.Canary) *appmeshv1.HttpRetryPolicy {
|
||||
func (ar *AppMeshRouter) makeHeaders(canary *flaggerv1.Canary) []appmeshv1.HttpRouteHeader {
|
||||
headers := []appmeshv1.HttpRouteHeader{}
|
||||
|
||||
for _, m := range canary.Spec.CanaryAnalysis.Match {
|
||||
for _, m := range canary.GetAnalysis().Match {
|
||||
for key, value := range m.Headers {
|
||||
header := appmeshv1.HttpRouteHeader{
|
||||
Name: key,
|
||||
|
||||
@@ -64,7 +64,7 @@ func (cr *ContourRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||
},
|
||||
}
|
||||
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
newSpec = contourv1.HTTPProxySpec{
|
||||
Routes: []contourv1.Route{
|
||||
{
|
||||
@@ -277,7 +277,7 @@ func (cr *ContourRouter) SetRoutes(
|
||||
},
|
||||
}
|
||||
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
proxy.Spec = contourv1.HTTPProxySpec{
|
||||
Routes: []contourv1.Route{
|
||||
{
|
||||
@@ -364,8 +364,8 @@ func (cr *ContourRouter) makePrefix(canary *flaggerv1.Canary) string {
|
||||
func (cr *ContourRouter) makeConditions(canary *flaggerv1.Canary) []contourv1.Condition {
|
||||
list := []contourv1.Condition{}
|
||||
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
for _, match := range canary.Spec.CanaryAnalysis.Match {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
for _, match := range canary.GetAnalysis().Match {
|
||||
for s, stringMatch := range match.Headers {
|
||||
h := &contourv1.HeaderCondition{
|
||||
Name: s,
|
||||
|
||||
@@ -117,7 +117,7 @@ func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||
}
|
||||
|
||||
// A/B testing
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
for k := range canaryIngress.Annotations {
|
||||
if k == i.GetAnnotationWithPrefix("canary-by-cookie") || k == i.GetAnnotationWithPrefix("canary-by-header") {
|
||||
return 0, 100, false, nil
|
||||
@@ -158,11 +158,11 @@ func (i *IngressRouter) SetRoutes(
|
||||
iClone := canaryIngress.DeepCopy()
|
||||
|
||||
// A/B testing
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
cookie := ""
|
||||
header := ""
|
||||
headerValue := ""
|
||||
for _, m := range canary.Spec.CanaryAnalysis.Match {
|
||||
for _, m := range canary.GetAnalysis().Match {
|
||||
for k, v := range m.Headers {
|
||||
if k == "cookie" {
|
||||
cookie = v.Exact
|
||||
|
||||
+4
-4
@@ -152,8 +152,8 @@ func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
||||
},
|
||||
}
|
||||
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
canaryMatch := mergeMatchConditions(canary.Spec.CanaryAnalysis.Match, canary.Spec.Service.Match)
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
canaryMatch := mergeMatchConditions(canary.GetAnalysis().Match, canary.Spec.Service.Match)
|
||||
newSpec.Http = []istiov1alpha3.HTTPRoute{
|
||||
{
|
||||
Match: canaryMatch,
|
||||
@@ -323,9 +323,9 @@ func (ir *IstioRouter) SetRoutes(
|
||||
}
|
||||
|
||||
// fix routing (A/B testing)
|
||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||
if len(canary.GetAnalysis().Match) > 0 {
|
||||
// merge the common routes with the canary ones
|
||||
canaryMatch := mergeMatchConditions(canary.Spec.CanaryAnalysis.Match, canary.Spec.Service.Match)
|
||||
canaryMatch := mergeMatchConditions(canary.GetAnalysis().Match, canary.Spec.Service.Match)
|
||||
vsCopy.Spec.Http = []istiov1alpha3.HTTPRoute{
|
||||
{
|
||||
Match: canaryMatch,
|
||||
|
||||
@@ -112,7 +112,7 @@ func newTestCanary() *flaggerv1.Canary {
|
||||
"public-gateway.istio",
|
||||
"mesh",
|
||||
},
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
@@ -158,7 +158,7 @@ func newTestCanaryAppMesh() *flaggerv1.Canary {
|
||||
PerTryTimeout: "gateway-error",
|
||||
RetryOn: "5s",
|
||||
},
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
@@ -203,7 +203,7 @@ func newTestSMICanary() *flaggerv1.Canary {
|
||||
},
|
||||
PortDiscovery: true,
|
||||
},
|
||||
CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
@@ -227,7 +227,7 @@ func newTestSMICanary() *flaggerv1.Canary {
|
||||
|
||||
func newTestMirror() *flaggerv1.Canary {
|
||||
cd := newTestCanary()
|
||||
cd.Spec.CanaryAnalysis.Mirror = true
|
||||
cd.GetAnalysis().Mirror = true
|
||||
return cd
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ func newTestABTest() *flaggerv1.Canary {
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
MeshName: "global",
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
Iterations: 2,
|
||||
Match: []istiov1alpha3.HTTPMatchRequest{
|
||||
@@ -397,7 +397,7 @@ func newTestCanaryIngress() *flaggerv1.Canary {
|
||||
},
|
||||
Service: flaggerv1.CanaryService{
|
||||
Port: 9898,
|
||||
}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
|
||||
}, CanaryAnalysis: &flaggerv1.CanaryAnalysis{
|
||||
Threshold: 10,
|
||||
StepWeight: 10,
|
||||
MaxWeight: 50,
|
||||
|
||||
@@ -34,7 +34,7 @@ spec:
|
||||
EOF
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -49,7 +49,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 50
|
||||
@@ -145,7 +145,7 @@ echo '✔ Canary promotion test passed'
|
||||
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -160,7 +160,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 50
|
||||
@@ -218,7 +218,7 @@ done
|
||||
echo '✔ Canary rollback test passed'
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -233,7 +233,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 10s
|
||||
threshold: 5
|
||||
iterations: 5
|
||||
|
||||
@@ -37,7 +37,7 @@ spec:
|
||||
EOF
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -52,7 +52,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 50
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
set -o errexit
|
||||
|
||||
ISTIO_VER="1.4.4"
|
||||
ISTIO_VER="1.4.5"
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
|
||||
echo ">>> Installing Istio ${ISTIO_VER}"
|
||||
|
||||
@@ -18,7 +18,7 @@ echo '>>> Initialising canary'
|
||||
kubectl apply -f ${REPO_ROOT}/test/e2e-daemonset.yaml
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -35,7 +35,7 @@ spec:
|
||||
targetPort: 9898
|
||||
name: podinfo-svc
|
||||
portDiscovery: true
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
iterations: 5
|
||||
|
||||
@@ -18,7 +18,7 @@ echo '>>> Initialising canary'
|
||||
kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -35,7 +35,7 @@ spec:
|
||||
targetPort: 9898
|
||||
name: podinfo-svc
|
||||
portDiscovery: true
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
iterations: 5
|
||||
|
||||
@@ -34,7 +34,7 @@ spec:
|
||||
EOS
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -48,7 +48,7 @@ spec:
|
||||
progressDeadlineSeconds: 60
|
||||
service:
|
||||
port: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
iterations: 5
|
||||
|
||||
@@ -43,7 +43,7 @@ spec:
|
||||
EOF
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -58,7 +58,7 @@ spec:
|
||||
port: 80
|
||||
targetPort: http
|
||||
portDiscovery: true
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 50
|
||||
@@ -154,7 +154,7 @@ done
|
||||
echo '✔ Canary promotion test passed'
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -168,7 +168,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 3
|
||||
maxWeight: 50
|
||||
|
||||
@@ -19,7 +19,7 @@ kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml
|
||||
kubectl apply -f ${REPO_ROOT}/test/e2e-ingress.yaml
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -37,7 +37,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: http
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 30
|
||||
@@ -156,7 +156,7 @@ done
|
||||
echo '✔ Canary promotion test passed'
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -174,7 +174,7 @@ spec:
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 10s
|
||||
threshold: 5
|
||||
iterations: 5
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
set -o errexit
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
NGINX_VERSION=1.26.0
|
||||
NGINX_VERSION=1.33.0
|
||||
|
||||
echo '>>> Installing NGINX Ingress'
|
||||
kubectl create ns ingress-nginx
|
||||
|
||||
+6
-6
@@ -19,7 +19,7 @@ echo '>>> Initialising canary'
|
||||
kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -39,7 +39,7 @@ spec:
|
||||
x-envoy-upstream-rq-timeout-ms: "15000"
|
||||
x-envoy-max-retries: "10"
|
||||
x-envoy-retry-on: "gateway-error,connect-failure,refused-stream"
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 15
|
||||
maxWeight: 30
|
||||
@@ -145,7 +145,7 @@ if [[ "$1" = "canary" ]]; then
|
||||
fi
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -161,7 +161,7 @@ spec:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
portName: http-podinfo
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 10s
|
||||
threshold: 5
|
||||
iterations: 5
|
||||
@@ -234,7 +234,7 @@ done
|
||||
echo '✔ B/G promotion test passed'
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -250,7 +250,7 @@ spec:
|
||||
port: 80
|
||||
portName: http-podinfo
|
||||
targetPort: http
|
||||
canaryAnalysis:
|
||||
analysis:
|
||||
interval: 10s
|
||||
threshold: 5
|
||||
iterations: 5
|
||||
|
||||
Reference in New Issue
Block a user