Skip to content

Commit 9586e38

Browse files
committed
Add support for service annotation propagation
Enable propagation of Service annotations from workload clusters to influence final Service creation. Only propagates annotations when enable-service-annotation-propagation is explicitly set true.
1 parent ef42075 commit 9586e38

File tree

6 files changed

+255
-52
lines changed

6 files changed

+255
-52
lines changed

pkg/cloudprovider/vsphereparavirtual/cloud.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ var (
6262

6363
// podIPPoolType specifies if Pod IP addresses are public or private.
6464
podIPPoolType string
65+
66+
// serviceAnnotationPropagationEnabled if set to true, will propagate the service annotation to resource in supervisor cluster.
67+
serviceAnnotationPropagationEnabled bool
6568
)
6669

6770
func init() {
@@ -89,6 +92,7 @@ func init() {
8992
flag.BoolVar(&vmservice.IsLegacy, "is-legacy-paravirtual", false, "If true, machine label selector will start with capw.vmware.com. By default, it's false, machine label selector will start with capv.vmware.com.")
9093
flag.BoolVar(&vpcModeEnabled, "enable-vpc-mode", false, "If true, routable pod controller will start with VPC mode. It is useful only when route controller is enabled in vsphereparavirtual mode")
9194
flag.StringVar(&podIPPoolType, "pod-ip-pool-type", "", "Specify if Pod IP address is Public or Private routable in VPC network. Valid values are Public and Private")
95+
flag.BoolVar(&serviceAnnotationPropagationEnabled, "enable-service-annotation-propagation", false, "If true, will propagate the service annotation to resource in supervisor cluster.")
9296
}
9397

9498
// Creates new Controller node interface and returns
@@ -139,7 +143,7 @@ func (cp *VSphereParavirtual) Initialize(clientBuilder cloudprovider.ControllerC
139143
}
140144
cp.routes = routes
141145

142-
lb, err := NewLoadBalancer(clusterNS, kcfg, cp.ownerReference)
146+
lb, err := NewLoadBalancer(clusterNS, kcfg, cp.ownerReference, serviceAnnotationPropagationEnabled)
143147
if err != nil {
144148
klog.Errorf("Failed to init LoadBalancer: %v", err)
145149
}

pkg/cloudprovider/vsphereparavirtual/loadbalancer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ type loadBalancer struct {
3939
}
4040

4141
// NewLoadBalancer returns an implementation of cloudprovider.LoadBalancer
42-
func NewLoadBalancer(clusterNS string, kcfg *rest.Config, ownerRef *metav1.OwnerReference) (cloudprovider.LoadBalancer, error) {
42+
func NewLoadBalancer(clusterNS string, kcfg *rest.Config, ownerRef *metav1.OwnerReference, serviceAnnotationPropagationEnabled bool) (cloudprovider.LoadBalancer, error) {
4343
klog.V(1).Info("Create load balancer for vsphere paravirtual cloud provider")
4444

4545
client, err := vmservice.GetVmopClient(kcfg)
4646
if err != nil {
4747
klog.Errorf("failed to create load balancer: %v", err)
4848
return nil, err
4949
}
50-
vmService := vmservice.NewVMService(client, clusterNS, ownerRef)
50+
vmService := vmservice.NewVMService(client, clusterNS, ownerRef, serviceAnnotationPropagationEnabled)
5151
return &loadBalancer{
5252
vmService: vmService,
5353
}, nil

pkg/cloudprovider/vsphereparavirtual/loadbalancer_test.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,34 @@ func newTestLoadBalancer() (cloudprovider.LoadBalancer, *dynamicfake.FakeDynamic
6060
fc := dynamicfake.NewSimpleDynamicClient(scheme)
6161
fcw := vmopclient.NewFakeClientSet(fc)
6262

63-
vms := vmservice.NewVMService(fcw, testClusterNameSpace, &testOwnerReference)
63+
vms := vmservice.NewVMService(fcw, testClusterNameSpace, &testOwnerReference, false)
6464
return &loadBalancer{vmService: vms}, fc
6565
}
6666

6767
func TestNewLoadBalancer(t *testing.T) {
6868
testCases := []struct {
69-
name string
70-
config *rest.Config
71-
err error
69+
name string
70+
config *rest.Config
71+
serviceAnnotationPropagationEnabled bool
72+
err error
7273
}{
7374
{
74-
name: "NewLoadBalancer: when everything is ok",
75-
config: &rest.Config{},
76-
err: nil,
75+
name: "NewLoadBalancer: when serviceAnnotationPropagationEnabled is false",
76+
config: &rest.Config{},
77+
serviceAnnotationPropagationEnabled: false,
78+
err: nil,
79+
},
80+
{
81+
name: "NewLoadBalancer: when serviceAnnotationPropagationEnabled is true",
82+
config: &rest.Config{},
83+
serviceAnnotationPropagationEnabled: true,
84+
err: nil,
7785
},
7886
}
7987

8088
for _, testCase := range testCases {
8189
t.Run(testCase.name, func(t *testing.T) {
82-
_, err := NewLoadBalancer(testClusterNameSpace, testCase.config, &testOwnerReference)
90+
_, err := NewLoadBalancer(testClusterNameSpace, testCase.config, &testOwnerReference, testCase.serviceAnnotationPropagationEnabled)
8391
assert.Equal(t, testCase.err, err)
8492
})
8593
}

pkg/cloudprovider/vsphereparavirtual/vmservice/types.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ type VMService interface {
4141

4242
// vmService takes care of mapping of LB type of service to VM service in supervisor cluster
4343
type vmService struct {
44-
vmClient vmop.Interface
45-
namespace string
46-
ownerReference *metav1.OwnerReference
44+
vmClient vmop.Interface
45+
namespace string
46+
ownerReference *metav1.OwnerReference
47+
serviceAnnotationPropagationEnabled bool
4748
}

pkg/cloudprovider/vsphereparavirtual/vmservice/vmservice.go

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/hex"
2323
"fmt"
2424
"reflect"
25+
"slices"
2526
"strconv"
2627

2728
"github.com/pkg/errors"
@@ -65,11 +66,21 @@ const (
6566
// configuration to the supervisor cluster.
6667
AnnotationServiceHealthCheckNodePortKey = "virtualmachineservice.vmoperator.vmware.com/service.healthCheckNodePort"
6768

69+
// AnnotationLastAppliedConfiguration is used by kubectl as a legacy mechanism to track changes.
70+
// That mechanism has been superseded by Server-side apply.
71+
AnnotationLastAppliedConfiguration = "kubectl.kubernetes.io/last-applied-configuration"
72+
6873
// MaxCheckSumLen is the maximum length of vmservice suffix: vsphere paravirtual name length cannot exceed 41 bytes in total, so we need to make sure vmservice suffix is 21 bytes (63 - 41 -1 = 21)
6974
// https://gitlab.eng.vmware.com/core-build/guest-cluster-controller/blob/master/webhooks/validation/tanzukubernetescluster_validator.go#L56
7075
MaxCheckSumLen = 21
7176
)
7277

78+
var excludedAnnotations = []string{
79+
AnnotationLastAppliedConfiguration,
80+
AnnotationServiceExternalTrafficPolicyKey,
81+
AnnotationServiceHealthCheckNodePortKey,
82+
}
83+
7384
// A list of possible error messages
7485
var (
7586
ErrCreateVMService = errors.New("failed to create VirtualMachineService")
@@ -93,11 +104,12 @@ func GetVmopClient(config *rest.Config) (vmop.Interface, error) {
93104
}
94105

95106
// NewVMService creates a vmService object
96-
func NewVMService(vmClient vmop.Interface, ns string, ownerRef *metav1.OwnerReference) VMService {
107+
func NewVMService(vmClient vmop.Interface, ns string, ownerRef *metav1.OwnerReference, serviceAnnotationPropagationEnabled bool) VMService {
97108
return &vmService{
98-
vmClient: vmClient,
99-
namespace: ns,
100-
ownerReference: ownerRef,
109+
vmClient: vmClient,
110+
namespace: ns,
111+
ownerReference: ownerRef,
112+
serviceAnnotationPropagationEnabled: serviceAnnotationPropagationEnabled,
101113
}
102114
}
103115

@@ -226,7 +238,7 @@ func (s *vmService) Update(ctx context.Context, service *v1.Service, clusterName
226238
service.Spec.LoadBalancerSourceRanges = []string{}
227239
}
228240

229-
annotations := getVMServiceAnnotations(vmService, service)
241+
annotations := getVMServiceAnnotations(vmService, service, s.serviceAnnotationPropagationEnabled)
230242

231243
// VMService only has a few fields to be kept in sync so we will simply
232244
// iterate over them
@@ -342,14 +354,14 @@ func (s *vmService) lbServiceToVMService(service *v1.Service, clusterName string
342354
Spec: vmServiceSpec,
343355
}
344356

345-
if annotations := getVMServiceAnnotations(vmService, service); len(annotations) != 0 {
357+
if annotations := getVMServiceAnnotations(vmService, service, s.serviceAnnotationPropagationEnabled); len(annotations) != 0 {
346358
vmService.Annotations = annotations
347359
}
348360

349361
return vmService, nil
350362
}
351363

352-
func getVMServiceAnnotations(vmService *vmopv1.VirtualMachineService, service *v1.Service) map[string]string {
364+
func getVMServiceAnnotations(vmService *vmopv1.VirtualMachineService, service *v1.Service, serviceAnnotationPropagationEnabled bool) map[string]string {
353365
var annotations map[string]string
354366
// When ExternalTrafficPolicy is set to Local in the Service, add its
355367
// value and the healthCheckNodePort to VirtualMachineService
@@ -362,6 +374,21 @@ func getVMServiceAnnotations(vmService *vmopv1.VirtualMachineService, service *v
362374
annotations[AnnotationServiceExternalTrafficPolicyKey] = string(service.Spec.ExternalTrafficPolicy)
363375
annotations[AnnotationServiceHealthCheckNodePortKey] = strconv.Itoa(int(service.Spec.HealthCheckNodePort))
364376
}
377+
378+
// Annotation propagation logic
379+
if serviceAnnotationPropagationEnabled {
380+
// Initialize annotations map if empty
381+
if annotations == nil {
382+
annotations = make(map[string]string)
383+
}
384+
// Merge service annotations
385+
for k, v := range service.Annotations {
386+
if !slices.Contains(excludedAnnotations, k) {
387+
annotations[k] = v
388+
}
389+
}
390+
}
391+
365392
return annotations
366393
}
367394

0 commit comments

Comments
 (0)