mirror of https://github.com/fluxcd/flagger.git
Merge pull request #311 from andrewjjenkins/mirror
Add traffic mirroring for Istio service mesh
This commit is contained in:
commit
9a9baadf0e
|
@ -41,6 +41,10 @@ spec:
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.interval
|
JSONPath: .spec.canaryAnalysis.interval
|
||||||
priority: 1
|
priority: 1
|
||||||
|
- name: Mirror
|
||||||
|
type: boolean
|
||||||
|
JSONPath: .spec.canaryAnalysis.mirror
|
||||||
|
priority: 1
|
||||||
- name: StepWeight
|
- name: StepWeight
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||||
|
@ -183,6 +187,9 @@ spec:
|
||||||
stepWeight:
|
stepWeight:
|
||||||
description: Canary incremental traffic percentage step
|
description: Canary incremental traffic percentage step
|
||||||
type: number
|
type: number
|
||||||
|
mirror:
|
||||||
|
description: Mirror traffic to canary before shifting
|
||||||
|
type: boolean
|
||||||
match:
|
match:
|
||||||
description: A/B testing match conditions
|
description: A/B testing match conditions
|
||||||
anyOf:
|
anyOf:
|
||||||
|
|
|
@ -42,6 +42,10 @@ spec:
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.interval
|
JSONPath: .spec.canaryAnalysis.interval
|
||||||
priority: 1
|
priority: 1
|
||||||
|
- name: Mirror
|
||||||
|
type: boolean
|
||||||
|
JSONPath: .spec.canaryAnalysis.mirror
|
||||||
|
priority: 1
|
||||||
- name: StepWeight
|
- name: StepWeight
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||||
|
@ -184,6 +188,9 @@ spec:
|
||||||
stepWeight:
|
stepWeight:
|
||||||
description: Canary incremental traffic percentage step
|
description: Canary incremental traffic percentage step
|
||||||
type: number
|
type: number
|
||||||
|
mirror:
|
||||||
|
description: Mirror traffic to canary before shifting
|
||||||
|
type: boolean
|
||||||
match:
|
match:
|
||||||
description: A/B testing match conditions
|
description: A/B testing match conditions
|
||||||
anyOf:
|
anyOf:
|
||||||
|
|
|
@ -102,6 +102,50 @@ The above configuration will run an analysis for five minutes.
|
||||||
Flagger starts the load test for the canary service (green version) and checks the Prometheus metrics every 30 seconds.
|
Flagger starts the load test for the canary service (green version) and checks the Prometheus metrics every 30 seconds.
|
||||||
If the analysis result is positive, Flagger will promote the canary (green version) to primary (blue version).
|
If the analysis result is positive, Flagger will promote the canary (green version) to primary (blue version).
|
||||||
|
|
||||||
|
**When can I use traffic mirroring?**
|
||||||
|
|
||||||
|
Traffic Mirroring is a pre-stage in a Canary (progressive traffic shifting) or
|
||||||
|
Blue/Green deployment strategy. Traffic mirroring will copy each incoming
|
||||||
|
request, sending one request to the primary and one to the canary service. The
|
||||||
|
response from the primary is sent back to the user. The response from the canary
|
||||||
|
is discarded. Metrics are collected on both requests so that the deployment will
|
||||||
|
only proceed if the canary metrics are healthy.
|
||||||
|
|
||||||
|
Mirroring is supported by Istio only.
|
||||||
|
|
||||||
|
In Istio, mirrored requests have `-shadow` appended to the `Host` (HTTP) or
|
||||||
|
`Authority` (HTTP/2) header; for example requests to `podinfo.test` that are
|
||||||
|
mirrored will be reported in telemetry with a destination host
|
||||||
|
`podinfo.test-shadow`.
|
||||||
|
|
||||||
|
Mirroring must only be used for requests that are **idempotent** or capable of
|
||||||
|
being processed twice (once by the primary and once by the canary). Reads are
|
||||||
|
idempotent. Before using mirroring on requests that may be writes, you should
|
||||||
|
consider what will happen if a write is duplicated and handled by the primary
|
||||||
|
and canary.
|
||||||
|
|
||||||
|
To use mirroring, set `spec.canaryAnalysis.mirror` to `true`. Example for
|
||||||
|
traffic shifting:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: flagger.app/v1alpha3
|
||||||
|
kind: Canary
|
||||||
|
spec:
|
||||||
|
provider: istio
|
||||||
|
canaryAnalysis:
|
||||||
|
interval: 30s
|
||||||
|
mirror: true
|
||||||
|
stepWeight: 20
|
||||||
|
maxWeight: 50
|
||||||
|
metrics:
|
||||||
|
- interval: 29s
|
||||||
|
name: request-success-rate
|
||||||
|
threshold: 99
|
||||||
|
- interval: 29s
|
||||||
|
name: request-duration
|
||||||
|
threshold: 500
|
||||||
|
```
|
||||||
|
|
||||||
### Kubernetes services
|
### Kubernetes services
|
||||||
|
|
||||||
**How is an application exposed inside the cluster?**
|
**How is an application exposed inside the cluster?**
|
||||||
|
|
|
@ -41,6 +41,10 @@ spec:
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.interval
|
JSONPath: .spec.canaryAnalysis.interval
|
||||||
priority: 1
|
priority: 1
|
||||||
|
- name: Mirror
|
||||||
|
type: boolean
|
||||||
|
JSONPath: .spec.canaryAnalysis.mirror
|
||||||
|
priority: 1
|
||||||
- name: StepWeight
|
- name: StepWeight
|
||||||
type: string
|
type: string
|
||||||
JSONPath: .spec.canaryAnalysis.stepWeight
|
JSONPath: .spec.canaryAnalysis.stepWeight
|
||||||
|
@ -183,6 +187,9 @@ spec:
|
||||||
stepWeight:
|
stepWeight:
|
||||||
description: Canary incremental traffic percentage step
|
description: Canary incremental traffic percentage step
|
||||||
type: number
|
type: number
|
||||||
|
mirror:
|
||||||
|
description: Mirror traffic to canary before shifting
|
||||||
|
type: boolean
|
||||||
match:
|
match:
|
||||||
description: A/B testing match conditions
|
description: A/B testing match conditions
|
||||||
anyOf:
|
anyOf:
|
||||||
|
|
|
@ -111,6 +111,7 @@ type CanaryAnalysis struct {
|
||||||
Interval string `json:"interval"`
|
Interval string `json:"interval"`
|
||||||
Threshold int `json:"threshold"`
|
Threshold int `json:"threshold"`
|
||||||
MaxWeight int `json:"maxWeight"`
|
MaxWeight int `json:"maxWeight"`
|
||||||
|
Mirror bool `json:"mirror,omitempty"`
|
||||||
StepWeight int `json:"stepWeight"`
|
StepWeight int `json:"stepWeight"`
|
||||||
Metrics []CanaryMetric `json:"metrics"`
|
Metrics []CanaryMetric `json:"metrics"`
|
||||||
Webhooks []CanaryWebhook `json:"webhooks,omitempty"`
|
Webhooks []CanaryWebhook `json:"webhooks,omitempty"`
|
||||||
|
|
|
@ -42,11 +42,9 @@ type Mocks struct {
|
||||||
router router.Interface
|
router router.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupMocks(abtest bool) Mocks {
|
func SetupMocks(c *v1alpha3.Canary) Mocks {
|
||||||
// init canary
|
if c == nil {
|
||||||
c := newTestCanary()
|
c = newTestCanary()
|
||||||
if abtest {
|
|
||||||
c = newTestCanaryAB()
|
|
||||||
}
|
}
|
||||||
flaggerClient := fakeFlagger.NewSimpleClientset(c)
|
flaggerClient := fakeFlagger.NewSimpleClientset(c)
|
||||||
|
|
||||||
|
@ -269,6 +267,12 @@ func newTestCanary() *v1alpha3.Canary {
|
||||||
return cd
|
return cd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestCanaryMirror() *v1alpha3.Canary {
|
||||||
|
cd := newTestCanary()
|
||||||
|
cd.Spec.CanaryAnalysis.Mirror = true
|
||||||
|
return cd
|
||||||
|
}
|
||||||
|
|
||||||
func newTestCanaryAB() *v1alpha3.Canary {
|
func newTestCanaryAB() *v1alpha3.Canary {
|
||||||
cd := &v1alpha3.Canary{
|
cd := &v1alpha3.Canary{
|
||||||
TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()},
|
TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()},
|
||||||
|
|
|
@ -155,7 +155,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
|
|
||||||
// check if virtual service exists
|
// check if virtual service exists
|
||||||
// and if it contains weighted destination routes to the primary and canary services
|
// and if it contains weighted destination routes to the primary and canary services
|
||||||
primaryWeight, canaryWeight, err := meshRouter.GetRoutes(cd)
|
primaryWeight, canaryWeight, mirrored, err := meshRouter.GetRoutes(cd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
|
@ -176,7 +176,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
// route all traffic back to primary
|
// route all traffic back to primary
|
||||||
primaryWeight = 100
|
primaryWeight = 100
|
||||||
canaryWeight = 0
|
canaryWeight = 0
|
||||||
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil {
|
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
if cd.Status.Phase == flaggerv1.CanaryPhasePromoting {
|
if cd.Status.Phase == flaggerv1.CanaryPhasePromoting {
|
||||||
if provider != "kubernetes" {
|
if provider != "kubernetes" {
|
||||||
c.recordEventInfof(cd, "Routing all traffic to primary")
|
c.recordEventInfof(cd, "Routing all traffic to primary")
|
||||||
if err := meshRouter.SetRoutes(cd, 100, 0); err != nil {
|
if err := meshRouter.SetRoutes(cd, 100, 0, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
// route all traffic back to primary
|
// route all traffic back to primary
|
||||||
primaryWeight = 100
|
primaryWeight = 100
|
||||||
canaryWeight = 0
|
canaryWeight = 0
|
||||||
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil {
|
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -302,8 +302,9 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the canary success rate is above the threshold
|
// check if the canary success rate is above the threshold
|
||||||
// skip check if no traffic is routed to canary
|
// skip check if no traffic is routed or mirrored to canary
|
||||||
if canaryWeight == 0 && cd.Status.Iterations == 0 {
|
if canaryWeight == 0 && cd.Status.Iterations == 0 &&
|
||||||
|
(cd.Spec.CanaryAnalysis.Mirror == false || mirrored == false) {
|
||||||
c.recordEventInfof(cd, "Starting canary analysis for %s.%s", cd.Spec.TargetRef.Name, cd.Namespace)
|
c.recordEventInfof(cd, "Starting canary analysis for %s.%s", cd.Spec.TargetRef.Name, cd.Namespace)
|
||||||
|
|
||||||
// run pre-rollout web hooks
|
// run pre-rollout web hooks
|
||||||
|
@ -328,7 +329,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
if len(cd.Spec.CanaryAnalysis.Match) > 0 && cd.Spec.CanaryAnalysis.Iterations > 0 {
|
if len(cd.Spec.CanaryAnalysis.Match) > 0 && cd.Spec.CanaryAnalysis.Iterations > 0 {
|
||||||
// route traffic to canary and increment iterations
|
// route traffic to canary and increment iterations
|
||||||
if cd.Spec.CanaryAnalysis.Iterations > cd.Status.Iterations {
|
if cd.Spec.CanaryAnalysis.Iterations > cd.Status.Iterations {
|
||||||
if err := meshRouter.SetRoutes(cd, 0, 100); err != nil {
|
if err := meshRouter.SetRoutes(cd, 0, 100, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -372,6 +373,15 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
if cd.Spec.CanaryAnalysis.Iterations > 0 {
|
if cd.Spec.CanaryAnalysis.Iterations > 0 {
|
||||||
// increment iterations
|
// increment iterations
|
||||||
if cd.Spec.CanaryAnalysis.Iterations > cd.Status.Iterations {
|
if cd.Spec.CanaryAnalysis.Iterations > cd.Status.Iterations {
|
||||||
|
// If in "mirror" mode, mirror requests during the entire B/G canary test
|
||||||
|
if provider != "kubernetes" &&
|
||||||
|
cd.Spec.CanaryAnalysis.Mirror == true && mirrored == false {
|
||||||
|
if err := meshRouter.SetRoutes(cd, 100, 0, true); err != nil {
|
||||||
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
|
}
|
||||||
|
c.logger.With("canary", fmt.Sprintf("%s.%s", name, namespace)).
|
||||||
|
Infof("Enabling mirroring for Blue/Green")
|
||||||
|
}
|
||||||
if err := c.deployer.SetStatusIterations(cd, cd.Status.Iterations+1); err != nil {
|
if err := c.deployer.SetStatusIterations(cd, cd.Status.Iterations+1); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
|
@ -390,7 +400,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
if cd.Spec.CanaryAnalysis.Iterations == cd.Status.Iterations {
|
if cd.Spec.CanaryAnalysis.Iterations == cd.Status.Iterations {
|
||||||
if provider != "kubernetes" {
|
if provider != "kubernetes" {
|
||||||
c.recordEventInfof(cd, "Routing all traffic to canary")
|
c.recordEventInfof(cd, "Routing all traffic to canary")
|
||||||
if err := meshRouter.SetRoutes(cd, 0, 100); err != nil {
|
if err := meshRouter.SetRoutes(cd, 0, 100, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -429,16 +439,34 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||||
if cd.Spec.CanaryAnalysis.StepWeight > 0 {
|
if cd.Spec.CanaryAnalysis.StepWeight > 0 {
|
||||||
// increase traffic weight
|
// increase traffic weight
|
||||||
if canaryWeight < maxWeight {
|
if canaryWeight < maxWeight {
|
||||||
primaryWeight -= cd.Spec.CanaryAnalysis.StepWeight
|
// If in "mirror" mode, do one step of mirroring before shifting traffic to canary.
|
||||||
if primaryWeight < 0 {
|
// When mirroring, all requests go to primary and canary, but only responses from
|
||||||
primaryWeight = 0
|
// primary go back to the user.
|
||||||
}
|
if cd.Spec.CanaryAnalysis.Mirror && canaryWeight == 0 {
|
||||||
canaryWeight += cd.Spec.CanaryAnalysis.StepWeight
|
if mirrored == false {
|
||||||
if canaryWeight > 100 {
|
mirrored = true
|
||||||
canaryWeight = 100
|
primaryWeight = 100
|
||||||
|
canaryWeight = 0
|
||||||
|
} else {
|
||||||
|
mirrored = false
|
||||||
|
primaryWeight = 100 - cd.Spec.CanaryAnalysis.StepWeight
|
||||||
|
canaryWeight = cd.Spec.CanaryAnalysis.StepWeight
|
||||||
|
}
|
||||||
|
c.logger.With("canary", fmt.Sprintf("%s.%s", name, namespace)).
|
||||||
|
Infof("Running mirror step %d/%d/%t", primaryWeight, canaryWeight, mirrored)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
primaryWeight -= cd.Spec.CanaryAnalysis.StepWeight
|
||||||
|
if primaryWeight < 0 {
|
||||||
|
primaryWeight = 0
|
||||||
|
}
|
||||||
|
canaryWeight += cd.Spec.CanaryAnalysis.StepWeight
|
||||||
|
if canaryWeight > 100 {
|
||||||
|
canaryWeight = 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil {
|
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight, mirrored); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -489,7 +517,7 @@ func (c *Controller) shouldSkipAnalysis(cd *flaggerv1.Canary, meshRouter router.
|
||||||
// route all traffic to primary
|
// route all traffic to primary
|
||||||
primaryWeight = 100
|
primaryWeight = 100
|
||||||
canaryWeight = 0
|
canaryWeight = 0
|
||||||
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil {
|
if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight, false); err != nil {
|
||||||
c.recordEventWarningf(cd, "%v", err)
|
c.recordEventWarningf(cd, "%v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestScheduler_Init(t *testing.T) {
|
func TestScheduler_Init(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
_, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
|
_, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
|
||||||
|
@ -18,7 +18,7 @@ func TestScheduler_Init(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_NewRevision(t *testing.T) {
|
func TestScheduler_NewRevision(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
// update
|
// update
|
||||||
|
@ -42,7 +42,7 @@ func TestScheduler_NewRevision(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_Rollback(t *testing.T) {
|
func TestScheduler_Rollback(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
// init
|
// init
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func TestScheduler_Rollback(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_SkipAnalysis(t *testing.T) {
|
func TestScheduler_SkipAnalysis(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
// init
|
// init
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ func TestScheduler_SkipAnalysis(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_NewRevisionReset(t *testing.T) {
|
func TestScheduler_NewRevisionReset(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
// init
|
// init
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func TestScheduler_NewRevisionReset(t *testing.T) {
|
||||||
// advance
|
// advance
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
|
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,10 @@ func TestScheduler_NewRevisionReset(t *testing.T) {
|
||||||
t.Errorf("Got canary route %v wanted %v", canaryWeight, 10)
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirrored != false {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
|
||||||
|
}
|
||||||
|
|
||||||
// second update
|
// second update
|
||||||
dep2.Spec.Template.Spec.ServiceAccountName = "test"
|
dep2.Spec.Template.Spec.ServiceAccountName = "test"
|
||||||
_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
|
_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
|
||||||
|
@ -146,7 +150,7 @@ func TestScheduler_NewRevisionReset(t *testing.T) {
|
||||||
// detect changes
|
// detect changes
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary)
|
primaryWeight, canaryWeight, mirrored, err = mocks.router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -158,10 +162,14 @@ func TestScheduler_NewRevisionReset(t *testing.T) {
|
||||||
if canaryWeight != 0 {
|
if canaryWeight != 0 {
|
||||||
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirrored != false {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_Promotion(t *testing.T) {
|
func TestScheduler_Promotion(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
|
|
||||||
// init
|
// init
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
@ -201,14 +209,14 @@ func TestScheduler_Promotion(t *testing.T) {
|
||||||
// detect configs changes
|
// detect configs changes
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
|
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryWeight = 60
|
primaryWeight = 60
|
||||||
canaryWeight = 40
|
canaryWeight = 40
|
||||||
err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight)
|
err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight, mirrored)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -242,7 +250,7 @@ func TestScheduler_Promotion(t *testing.T) {
|
||||||
// finalise
|
// finalise
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary)
|
primaryWeight, canaryWeight, mirrored, err = mocks.router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -255,6 +263,10 @@ func TestScheduler_Promotion(t *testing.T) {
|
||||||
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirrored != false {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
|
||||||
|
}
|
||||||
|
|
||||||
primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
|
primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
|
@ -307,8 +319,66 @@ func TestScheduler_Promotion(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestScheduler_Mirroring(t *testing.T) {
|
||||||
|
mocks := SetupMocks(newTestCanaryMirror())
|
||||||
|
// init
|
||||||
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
// update
|
||||||
|
dep2 := newTestDeploymentV2()
|
||||||
|
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect pod spec changes
|
||||||
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
// advance
|
||||||
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
// check if traffic is mirrored to canary
|
||||||
|
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if primaryWeight != 100 {
|
||||||
|
t.Errorf("Got primary route %v wanted %v", primaryWeight, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
if canaryWeight != 0 {
|
||||||
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mirrored != true {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance
|
||||||
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
// check if traffic is mirrored to canary
|
||||||
|
primaryWeight, canaryWeight, mirrored, err = mocks.router.GetRoutes(mocks.canary)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if primaryWeight != 90 {
|
||||||
|
t.Errorf("Got primary route %v wanted %v", primaryWeight, 90)
|
||||||
|
}
|
||||||
|
|
||||||
|
if canaryWeight != 10 {
|
||||||
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mirrored != false {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestScheduler_ABTesting(t *testing.T) {
|
func TestScheduler_ABTesting(t *testing.T) {
|
||||||
mocks := SetupMocks(true)
|
mocks := SetupMocks(newTestCanaryAB())
|
||||||
// init
|
// init
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
|
@ -326,7 +396,7 @@ func TestScheduler_ABTesting(t *testing.T) {
|
||||||
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
mocks.ctrl.advanceCanary("podinfo", "default", true)
|
||||||
|
|
||||||
// check if traffic is routed to canary
|
// check if traffic is routed to canary
|
||||||
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
|
primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -339,6 +409,10 @@ func TestScheduler_ABTesting(t *testing.T) {
|
||||||
t.Errorf("Got canary route %v wanted %v", canaryWeight, 100)
|
t.Errorf("Got canary route %v wanted %v", canaryWeight, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirrored != false {
|
||||||
|
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
|
||||||
|
}
|
||||||
|
|
||||||
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
|
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
|
@ -392,7 +466,7 @@ func TestScheduler_ABTesting(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScheduler_PortDiscovery(t *testing.T) {
|
func TestScheduler_PortDiscovery(t *testing.T) {
|
||||||
mocks := SetupMocks(false)
|
mocks := SetupMocks(nil)
|
||||||
|
|
||||||
// enable port discovery
|
// enable port discovery
|
||||||
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
|
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
|
||||||
|
|
|
@ -259,6 +259,7 @@ func (ar *AppMeshRouter) reconcileVirtualService(canary *flaggerv1.Canary, name
|
||||||
func (ar *AppMeshRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (ar *AppMeshRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
@ -293,6 +294,8 @@ func (ar *AppMeshRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
vsName, targetName, targetName)
|
vsName, targetName, targetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mirrored = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +304,7 @@ func (ar *AppMeshRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
vsName := fmt.Sprintf("%s.%s", targetName, canary.Namespace)
|
vsName := fmt.Sprintf("%s.%s", targetName, canary.Namespace)
|
||||||
|
|
|
@ -161,12 +161,12 @@ func TestAppmeshRouter_GetSetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.appmeshCanary, 60, 40)
|
err = router.SetRoutes(mocks.appmeshCanary, 60, 40, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.appmeshCanary)
|
p, c, m, err := router.GetRoutes(mocks.appmeshCanary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -178,4 +178,8 @@ func TestAppmeshRouter_GetSetRoutes(t *testing.T) {
|
||||||
if c != 40 {
|
if c != 40 {
|
||||||
t.Errorf("Got canary weight %v wanted %v", c, 40)
|
t.Errorf("Got canary weight %v wanted %v", c, 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m != false {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,11 @@ func NewGlooRouterWithClient(ctx context.Context, routingRuleClient gloov1.Upstr
|
||||||
// Reconcile creates or updates the Istio virtual service
|
// Reconcile creates or updates the Istio virtual service
|
||||||
func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
|
func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
// do we have routes already?
|
// do we have routes already?
|
||||||
if _, _, err := gr.GetRoutes(canary); err == nil {
|
if _, _, _, err := gr.GetRoutes(canary); err == nil {
|
||||||
// we have routes, no need to do anything else
|
// we have routes, no need to do anything else
|
||||||
return nil
|
return nil
|
||||||
} else if solokiterror.IsNotExist(err) {
|
} else if solokiterror.IsNotExist(err) {
|
||||||
return gr.SetRoutes(canary, 100, 0)
|
return gr.SetRoutes(canary, 100, 0, false)
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
@ -101,6 +102,8 @@ func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
targetName, canary.Namespace, targetName, targetName)
|
targetName, canary.Namespace, targetName, targetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mirrored = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +112,7 @@ func (gr *GlooRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
|
||||||
|
|
|
@ -68,15 +68,16 @@ func TestGlooRouter_SetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p = 50
|
p = 50
|
||||||
c = 50
|
c = 50
|
||||||
|
m = false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.canary, p, c)
|
err = router.SetRoutes(mocks.canary, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -127,7 +128,7 @@ func TestGlooRouter_GetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -139,4 +140,8 @@ func TestGlooRouter_GetRoutes(t *testing.T) {
|
||||||
if c != 0 {
|
if c != 0 {
|
||||||
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m != false {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,19 +106,20 @@ func (i *IngressRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name)
|
canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name)
|
||||||
canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{})
|
canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// A/B testing
|
// A/B testing
|
||||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||||
for k := range canaryIngress.Annotations {
|
for k := range canaryIngress.Annotations {
|
||||||
if k == i.GetAnnotationWithPrefix("canary-by-cookie") || k == i.GetAnnotationWithPrefix("canary-by-header") {
|
if k == i.GetAnnotationWithPrefix("canary-by-cookie") || k == i.GetAnnotationWithPrefix("canary-by-header") {
|
||||||
return 0, 100, nil
|
return 0, 100, false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +129,7 @@ func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
if k == i.GetAnnotationWithPrefix("canary-weight") {
|
if k == i.GetAnnotationWithPrefix("canary-weight") {
|
||||||
val, err := strconv.Atoi(v)
|
val, err := strconv.Atoi(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
canaryWeight = val
|
canaryWeight = val
|
||||||
|
@ -137,6 +138,7 @@ func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryWeight = 100 - canaryWeight
|
primaryWeight = 100 - canaryWeight
|
||||||
|
mirrored = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +146,7 @@ func (i *IngressRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name)
|
canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name)
|
||||||
canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{})
|
canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{})
|
||||||
|
|
|
@ -56,15 +56,16 @@ func TestIngressRouter_GetSetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.ingressCanary)
|
p, c, m, err := router.GetRoutes(mocks.ingressCanary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p = 50
|
p = 50
|
||||||
c = 50
|
c = 50
|
||||||
|
m = false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.ingressCanary, p, c)
|
err = router.SetRoutes(mocks.ingressCanary, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -93,8 +94,9 @@ func TestIngressRouter_GetSetRoutes(t *testing.T) {
|
||||||
|
|
||||||
p = 100
|
p = 100
|
||||||
c = 0
|
c = 0
|
||||||
|
m = false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.ingressCanary, p, c)
|
err = router.SetRoutes(mocks.ingressCanary, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,8 +101,6 @@ func (ir *IstioRouter) reconcileDestinationRule(canary *flaggerv1.Canary, name s
|
||||||
|
|
||||||
func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
primaryName := fmt.Sprintf("%s-primary", targetName)
|
|
||||||
canaryName := fmt.Sprintf("%s-canary", targetName)
|
|
||||||
|
|
||||||
// set hosts and add the ClusterIP service host if it doesn't exists
|
// set hosts and add the ClusterIP service host if it doesn't exists
|
||||||
hosts := canary.Spec.Service.Hosts
|
hosts := canary.Spec.Service.Hosts
|
||||||
|
@ -133,6 +131,8 @@ func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create destinations with primary weight 100% and canary weight 0%
|
// create destinations with primary weight 100% and canary weight 0%
|
||||||
|
primaryName := fmt.Sprintf("%s-primary", targetName)
|
||||||
|
canaryName := fmt.Sprintf("%s-canary", targetName)
|
||||||
canaryRoute := []istiov1alpha3.DestinationWeight{
|
canaryRoute := []istiov1alpha3.DestinationWeight{
|
||||||
makeDestination(canary, primaryName, 100),
|
makeDestination(canary, primaryName, 100),
|
||||||
makeDestination(canary, canaryName, 0),
|
makeDestination(canary, canaryName, 0),
|
||||||
|
@ -210,9 +210,14 @@ func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
||||||
return fmt.Errorf("VirtualService %s.%s query error %v", targetName, canary.Namespace, err)
|
return fmt.Errorf("VirtualService %s.%s query error %v", targetName, canary.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update service but keep the original destination weights
|
// update service but keep the original destination weights and mirror
|
||||||
if virtualService != nil {
|
if virtualService != nil {
|
||||||
if diff := cmp.Diff(newSpec, virtualService.Spec, cmpopts.IgnoreFields(istiov1alpha3.DestinationWeight{}, "Weight")); diff != "" {
|
if diff := cmp.Diff(
|
||||||
|
newSpec,
|
||||||
|
virtualService.Spec,
|
||||||
|
cmpopts.IgnoreFields(istiov1alpha3.DestinationWeight{}, "Weight"),
|
||||||
|
cmpopts.IgnoreFields(istiov1alpha3.HTTPRoute{}, "Mirror"),
|
||||||
|
); diff != "" {
|
||||||
vtClone := virtualService.DeepCopy()
|
vtClone := virtualService.DeepCopy()
|
||||||
vtClone.Spec = newSpec
|
vtClone.Spec = newSpec
|
||||||
|
|
||||||
|
@ -232,6 +237,7 @@ func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error {
|
||||||
func (ir *IstioRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (ir *IstioRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
@ -264,6 +270,9 @@ func (ir *IstioRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
canaryWeight = route.Weight
|
canaryWeight = route.Weight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if httpRoute.Mirror != nil && httpRoute.Mirror.Host != "" {
|
||||||
|
mirrored = true
|
||||||
|
}
|
||||||
|
|
||||||
if primaryWeight == 0 && canaryWeight == 0 {
|
if primaryWeight == 0 && canaryWeight == 0 {
|
||||||
err = fmt.Errorf("VirtualService %s.%s does not contain routes for %s-primary and %s-canary",
|
err = fmt.Errorf("VirtualService %s.%s does not contain routes for %s-primary and %s-canary",
|
||||||
|
@ -278,6 +287,7 @@ func (ir *IstioRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
primaryName := fmt.Sprintf("%s-primary", targetName)
|
primaryName := fmt.Sprintf("%s-primary", targetName)
|
||||||
|
@ -310,6 +320,12 @@ func (ir *IstioRouter) SetRoutes(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirrored {
|
||||||
|
vsCopy.Spec.Http[0].Mirror = &istiov1alpha3.Destination{
|
||||||
|
Host: canaryName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fix routing (A/B testing)
|
// fix routing (A/B testing)
|
||||||
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
if len(canary.Spec.CanaryAnalysis.Match) > 0 {
|
||||||
// merge the common routes with the canary ones
|
// merge the common routes with the canary ones
|
||||||
|
|
|
@ -119,15 +119,16 @@ func TestIstioRouter_SetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p = 50
|
p = 60
|
||||||
c = 50
|
c = 40
|
||||||
|
m = false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.canary, p, c)
|
err = router.SetRoutes(mocks.canary, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -137,16 +138,20 @@ func TestIstioRouter_SetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pHost := fmt.Sprintf("%s-primary", mocks.canary.Spec.TargetRef.Name)
|
||||||
|
cHost := fmt.Sprintf("%s-canary", mocks.canary.Spec.TargetRef.Name)
|
||||||
pRoute := istiov1alpha3.DestinationWeight{}
|
pRoute := istiov1alpha3.DestinationWeight{}
|
||||||
cRoute := istiov1alpha3.DestinationWeight{}
|
cRoute := istiov1alpha3.DestinationWeight{}
|
||||||
|
var mirror *istiov1alpha3.Destination
|
||||||
|
|
||||||
for _, http := range vs.Spec.Http {
|
for _, http := range vs.Spec.Http {
|
||||||
for _, route := range http.Route {
|
for _, route := range http.Route {
|
||||||
if route.Destination.Host == fmt.Sprintf("%s-primary", mocks.canary.Spec.TargetRef.Name) {
|
if route.Destination.Host == pHost {
|
||||||
pRoute = route
|
pRoute = route
|
||||||
}
|
}
|
||||||
if route.Destination.Host == fmt.Sprintf("%s-canary", mocks.canary.Spec.TargetRef.Name) {
|
if route.Destination.Host == cHost {
|
||||||
cRoute = route
|
cRoute = route
|
||||||
|
mirror = http.Mirror
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +163,51 @@ func TestIstioRouter_SetRoutes(t *testing.T) {
|
||||||
if cRoute.Weight != c {
|
if cRoute.Weight != c {
|
||||||
t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
|
t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirror != nil {
|
||||||
|
t.Errorf("Got mirror %v wanted nil", mirror)
|
||||||
|
}
|
||||||
|
|
||||||
|
mirror = nil
|
||||||
|
p = 100
|
||||||
|
c = 0
|
||||||
|
m = true
|
||||||
|
|
||||||
|
err = router.SetRoutes(mocks.canary, p, c, m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, http := range vs.Spec.Http {
|
||||||
|
for _, route := range http.Route {
|
||||||
|
if route.Destination.Host == pHost {
|
||||||
|
pRoute = route
|
||||||
|
}
|
||||||
|
if route.Destination.Host == cHost {
|
||||||
|
cRoute = route
|
||||||
|
mirror = http.Mirror
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pRoute.Weight != p {
|
||||||
|
t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cRoute.Weight != c {
|
||||||
|
t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mirror == nil {
|
||||||
|
t.Errorf("Got mirror nil wanted a mirror")
|
||||||
|
} else if mirror.Host != cHost {
|
||||||
|
t.Errorf("Got mirror host \"%v\" wanted \"%v\"", mirror.Host, cHost)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIstioRouter_GetRoutes(t *testing.T) {
|
func TestIstioRouter_GetRoutes(t *testing.T) {
|
||||||
|
@ -174,7 +224,7 @@ func TestIstioRouter_GetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -186,6 +236,74 @@ func TestIstioRouter_GetRoutes(t *testing.T) {
|
||||||
if c != 0 {
|
if c != 0 {
|
||||||
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m != false {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
mocks.canary = newMockMirror()
|
||||||
|
|
||||||
|
err = router.Reconcile(mocks.canary)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
p, c, m, err = router.GetRoutes(mocks.canary)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if p != 100 {
|
||||||
|
t.Errorf("Got primary weight %v wanted %v", p, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c != 0 {
|
||||||
|
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Canary resource with mirror on does not automatically create mirroring
|
||||||
|
// in the virtual server (mirroring is activated as a temporary stage).
|
||||||
|
if m != false {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust vs to activate mirroring.
|
||||||
|
vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cHost := fmt.Sprintf("%s-canary", mocks.canary.Spec.TargetRef.Name)
|
||||||
|
for i, http := range vs.Spec.Http {
|
||||||
|
for _, route := range http.Route {
|
||||||
|
if route.Destination.Host == cHost {
|
||||||
|
vs.Spec.Http[i].Mirror = &istiov1alpha3.Destination{
|
||||||
|
Host: cHost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices(mocks.canary.Namespace).Update(vs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
p, c, m, err = router.GetRoutes(mocks.canary)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if p != 100 {
|
||||||
|
t.Errorf("Got primary weight %v wanted %v", p, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c != 0 {
|
||||||
|
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m != true {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIstioRouter_HTTPRequestHeaders(t *testing.T) {
|
func TestIstioRouter_HTTPRequestHeaders(t *testing.T) {
|
||||||
|
@ -276,8 +394,9 @@ func TestIstioRouter_ABTest(t *testing.T) {
|
||||||
|
|
||||||
p := 0
|
p := 0
|
||||||
c := 100
|
c := 100
|
||||||
|
m := false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.abtest, p, c)
|
err = router.SetRoutes(mocks.abtest, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -287,16 +406,20 @@ func TestIstioRouter_ABTest(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pHost := fmt.Sprintf("%s-primary", mocks.abtest.Spec.TargetRef.Name)
|
||||||
|
cHost := fmt.Sprintf("%s-canary", mocks.abtest.Spec.TargetRef.Name)
|
||||||
pRoute := istiov1alpha3.DestinationWeight{}
|
pRoute := istiov1alpha3.DestinationWeight{}
|
||||||
cRoute := istiov1alpha3.DestinationWeight{}
|
cRoute := istiov1alpha3.DestinationWeight{}
|
||||||
|
var mirror *istiov1alpha3.Destination
|
||||||
|
|
||||||
for _, http := range vs.Spec.Http {
|
for _, http := range vs.Spec.Http {
|
||||||
for _, route := range http.Route {
|
for _, route := range http.Route {
|
||||||
if route.Destination.Host == fmt.Sprintf("%s-primary", mocks.abtest.Spec.TargetRef.Name) {
|
if route.Destination.Host == pHost {
|
||||||
pRoute = route
|
pRoute = route
|
||||||
}
|
}
|
||||||
if route.Destination.Host == fmt.Sprintf("%s-canary", mocks.abtest.Spec.TargetRef.Name) {
|
if route.Destination.Host == cHost {
|
||||||
cRoute = route
|
cRoute = route
|
||||||
|
mirror = http.Mirror
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,4 +431,8 @@ func TestIstioRouter_ABTest(t *testing.T) {
|
||||||
if cRoute.Weight != c {
|
if cRoute.Weight != c {
|
||||||
t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
|
t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mirror != nil {
|
||||||
|
t.Errorf("Got mirror %v wanted nil", mirror)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,13 @@ func (*NopRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*NopRouter) SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int) error {
|
func (*NopRouter) SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int, mirror bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*NopRouter) GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, err error) {
|
func (*NopRouter) GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, mirror bool, err error) {
|
||||||
if canary.Status.Iterations > 0 {
|
if canary.Status.Iterations > 0 {
|
||||||
return 0, 100, nil
|
return 0, 100, false, nil
|
||||||
}
|
}
|
||||||
return 100, 0, nil
|
return 100, 0, false, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ import flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
Reconcile(canary *flaggerv1.Canary) error
|
Reconcile(canary *flaggerv1.Canary) error
|
||||||
SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int) error
|
SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int, mirrored bool) error
|
||||||
GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, err error)
|
GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, mirrored bool, err error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,12 @@ func newMockCanary() *v1alpha3.Canary {
|
||||||
return cd
|
return cd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMockMirror() *v1alpha3.Canary {
|
||||||
|
cd := newMockCanary()
|
||||||
|
cd.Spec.CanaryAnalysis.Mirror = true
|
||||||
|
return cd
|
||||||
|
}
|
||||||
|
|
||||||
func newMockABTest() *v1alpha3.Canary {
|
func newMockABTest() *v1alpha3.Canary {
|
||||||
cd := &v1alpha3.Canary{
|
cd := &v1alpha3.Canary{
|
||||||
TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()},
|
TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()},
|
||||||
|
|
|
@ -107,6 +107,7 @@ func (sr *SmiRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
func (sr *SmiRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (sr *SmiRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
@ -137,6 +138,8 @@ func (sr *SmiRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
targetName, canary.Namespace, primaryName, canaryName)
|
targetName, canary.Namespace, primaryName, canaryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mirrored = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +148,7 @@ func (sr *SmiRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
canaryName := fmt.Sprintf("%s-canary", targetName)
|
canaryName := fmt.Sprintf("%s-canary", targetName)
|
||||||
|
|
|
@ -82,11 +82,11 @@ func (sr *SuperglooRouter) Reconcile(canary *flaggerv1.Canary) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we have routes already?
|
// do we have routes already?
|
||||||
if _, _, err := sr.GetRoutes(canary); err == nil {
|
if _, _, _, err := sr.GetRoutes(canary); err == nil {
|
||||||
// we have routes, no need to do anything else
|
// we have routes, no need to do anything else
|
||||||
return nil
|
return nil
|
||||||
} else if solokiterror.IsNotExist(err) {
|
} else if solokiterror.IsNotExist(err) {
|
||||||
return sr.SetRoutes(canary, 100, 0)
|
return sr.SetRoutes(canary, 100, 0, false)
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -219,6 +219,7 @@ func (sr *SuperglooRouter) createRule(canary *flaggerv1.Canary, namesuffix strin
|
||||||
func (sr *SuperglooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
func (sr *SuperglooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
targetName := canary.Spec.TargetRef.Name
|
targetName := canary.Spec.TargetRef.Name
|
||||||
|
@ -247,6 +248,8 @@ func (sr *SuperglooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
||||||
targetName, canary.Namespace, targetName, targetName)
|
targetName, canary.Namespace, targetName, targetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mirrored = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +262,7 @@ func (sr *SuperglooRouter) SetRoutes(
|
||||||
canary *flaggerv1.Canary,
|
canary *flaggerv1.Canary,
|
||||||
primaryWeight int,
|
primaryWeight int,
|
||||||
canaryWeight int,
|
canaryWeight int,
|
||||||
|
mirrored bool,
|
||||||
) error {
|
) error {
|
||||||
// upstream name is
|
// upstream name is
|
||||||
// in gloo-system
|
// in gloo-system
|
||||||
|
|
|
@ -71,15 +71,16 @@ func TestSuperglooRouter_SetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p = 50
|
p = 50
|
||||||
c = 50
|
c = 50
|
||||||
|
m = false
|
||||||
|
|
||||||
err = router.SetRoutes(mocks.canary, p, c)
|
err = router.SetRoutes(mocks.canary, p, c, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,7 @@ func TestSuperglooRouter_GetRoutes(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
p, c, err := router.GetRoutes(mocks.canary)
|
p, c, m, err := router.GetRoutes(mocks.canary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -146,4 +147,8 @@ func TestSuperglooRouter_GetRoutes(t *testing.T) {
|
||||||
if c != 0 {
|
if c != 0 {
|
||||||
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
t.Errorf("Got canary weight %v wanted %v", c, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m != false {
|
||||||
|
t.Errorf("Got mirror %v wanted %v", m, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue