Add support for setting traffic distribution

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan
2025-10-15 16:57:06 +03:00
parent 60cb38a773
commit c30e6552d7
8 changed files with 76 additions and 6 deletions
+8 -1
View File
@@ -191,11 +191,18 @@ spec:
appProtocol:
description: Application protocol of the port
type: string
trafficDistribution:
description: Traffic distribution of the service
type: string
enum:
- PreferClose
- PreferSameZone
- PreferSameNode
targetPort:
description: Container target port name
x-kubernetes-int-or-string: true
portDiscovery:
description: Enable port dicovery
description: Enable port discovery
type: boolean
headless:
description: Headless if set to true, generates headless Kubernetes services.
+8 -1
View File
@@ -191,11 +191,18 @@ spec:
appProtocol:
description: Application protocol of the port
type: string
trafficDistribution:
description: Traffic distribution of the service
type: string
enum:
- PreferClose
- PreferSameZone
- PreferSameNode
targetPort:
description: Container target port name
x-kubernetes-int-or-string: true
portDiscovery:
description: Enable port dicovery
description: Enable port discovery
type: boolean
headless:
description: Headless if set to true, generates headless Kubernetes services.
+2 -1
View File
@@ -148,6 +148,7 @@ spec:
targetPort: 9898
portDiscovery: true
headless: false
trafficDistribution: PreferClose
```
The container port from the target workload should match the `service.port` or `service.targetPort`.
@@ -155,7 +156,7 @@ The `service.name` is optional, defaults to `spec.targetRef.name`.
The `service.targetPort` can be a container port number or name.
The `service.portName` is optional (defaults to `http`), if your workload uses gRPC then set the port name to `grpc`.
The `service.appProtocol` is optional, more details can be found [here](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol).
The `service.trafficDistribution` is optional, more details can be found [here](https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution).
If port discovery is enabled, Flagger scans the target workload and extracts the containers ports
excluding the port specified in the canary service and service mesh sidecar ports.
+8 -1
View File
@@ -191,11 +191,18 @@ spec:
appProtocol:
description: Application protocol of the port
type: string
trafficDistribution:
description: Traffic distribution of the service
type: string
enum:
- PreferClose
- PreferSameZone
- PreferSameNode
targetPort:
description: Container target port name
x-kubernetes-int-or-string: true
portDiscovery:
description: Enable port dicovery
description: Enable port discovery
type: boolean
headless:
description: Headless if set to true, generates headless Kubernetes services.
+8 -2
View File
@@ -20,10 +20,11 @@ import (
"fmt"
"time"
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
)
const (
@@ -150,6 +151,11 @@ type CanaryService struct {
// +optional
AppProtocol string `json:"appProtocol,omitempty"`
// TrafficDistribution of the service
// https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution
// +optional
TrafficDistribution string `json:"trafficDistribution,omitempty"`
// PortDiscovery adds all container ports to the generated Kubernetes service
PortDiscovery bool `json:"portDiscovery"`
+5
View File
@@ -165,6 +165,11 @@ func buildService(canary *flaggerv1.Canary, name string, src *corev1.Service) *c
// Operation cannot be fulfilled on services "mysvc-canary": the object has been modified; please apply your changes to the latest version and try again
delete(svc.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
}
if v := canary.Spec.Service.TrafficDistribution; v != "" {
svc.Spec.TrafficDistribution = &v
}
return svc
}
+4
View File
@@ -121,6 +121,10 @@ func (c *KubernetesDefaultRouter) reconcileService(canary *flaggerv1.Canary, nam
svcSpec.Ports[0].AppProtocol = &v
}
if v := canary.Spec.Service.TrafficDistribution; v != "" {
svcSpec.TrafficDistribution = &v
}
// set additional ports
for n, p := range c.ports {
cp := corev1.ServicePort{
+33
View File
@@ -64,6 +64,39 @@ func TestServiceRouter_Create(t *testing.T) {
assert.Equal(t, "None", primarySvc.Spec.ClusterIP)
}
func TestServiceRouter_TrafficDistribution(t *testing.T) {
mocks := newFixture(nil)
trafficDistribution := "PreferClose"
mocks.canary.Spec.Service.TrafficDistribution = trafficDistribution
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
}
err := router.Initialize(mocks.canary)
require.NoError(t, err)
err = router.Reconcile(mocks.canary)
require.NoError(t, err)
canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get(context.TODO(), "podinfo-canary", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, canarySvc.Spec.TrafficDistribution)
assert.Equal(t, trafficDistribution, *canarySvc.Spec.TrafficDistribution)
primarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, primarySvc.Spec.TrafficDistribution)
assert.Equal(t, trafficDistribution, *primarySvc.Spec.TrafficDistribution)
apexSvc, err := mocks.kubeClient.CoreV1().Services("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, apexSvc.Spec.TrafficDistribution)
assert.Equal(t, trafficDistribution, *apexSvc.Spec.TrafficDistribution)
}
func TestServiceRouter_Update(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{