Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pkg/router/istio.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
33 changes: 33 additions & 0 deletions pkg/router/istio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
45 changes: 42 additions & 3 deletions test/istio/test-delegation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ spec:
namespace: test
EOF

echo '>>> Initialising canary for delegate'
echo '>>> Initialising canary'
cat <<EOF | kubectl apply -f -
apiVersion: flagger.app/v1beta1
kind: Canary
Expand All @@ -68,8 +68,7 @@ spec:
service:
port: 80
targetPort: 9898
portDiscovery: true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed portDiscovery: true because it was workaround for the reported issue.

delegation: true
delegation: false # start with delegation disabled to make sure it works properly after it's enabled
analysis:
interval: 15s
threshold: 15
Expand Down Expand Up @@ -102,6 +101,46 @@ done

echo '✔ Canary initialization test passed'

echo '>>> 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

Expand Down
Loading