Skip to content

Commit

Permalink
Add integration test for service-based canary
Browse files Browse the repository at this point in the history
  • Loading branch information
mumoshu committed Nov 19, 2019
1 parent e39f45b commit a24ca63
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 0 deletions.
54 changes: 54 additions & 0 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
hpav2 "k8s.io/api/autoscaling/v2beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/record"
Expand Down Expand Up @@ -52,6 +53,7 @@ func SetupMocks(c *flaggerv1.Canary) Mocks {
// init kube clientset and register mock objects
kubeClient := fake.NewSimpleClientset(
newTestDeployment(),
newTestService(),
newTestHPA(),
NewTestConfigMap(),
NewTestConfigMapEnv(),
Expand Down Expand Up @@ -560,6 +562,58 @@ func newTestDeploymentV2() *appsv1.Deployment {
return d
}

func newTestService() *corev1.Service {
d := &corev1.Service{
TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "podinfo",
},
Type: corev1.ServiceTypeClusterIP,
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 9898,
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromString("http"),
},
},
},
}

return d
}

func newTestServiceV2() *corev1.Service {
d := &corev1.Service{
TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "podinfo-v2",
},
Type: corev1.ServiceTypeClusterIP,
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 9898,
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromString("http"),
},
},
},
}

return d
}

func newTestHPA() *hpav2.HorizontalPodAutoscaler {
h := &hpav2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()},
Expand Down
166 changes: 166 additions & 0 deletions pkg/controller/scheduler_svc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package controller

import (
"testing"

hpav1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
)

func TestScheduler_ServicePromotion(t *testing.T) {
mocks := SetupMocks(newTestServiceCanary())

// init
mocks.ctrl.advanceCanary("podinfo", "default", true)

// check initialized status
c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

if c.Status.Phase != flaggerv1.CanaryPhaseInitialized {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, flaggerv1.CanaryPhaseInitialized)
}

// update
svc2 := newTestServiceV2()
_, err = mocks.kubeClient.CoreV1().Services("default").Update(svc2)
if err != nil {
t.Fatal(err.Error())
}

// detect service spec changes
mocks.ctrl.advanceCanary("podinfo", "default", true)

primaryWeight, canaryWeight, mirrored, err := mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}

primaryWeight = 60
canaryWeight = 40
err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight, mirrored)
if err != nil {
t.Fatal(err.Error())
}

// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)

// check progressing status
c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

if c.Status.Phase != flaggerv1.CanaryPhaseProgressing {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, flaggerv1.CanaryPhaseProgressing)
}

// promote
mocks.ctrl.advanceCanary("podinfo", "default", true)

// check promoting status
c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

if c.Status.Phase != flaggerv1.CanaryPhasePromoting {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, flaggerv1.CanaryPhasePromoting)
}

// finalise
mocks.ctrl.advanceCanary("podinfo", "default", true)

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 != false {
t.Errorf("Got mirrored %v wanted %v", mirrored, false)
}

primarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

primaryLabelValue := primarySvc.Spec.Selector["app"]
canaryLabelValue := svc2.Spec.Selector["app"]
if primaryLabelValue != canaryLabelValue {
t.Errorf("Got primary selector label value %v wanted %v", primaryLabelValue, canaryLabelValue)
}

// check finalising status
c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

if c.Status.Phase != flaggerv1.CanaryPhaseFinalising {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, flaggerv1.CanaryPhaseFinalising)
}

// scale canary to zero
mocks.ctrl.advanceCanary("podinfo", "default", true)

c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}

if c.Status.Phase != flaggerv1.CanaryPhaseSucceeded {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, flaggerv1.CanaryPhaseSucceeded)
}
}

func newTestServiceCanary() *flaggerv1.Canary {
cd := &flaggerv1.Canary{
TypeMeta: metav1.TypeMeta{APIVersion: flaggerv1.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: flaggerv1.CanarySpec{
TargetRef: hpav1.CrossVersionObjectReference{
Name: "podinfo",
APIVersion: "core/v1",
Kind: "Service",
},
Service: flaggerv1.CanaryService{
Port: 9898,
},
CanaryAnalysis: flaggerv1.CanaryAnalysis{
Threshold: 10,
StepWeight: 10,
MaxWeight: 50,
Metrics: []flaggerv1.CanaryMetric{
{
Name: "istio_requests_total",
Threshold: 99,
Interval: "1m",
},
{
Name: "istio_request_duration_seconds_bucket",
Threshold: 500,
Interval: "1m",
},
},
},
},
}
return cd
}

0 comments on commit a24ca63

Please sign in to comment.