Repository: weaveworks/flagger
Pull request #436 (2020-02-12 07:44:45)

Set destination port for Istio ingress gateways
This PR makes it possible to have multiple ports exposed inside the mesh when using an Istio ingress gateway. The `service.port` is mapped in the virtual service so that the Gateway traffic would reach the destination on that port. When using the `mesh` gateway, all ports are subject to traffic shifting, when using an ingress gateway only the `service.port` will be used.

Fix: #434 


Patch:
diff --git a/charts/flagger/values.yaml b/charts/flagger/values.yaml
index f12d9e42..826c551a 100644
--- a/charts/flagger/values.yaml
+++ b/charts/flagger/values.yaml
@@ -57,11 +57,6 @@ podMonitor:
 #    secretKeyRef:
 #      name: msteams
 #      key: url
-#- name: EVENT_WEBHOOK_URL
-#  valueFrom:
-#    secretKeyRef:
-#      name: eventwebhook
-#      key: url
 env: []
 
 leaderElection:
diff --git a/cmd/flagger/main.go b/cmd/flagger/main.go
index 5ab5c80f..fe5d79c8 100644
--- a/cmd/flagger/main.go
+++ b/cmd/flagger/main.go
@@ -186,7 +186,7 @@ func main() {
 		observerFactory,
 		meshProvider,
 		version.VERSION,
-		fromEnv("EVENT_WEBHOOK_URL", eventWebhook),
+		eventWebhook,
 	)
 
 	// leader election context
diff --git a/docs/gitbook/usage/alerting.md b/docs/gitbook/usage/alerting.md
index be6f46c8..3c462792 100644
--- a/docs/gitbook/usage/alerting.md
+++ b/docs/gitbook/usage/alerting.md
@@ -62,9 +62,6 @@ helm upgrade -i flagger flagger/flagger \
 --set eventWebhook=https://example.com/flagger-canary-event-webhook
 ```
 
-The environment variable *EVENT_WEBHOOK_URL* can be used for activating the event-webhook, too.
-This is handy for using a secret to store a sensible value that could contain api keys for example.
-
 When configured, every action that Flagger takes during a canary deployment will be sent as JSON via an HTTP POST
 request. The JSON payload has the following schema:
 
diff --git a/pkg/router/istio.go b/pkg/router/istio.go
index 4a87b8fa..c8f04849 100644
--- a/pkg/router/istio.go
+++ b/pkg/router/istio.go
@@ -395,5 +395,20 @@ func makeDestination(canary *flaggerv1.Canary, host string, weight int) istiov1a
 		Weight: weight,
 	}
 
+	// set destination port when an ingress gateway is specified
+	if canary.Spec.Service.PortDiscovery &&
+		len(canary.Spec.Service.Gateways) > 0 &&
+		canary.Spec.Service.Gateways[0] != "mesh" {
+		dest = istiov1alpha3.DestinationWeight{
+			Destination: istiov1alpha3.Destination{
+				Host: host,
+				Port: &istiov1alpha3.PortSelector{
+					Number: uint32(canary.Spec.Service.Port),
+				},
+			},
+			Weight: weight,
+		}
+	}
+
 	return dest
 }


Test patch:
diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go
index de09c480..e7ba0732 100644
--- a/pkg/router/istio_test.go
+++ b/pkg/router/istio_test.go
@@ -82,16 +82,20 @@ func TestIstioRouter_Sync(t *testing.T) {
 	gateways := vsClone.Spec.Gateways
 	gateways = append(gateways, "test-gateway.istio-system")
 	vsClone.Spec.Gateways = gateways
+	totalGateways := len(mocks.canary.Spec.Service.Gateways)
 
 	vsGateways, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Update(vsClone)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
-	if len(vsGateways.Spec.Gateways) != 2 {
-		t.Errorf("Got Istio VS gateway %v wanted %v", vsGateways.Spec.Gateways, 2)
+
+	totalGateways++
+	if len(vsGateways.Spec.Gateways) != totalGateways {
+		t.Errorf("Got Istio VS gateway %v wanted %v", vsGateways.Spec.Gateways, totalGateways)
 	}
 
 	// undo change
+	totalGateways--
 	err = router.Reconcile(mocks.canary)
 	if err != nil {
 		t.Fatal(err.Error())
@@ -102,8 +106,8 @@ func TestIstioRouter_Sync(t *testing.T) {
 	if err != nil {
 		t.Fatal(err.Error())
 	}
-	if len(vs.Spec.Gateways) != 1 {
-		t.Errorf("Got Istio VS gateways %v wanted %v", vs.Spec.Gateways, 1)
+	if len(vs.Spec.Gateways) != totalGateways {
+		t.Errorf("Got Istio VS gateways %v wanted %v", vs.Spec.Gateways, totalGateways)
 	}
 }
 
@@ -438,3 +442,28 @@ func TestIstioRouter_ABTest(t *testing.T) {
 		t.Errorf("Got mirror %v wanted nil", mirror)
 	}
 }
+
+func TestIstioRouter_GatewayPort(t *testing.T) {
+	mocks := newFixture()
+	router := &IstioRouter{
+		logger:        mocks.logger,
+		flaggerClient: mocks.flaggerClient,
+		istioClient:   mocks.meshClient,
+		kubeClient:    mocks.kubeClient,
+	}
+
+	err := router.Reconcile(mocks.canary)
+	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())
+	}
+
+	port := vs.Spec.Http[0].Route[0].Destination.Port.Number
+	if port != uint32(mocks.canary.Spec.Service.Port) {
+		t.Fatalf("Got port %v wanted %v", port, mocks.canary.Spec.Service.Port)
+	}
+}
diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go
index 1389a690..0d1973ae 100644
--- a/pkg/router/router_test.go
+++ b/pkg/router/router_test.go
@@ -113,7 +113,8 @@ func newMockCanary() *flaggerv1.Canary {
 				Kind:       "Deployment",
 			},
 			Service: flaggerv1.CanaryService{
-				Port: 9898,
+				Port:          9898,
+				PortDiscovery: true,
 				Headers: &istiov1alpha3.Headers{
 					Request: &istiov1alpha3.HeaderOperations{
 						Add: map[string]string{
@@ -136,6 +137,10 @@ func newMockCanary() *flaggerv1.Canary {
 					Attempts:      10,
 					PerTryTimeout: "30s",
 				},
+				Gateways: []string{
+					"public-gateway.istio",
+					"mesh",
+				},
 			}, CanaryAnalysis: flaggerv1.CanaryAnalysis{
 				Threshold:  10,
 				StepWeight: 10,