From ff1ad8557856e9dbecdb3ab68af4a3010ea40d46 Mon Sep 17 00:00:00 2001 From: Jhuliano Skittberg Moreno Date: Thu, 31 Jul 2025 09:51:00 +0200 Subject: [PATCH 1/2] add failing test for VS cleanup on delegation Signed-off-by: Jhuliano Skittberg Moreno --- pkg/router/istio_test.go | 33 +++++++++++++++++++++++++ test/istio/test-delegation.sh | 45 ++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go index 9bebeb0e2..56a01530e 100644 --- a/pkg/router/istio_test.go +++ b/pkg/router/istio_test.go @@ -599,6 +599,39 @@ func TestIstioRouter_Delegate(t *testing.T) { err := router.Reconcile(mocks.canary) require.Error(t, err) }) + + t.Run("syncs when enabled", func(t *testing.T) { + mocks := newFixture(nil) + mocks.canary.Spec.Service.Hosts = []string{} + mocks.canary.Spec.Service.Gateways = []string{} + mocks.canary.Spec.Service.PortDiscovery = false + + router := &IstioRouter{ + logger: mocks.logger, + flaggerClient: mocks.flaggerClient, + istioClient: mocks.meshClient, + kubeClient: mocks.kubeClient, + } + + err := router.Reconcile(mocks.canary) + require.NoError(t, err) + + vs, err := mocks.meshClient.NetworkingV1beta1().VirtualServices("default").Get(context.TODO(), "podinfo", metav1.GetOptions{}) + require.NoError(t, err) + require.Len(t, vs.Spec.Http, 1) + require.Len(t, vs.Spec.Http[0].Route, 2) + + // enable delegation + mocks.canary.Spec.Service.Delegation = true + + err = router.Reconcile(mocks.canary) + require.NoError(t, err) + + vs, err = mocks.meshClient.NetworkingV1beta1().VirtualServices("default").Get(context.TODO(), "podinfo", metav1.GetOptions{}) + require.NoError(t, err) + assert.Len(t, vs.Spec.Gateways, 0) + assert.Len(t, vs.Spec.Hosts, 0) + }) } func TestIstioRouter_Finalize(t *testing.T) { diff --git a/test/istio/test-delegation.sh b/test/istio/test-delegation.sh index d83ec219b..63b88e4e1 100755 --- a/test/istio/test-delegation.sh +++ b/test/istio/test-delegation.sh @@ -52,7 +52,7 @@ spec: namespace: test EOF -echo '>>> Initialising canary for delegate' +echo '>>> Initialising canary' cat <>> Enabling delegation on canary' +kubectl patch canary podinfo -n test --type=merge -p '{"spec":{"service":{"delegation":true}}}' + +echo '>>> Waiting for VirtualService to be updated with delegation...' + +retries=30 +count=0 +ok=false + +until ${ok}; do + VS_YAML=$(kubectl get virtualservice/podinfo -n test -o yaml 2>/dev/null) + + # A VirtualService is considered INVALID if it contains a 'hosts:' or 'gateways:' key + # that is NOT immediately followed by an empty array '[]'. + if [[ -n "${VS_YAML}" ]] && ! echo "${VS_YAML}" | grep -vE '^ (hosts|gateways): \[\]$' | grep -qE '^ (hosts|gateways):'; then + echo "✔ Validation Passed: 'gateways' and 'hosts' are either absent or empty." + ok=true + else + # If ok is not true, print a status message. + echo "VirtualService not ready yet ('gateways' or 'hosts' key is present). Retrying..." + ok=false + fi + + if ${ok}; then + break + fi + + count=$(($count + 1)) + if [[ ${count} -eq ${retries} ]]; then + kubectl -n istio-system logs deployment/flagger + kubectl get virtualservice/podinfo -n test -o yaml + echo "No more retries left" + exit 1 + fi + + sleep 5 +done + +echo '✔ VirtualService delegation enabled' + echo '>>> Triggering canary deployment' kubectl -n test set image deployment/podinfo podinfod=ghcr.io/stefanprodan/podinfo:6.0.1 From 38766d43d549a99b5a636214eb9c2473c0a070b2 Mon Sep 17 00:00:00 2001 From: Jhuliano Skittberg Moreno Date: Thu, 31 Jul 2025 09:51:32 +0200 Subject: [PATCH 2/2] ensure VirtualService is updated when delegation is enabled Signed-off-by: Jhuliano Skittberg Moreno --- pkg/router/istio.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/router/istio.go b/pkg/router/istio.go index 75b3701a7..2a9ce30d9 100644 --- a/pkg/router/istio.go +++ b/pkg/router/istio.go @@ -294,9 +294,19 @@ func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error { } if canary.Spec.Service.Delegation { + updateVS := len(virtualService.Spec.Hosts) > 0 || len(virtualService.Spec.Gateways) > 0 // delegate VirtualService requires the hosts and gateway empty. virtualService.Spec.Gateways = []string{} virtualService.Spec.Hosts = []string{} + + if updateVS { + _, err = ir.istioClient.NetworkingV1beta1().VirtualServices(canary.Namespace).Update(context.TODO(), virtualService, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("VirtualService %s.%s update error: %w", apexName, canary.Namespace, err) + } + ir.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). + Infof("VirtualService %s.%s updated for delegation", virtualService.GetName(), canary.Namespace) + } } ignoreCmpOptions := []cmp.Option{