diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go index 5bd83927b..26f5a9fc8 100644 --- a/pkg/router/istio_test.go +++ b/pkg/router/istio_test.go @@ -239,3 +239,63 @@ func TestIstioRouter_CORS(t *testing.T) { t.Fatalf("Got CORS allow methods %v wanted %v", len(methods), 2) } } + +func TestIstioRouter_ABTest(t *testing.T) { + mocks := setupfakeClients() + router := &IstioRouter{ + logger: mocks.logger, + flaggerClient: mocks.flaggerClient, + istioClient: mocks.istioClient, + kubeClient: mocks.kubeClient, + } + + err := router.Sync(mocks.abtest) + if err != nil { + t.Fatal(err.Error()) + } + + // test insert + vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + if len(vs.Spec.Http) != 2 { + t.Errorf("Got Istio VS Http %v wanted %v", len(vs.Spec.Http), 2) + } + + p := 0 + c := 100 + + err = router.SetRoutes(mocks.abtest, p, c) + if err != nil { + t.Fatal(err.Error()) + } + + vs, err = mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + pRoute := istiov1alpha3.DestinationWeight{} + cRoute := istiov1alpha3.DestinationWeight{} + + for _, http := range vs.Spec.Http { + for _, route := range http.Route { + if route.Destination.Host == fmt.Sprintf("%s-primary", mocks.abtest.Spec.TargetRef.Name) { + pRoute = route + } + if route.Destination.Host == fmt.Sprintf("%s-canary", mocks.abtest.Spec.TargetRef.Name) { + cRoute = route + } + } + } + + 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) + } +} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index e3bc14607..fc08db1d2 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -2,6 +2,7 @@ package router import ( "github.com/stefanprodan/flagger/pkg/apis/flagger/v1alpha3" + istiov1alpha1 "github.com/stefanprodan/flagger/pkg/apis/istio/common/v1alpha1" istiov1alpha3 "github.com/stefanprodan/flagger/pkg/apis/istio/v1alpha3" clientset "github.com/stefanprodan/flagger/pkg/client/clientset/versioned" fakeFlagger "github.com/stefanprodan/flagger/pkg/client/clientset/versioned/fake" @@ -17,6 +18,7 @@ import ( type fakeClients struct { canary *v1alpha3.Canary + abtest *v1alpha3.Canary kubeClient kubernetes.Interface istioClient clientset.Interface flaggerClient clientset.Interface @@ -25,17 +27,17 @@ type fakeClients struct { func setupfakeClients() fakeClients { canary := newMockCanary() - flaggerClient := fakeFlagger.NewSimpleClientset(canary) + abtest := newMockABTest() + flaggerClient := fakeFlagger.NewSimpleClientset(canary, abtest) - kubeClient := fake.NewSimpleClientset( - newMockDeployment(), - ) + kubeClient := fake.NewSimpleClientset(newMockDeployment(), newMockABTestDeployment()) istioClient := fakeFlagger.NewSimpleClientset() logger, _ := logging.NewLogger("debug") return fakeClients{ canary: canary, + abtest: abtest, kubeClient: kubeClient, istioClient: istioClient, flaggerClient: flaggerClient, @@ -93,6 +95,51 @@ func newMockCanary() *v1alpha3.Canary { return cd } +func newMockABTest() *v1alpha3.Canary { + cd := &v1alpha3.Canary{ + TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "abtest", + }, + Spec: v1alpha3.CanarySpec{ + TargetRef: hpav1.CrossVersionObjectReference{ + Name: "abtest", + APIVersion: "apps/v1", + Kind: "Deployment", + }, + Service: v1alpha3.CanaryService{ + Port: 9898, + }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ + Threshold: 10, + Iterations: 2, + Match: []istiov1alpha3.HTTPMatchRequest{ + { + Headers: map[string]istiov1alpha1.StringMatch{ + "x-user-type": { + Exact: "test", + }, + }, + }, + }, + Metrics: []v1alpha3.CanaryMetric{ + { + Name: "istio_requests_total", + Threshold: 99, + Interval: "1m", + }, + { + Name: "istio_request_duration_seconds_bucket", + Threshold: 500, + Interval: "1m", + }, + }, + }, + }, + } + return cd +} + func newMockDeployment() *appsv1.Deployment { d := &appsv1.Deployment{ TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, @@ -137,3 +184,48 @@ func newMockDeployment() *appsv1.Deployment { return d } + +func newMockABTestDeployment() *appsv1.Deployment { + d := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "abtest", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "abtest", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "abtest", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "podinfo", + Image: "quay.io/stefanprodan/podinfo:1.4.0", + Command: []string{ + "./podinfo", + "--port=9898", + }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 9898, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + }, + }, + }, + }, + } + + return d +}