Skip to content

Commit 28cdaad

Browse files
authored
Merge pull request #1457 from silvery1622/service-annotation-propagation
Add support for service annotation propagation
2 parents ef42075 + 9586e38 commit 28cdaad

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)