Skip to content

Commit 97e9bd1

Browse files
[INF-12851] Make sure service updates preserve lb class (#3641)
When Service object is updated, ensure that load balancer class is preserved. Co-authored-by: Jack Andersen <[email protected]>
1 parent 33838dd commit 97e9bd1

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

webhooks/core/service_mutator.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,41 @@ func (m *serviceMutator) MutateCreate(ctx context.Context, obj runtime.Object) (
5252
}
5353

5454
func (m *serviceMutator) MutateUpdate(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
55-
return obj, nil
55+
// this mutator only cares about Service objects
56+
newSvc, ok := obj.(*corev1.Service)
57+
if !ok {
58+
return obj, nil
59+
}
60+
61+
oldSvc, ok := oldObj.(*corev1.Service)
62+
if !ok {
63+
return obj, nil
64+
}
65+
66+
if newSvc.Spec.Type != corev1.ServiceTypeLoadBalancer {
67+
return obj, nil
68+
}
69+
70+
// does the old Service object have spec.loadBalancerClass?
71+
if oldSvc.Spec.LoadBalancerClass != nil && *oldSvc.Spec.LoadBalancerClass != "" {
72+
// if so, let's inspect the incoming object for the same field
73+
74+
// does the new Service object lack spec.loadBalancerClass?
75+
// if so, set it to the old value
76+
// if yes, then leave it be because someone wanted it that way, let the user deal with the error
77+
if newSvc.Spec.LoadBalancerClass == nil || *newSvc.Spec.LoadBalancerClass == "" {
78+
newSvc.Spec.LoadBalancerClass = oldSvc.Spec.LoadBalancerClass
79+
80+
m.logger.Info("preserved loadBalancerClass", "service", newSvc.Name, "loadBalancerClass", *newSvc.Spec.LoadBalancerClass)
81+
return newSvc, nil
82+
}
83+
84+
m.logger.Info("service already has loadBalancerClass, skipping", "service", newSvc.Name, "loadBalancerClass", *newSvc.Spec.LoadBalancerClass)
85+
return newSvc, nil
86+
}
87+
88+
m.logger.Info("service did not originally have a loadBalancerClass, skipping", "service", newSvc.Name)
89+
return newSvc, nil
5690
}
5791

5892
// +kubebuilder:webhook:path=/mutate-v1-service,mutating=true,failurePolicy=fail,groups="",resources=services,verbs=create,versions=v1,name=mservice.elbv2.k8s.aws,sideEffects=None,webhookVersions=v1,admissionReviewVersions=v1beta1

webhooks/core/service_mutator_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package core
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
corev1 "k8s.io/api/core/v1"
9+
)
10+
11+
func TestMutateUpdate_WhenServiceIsNotLoadBalancer(t *testing.T) {
12+
m := &serviceMutator{}
13+
svc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeClusterIP}}
14+
_, err := m.MutateUpdate(context.Background(), svc, svc)
15+
assert.NoError(t, err)
16+
17+
assert.Nil(t, svc.Spec.LoadBalancerClass)
18+
}
19+
20+
func TestMutateUpdate_WhenOldServiceHasLoadBalancerClassAndNewServiceDoesNot(t *testing.T) {
21+
m := &serviceMutator{}
22+
oldSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer, LoadBalancerClass: stringPtr("old-class")}}
23+
newSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}}
24+
_, err := m.MutateUpdate(context.Background(), newSvc, oldSvc)
25+
assert.NoError(t, err)
26+
assert.Equal(t, "old-class", *newSvc.Spec.LoadBalancerClass)
27+
}
28+
29+
func TestMutateUpdate_WhenOldServiceHasLoadBalancerClassAndNewServiceHasDifferent(t *testing.T) {
30+
m := &serviceMutator{}
31+
oldSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer, LoadBalancerClass: stringPtr("old-class")}}
32+
newSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer, LoadBalancerClass: stringPtr("new-class")}}
33+
_, err := m.MutateUpdate(context.Background(), newSvc, oldSvc)
34+
assert.NoError(t, err)
35+
assert.Equal(t, "new-class", *newSvc.Spec.LoadBalancerClass)
36+
}
37+
38+
func TestMutateUpdate_WhenOldServiceDoesNotHaveLoadBalancerClass(t *testing.T) {
39+
m := &serviceMutator{}
40+
oldSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}}
41+
newSvc := &corev1.Service{Spec: corev1.ServiceSpec{Type: corev1.ServiceTypeLoadBalancer}}
42+
_, err := m.MutateUpdate(context.Background(), newSvc, oldSvc)
43+
assert.NoError(t, err)
44+
45+
assert.Nil(t, newSvc.Spec.LoadBalancerClass)
46+
}
47+
48+
func stringPtr(s string) *string {
49+
return &s
50+
}

0 commit comments

Comments
 (0)