diff --git a/pkg/router/appmesh_test.go b/pkg/router/appmesh_test.go new file mode 100644 index 00000000..b5651cf4 --- /dev/null +++ b/pkg/router/appmesh_test.go @@ -0,0 +1,131 @@ +package router + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestAppmeshRouter_Sync(t *testing.T) { + mocks := setupfakeClients() + router := &AppmeshRouter{ + logger: mocks.logger, + flaggerClient: mocks.flaggerClient, + appMeshClient: mocks.meshClient, + kubeClient: mocks.kubeClient, + } + + err := router.Sync(mocks.appmeshCanary) + if err != nil { + t.Fatal(err.Error()) + } + + // check virtual service + vsName := fmt.Sprintf("%s.%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) + vs, err := router.appMeshClient.AppmeshV1alpha1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + meshName := mocks.appmeshCanary.Spec.Service.MeshName + if vs.Spec.MeshName != meshName { + t.Errorf("Got mesh name %v wanted %v", vs.Spec.MeshName, meshName) + } + + targetsCount := len(vs.Spec.Routes[0].Http.Action.WeightedTargets) + if targetsCount != 2 { + t.Errorf("Got routes %v wanted %v", targetsCount, 2) + } + + // check virtual node + vnName := fmt.Sprintf("%s-%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) + vn, err := router.appMeshClient.AppmeshV1alpha1().VirtualNodes("default").Get(vnName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + primaryDNS := fmt.Sprintf("%s-primary.%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) + vnHostName := vn.Spec.ServiceDiscovery.Dns.HostName + if vnHostName != primaryDNS { + t.Errorf("Got DNS host name %v wanted %v", vnHostName, primaryDNS) + } + + // test backends update + cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("appmesh", metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + cdClone := cd.DeepCopy() + hosts := cdClone.Spec.Service.Backends + hosts = append(hosts, "test.example.com") + cdClone.Spec.Service.Backends = hosts + canary, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cdClone) + if err != nil { + t.Fatal(err.Error()) + } + + // apply change + err = router.Sync(canary) + if err != nil { + t.Fatal(err.Error()) + } + + // verify + vnCanaryName := fmt.Sprintf("%s-canary-%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) + vnCanary, err := router.appMeshClient.AppmeshV1alpha1().VirtualNodes("default").Get(vnCanaryName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + if len(vnCanary.Spec.Backends) != 2 { + t.Errorf("Got backends %v wanted %v", len(vnCanary.Spec.Backends), 2) + } + + // test weight update + vsClone := vs.DeepCopy() + vsClone.Spec.Routes[0].Http.Action.WeightedTargets[0].Weight = 50 + vsClone.Spec.Routes[0].Http.Action.WeightedTargets[1].Weight = 50 + vs, err = mocks.meshClient.AppmeshV1alpha1().VirtualServices("default").Update(vsClone) + if err != nil { + t.Fatal(err.Error()) + } + + // apply change + err = router.Sync(canary) + if err != nil { + t.Fatal(err.Error()) + } + vs, err = router.appMeshClient.AppmeshV1alpha1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + weight := vs.Spec.Routes[0].Http.Action.WeightedTargets[0].Weight + if weight != 50 { + t.Errorf("Got weight %v wanted %v", weight, 502) + } + + // test URI update + vsClone = vs.DeepCopy() + vsClone.Spec.Routes[0].Http.Match.Prefix = "api" + vs, err = mocks.meshClient.AppmeshV1alpha1().VirtualServices("default").Update(vsClone) + if err != nil { + t.Fatal(err.Error()) + } + + // apply change + err = router.Sync(canary) + if err != nil { + t.Fatal(err.Error()) + } + vs, err = router.appMeshClient.AppmeshV1alpha1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err.Error()) + } + + prefix := vs.Spec.Routes[0].Http.Match.Prefix + if prefix != "/" { + t.Errorf("Got prefix %v wanted %v", prefix, "/") + } +} diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go index 26f5a9fc..f19ea59d 100644 --- a/pkg/router/istio_test.go +++ b/pkg/router/istio_test.go @@ -12,7 +12,7 @@ func TestIstioRouter_Sync(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -22,7 +22,7 @@ func TestIstioRouter_Sync(t *testing.T) { } // test insert - vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -57,7 +57,7 @@ func TestIstioRouter_Sync(t *testing.T) { } // verify - vs, err = mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -71,7 +71,7 @@ func TestIstioRouter_Sync(t *testing.T) { gateways = append(gateways, "test-gateway.istio-system") vsClone.Spec.Gateways = gateways - vsGateways, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Update(vsClone) + vsGateways, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Update(vsClone) if err != nil { t.Fatal(err.Error()) } @@ -86,7 +86,7 @@ func TestIstioRouter_Sync(t *testing.T) { } // verify - vs, err = mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -100,7 +100,7 @@ func TestIstioRouter_SetRoutes(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -122,7 +122,7 @@ func TestIstioRouter_SetRoutes(t *testing.T) { t.Fatal(err.Error()) } - vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -155,7 +155,7 @@ func TestIstioRouter_GetRoutes(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -183,7 +183,7 @@ func TestIstioRouter_HTTPRequestHeaders(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -192,7 +192,7 @@ func TestIstioRouter_HTTPRequestHeaders(t *testing.T) { t.Fatal(err.Error()) } - vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -212,7 +212,7 @@ func TestIstioRouter_CORS(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -221,7 +221,7 @@ func TestIstioRouter_CORS(t *testing.T) { t.Fatal(err.Error()) } - vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) + vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -245,7 +245,7 @@ func TestIstioRouter_ABTest(t *testing.T) { router := &IstioRouter{ logger: mocks.logger, flaggerClient: mocks.flaggerClient, - istioClient: mocks.istioClient, + istioClient: mocks.meshClient, kubeClient: mocks.kubeClient, } @@ -255,7 +255,7 @@ func TestIstioRouter_ABTest(t *testing.T) { } // test insert - vs, err := mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) + vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } @@ -272,7 +272,7 @@ func TestIstioRouter_ABTest(t *testing.T) { t.Fatal(err.Error()) } - vs, err = mocks.istioClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) + vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) if err != nil { t.Fatal(err.Error()) } diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index fc08db1d..6254ecd4 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -19,8 +19,9 @@ import ( type fakeClients struct { canary *v1alpha3.Canary abtest *v1alpha3.Canary + appmeshCanary *v1alpha3.Canary kubeClient kubernetes.Interface - istioClient clientset.Interface + meshClient clientset.Interface flaggerClient clientset.Interface logger *zap.SugaredLogger } @@ -28,23 +29,59 @@ type fakeClients struct { func setupfakeClients() fakeClients { canary := newMockCanary() abtest := newMockABTest() - flaggerClient := fakeFlagger.NewSimpleClientset(canary, abtest) + appmeshCanary := newMockCanaryAppMesh() + flaggerClient := fakeFlagger.NewSimpleClientset(canary, abtest, appmeshCanary) kubeClient := fake.NewSimpleClientset(newMockDeployment(), newMockABTestDeployment()) - istioClient := fakeFlagger.NewSimpleClientset() + meshClient := fakeFlagger.NewSimpleClientset() logger, _ := logging.NewLogger("debug") return fakeClients{ canary: canary, abtest: abtest, + appmeshCanary: appmeshCanary, kubeClient: kubeClient, - istioClient: istioClient, + meshClient: meshClient, flaggerClient: flaggerClient, logger: logger, } } +func newMockCanaryAppMesh() *v1alpha3.Canary { + cd := &v1alpha3.Canary{ + TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "appmesh", + }, + Spec: v1alpha3.CanarySpec{ + TargetRef: hpav1.CrossVersionObjectReference{ + Name: "podinfo", + APIVersion: "apps/v1", + Kind: "Deployment", + }, + Service: v1alpha3.CanaryService{ + Port: 9898, + MeshName: "global", + Backends: []string{"backend.default"}, + }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ + Threshold: 10, + StepWeight: 10, + MaxWeight: 50, + Metrics: []v1alpha3.CanaryMetric{ + { + Name: "appmesh_requests_total", + Threshold: 99, + Interval: "1m", + }, + }, + }, + }, + } + return cd +} + func newMockCanary() *v1alpha3.Canary { cd := &v1alpha3.Canary{ TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()},