diff --git a/test/integration/service/loadbalancer_test.go b/test/integration/service/loadbalancer_test.go index 1c2fd8ee92b..7951cfa4afc 100644 --- a/test/integration/service/loadbalancer_test.go +++ b/test/integration/service/loadbalancer_test.go @@ -18,12 +18,15 @@ package service import ( "context" + "encoding/json" "reflect" "testing" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" @@ -287,6 +290,79 @@ func Test_ServiceLoadBalancerDisableAllocatedNodePorts(t *testing.T) { } } +// Test_ServiceLoadBalancerDisableAllocatedNodePortsByPatch test that switching a Service +// to spec.allocateLoadBalancerNodePorts=false with path can de-allocate one of existing node ports. +func Test_ServiceLoadBalancerDisableAllocatedNodePortsByPatch(t *testing.T) { + server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) + defer server.TearDownFn() + + client, err := clientset.NewForConfig(server.ClientConfig) + if err != nil { + t.Fatalf("Error creating clientset: %v", err) + } + + ns := framework.CreateNamespaceOrDie(client, "test-service-deallocate-node-ports", t) + defer framework.DeleteNamespaceOrDie(client, ns, t) + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-123", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + AllocateLoadBalancerNodePorts: utilpointer.Bool(true), + Ports: []corev1.ServicePort{{ + Name: "np-1", + Port: int32(80), + }, { + Name: "np-2", + Port: int32(81), + }}, + Selector: map[string]string{ + "foo": "bar", + }, + }, + } + + service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating test service: %v", err) + } + + if !serviceHasNodePorts(service) { + t.Error("expected node ports but found none") + } + + clone := service.DeepCopy() + clone.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false) + clone.Spec.Ports[0].NodePort = 0 + + oldData, err := json.Marshal(service) + if err != nil { + t.Fatalf("Error marshalling test service: %v", err) + } + newData, err := json.Marshal(clone) + if err != nil { + t.Fatalf("Error marshalling test service: %v", err) + } + patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, corev1.Service{}) + if err != nil { + t.Fatalf("Error creating patch: %v", err) + } + + service, err = client.CoreV1().Services(ns.Name).Patch(context.TODO(), service.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + t.Fatalf("Error updating test service: %v", err) + } + + if service.Spec.Ports[0].NodePort != 0 { + t.Error("node ports[0] was expected to be deallocated") + } + if service.Spec.Ports[1].NodePort == 0 { + t.Error("node ports was not expected to be deallocated") + } +} + // Test_ServiceLoadBalancerDisableThenEnableAllocatedNodePorts test that switching a Service // to spec.allocateLoadBalancerNodePorts=true from false, allocate new node ports. func Test_ServiceLoadBalancerDisableThenEnableAllocatedNodePorts(t *testing.T) {