diff --git a/pkg/api/service/util_test.go b/pkg/api/service/util_test.go index c1e6c7fb4d4..e0508f2f8ca 100644 --- a/pkg/api/service/util_test.go +++ b/pkg/api/service/util_test.go @@ -19,10 +19,15 @@ package service import ( "testing" + "fmt" + "reflect" "strings" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/api" netsets "k8s.io/kubernetes/pkg/util/net/sets" + + "github.com/davecgh/go-spew/spew" ) func TestGetLoadBalancerSourceRanges(t *testing.T) { @@ -129,3 +134,385 @@ func TestAllowAll(t *testing.T) { checkAllowAll(true, "192.168.0.0/0") checkAllowAll(true, "192.168.0.1/32", "0.0.0.0/0") } + +func TestRequestsOnlyLocalTraffic(t *testing.T) { + checkRequestsOnlyLocalTraffic := func(requestsOnlyLocalTraffic bool, service *api.Service) { + res := RequestsOnlyLocalTraffic(service) + if res != requestsOnlyLocalTraffic { + t.Errorf("Expected requests OnlyLocal traffic = %v, got %v", + requestsOnlyLocalTraffic, res) + } + } + + checkRequestsOnlyLocalTraffic(false, &api.Service{}) + checkRequestsOnlyLocalTraffic(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + }) + checkRequestsOnlyLocalTraffic(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + }, + }) + checkRequestsOnlyLocalTraffic(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkRequestsOnlyLocalTraffic(true, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + checkRequestsOnlyLocalTraffic(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkRequestsOnlyLocalTraffic(true, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + }, + }) +} + +func TestNeedsHealthCheck(t *testing.T) { + checkNeedsHealthCheck := func(needsHealthCheck bool, service *api.Service) { + res := NeedsHealthCheck(service) + if res != needsHealthCheck { + t.Errorf("Expected needs health check = %v, got %v", + needsHealthCheck, res) + } + } + + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + }) + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkNeedsHealthCheck(true, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: "invalid", + }, + }, + }) + checkNeedsHealthCheck(false, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }) + checkNeedsHealthCheck(true, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }) +} + +func TestGetServiceHealthCheckNodePort(t *testing.T) { + checkGetServiceHealthCheckNodePort := func(healthCheckNodePort int32, service *api.Service) { + res := GetServiceHealthCheckNodePort(service) + if res != healthCheckNodePort { + t.Errorf("Expected health check node port = %v, got %v", + healthCheckNodePort, res) + } + } + + checkGetServiceHealthCheckNodePort(0, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + }) + checkGetServiceHealthCheckNodePort(0, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkGetServiceHealthCheckNodePort(0, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkGetServiceHealthCheckNodePort(34567, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + HealthCheckNodePort: int32(34567), + }, + }) + checkGetServiceHealthCheckNodePort(34567, &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + BetaAnnotationHealthCheckNodePort: "34567", + }, + }, + }) +} + +func TestSetDefaultExternalTrafficPolicyIfNeeded(t *testing.T) { + testCases := []struct { + inputService *api.Service + expectedService *api.Service + }{ + // First class fields cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + }, + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + }, + }, + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeNodePort, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + }, + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + }, + }, + // Beta annotations cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + }, + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + }, + } + + for i, tc := range testCases { + SetDefaultExternalTrafficPolicyIfNeeded(tc.inputService) + if !reflect.DeepEqual(tc.inputService, tc.expectedService) { + t.Errorf("%v: got unexpected service", i) + spew.Dump(tc) + } + } +} + +func TestClearExternalTrafficPolicy(t *testing.T) { + testCases := []struct { + inputService *api.Service + }{ + // First class fields cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + // Beta annotations cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + }, + } + + for i, tc := range testCases { + ClearExternalTrafficPolicy(tc.inputService) + if _, ok := tc.inputService.Annotations[BetaAnnotationExternalTraffic]; ok || + tc.inputService.Spec.ExternalTrafficPolicy != "" { + t.Errorf("%v: failed to clear ExternalTrafficPolicy", i) + spew.Dump(tc) + } + } +} + +func TestSetServiceHealthCheckNodePort(t *testing.T) { + testCases := []struct { + inputService *api.Service + hcNodePort int32 + beta bool + }{ + // First class fields cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + 30012, + false, + }, + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + 0, + false, + }, + // Beta annotations cases. + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + 30012, + true, + }, + { + &api.Service{ + Spec: api.ServiceSpec{ + Type: api.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + 0, + true, + }, + } + + for i, tc := range testCases { + SetServiceHealthCheckNodePort(tc.inputService, tc.hcNodePort) + if !tc.beta { + if tc.inputService.Spec.HealthCheckNodePort != tc.hcNodePort { + t.Errorf("%v: got HealthCheckNodePort %v, want %v", i, tc.inputService.Spec.HealthCheckNodePort, tc.hcNodePort) + } + } else { + l, ok := tc.inputService.Annotations[BetaAnnotationHealthCheckNodePort] + if tc.hcNodePort == 0 { + if ok { + t.Errorf("%v: HealthCheckNodePort set, want it to be cleared", i) + } + } else { + if !ok { + t.Errorf("%v: HealthCheckNodePort unset, want %v", i, tc.hcNodePort) + } else if l != fmt.Sprintf("%v", tc.hcNodePort) { + t.Errorf("%v: got HealthCheckNodePort %v, want %v", i, l, tc.hcNodePort) + } + } + } + } +} diff --git a/pkg/api/v1/service/util_test.go b/pkg/api/v1/service/util_test.go index 28572f3c829..1149dc1f37a 100644 --- a/pkg/api/v1/service/util_test.go +++ b/pkg/api/v1/service/util_test.go @@ -19,10 +19,15 @@ package service import ( "testing" + "fmt" + "reflect" "strings" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/api/v1" netsets "k8s.io/kubernetes/pkg/util/net/sets" + + "github.com/davecgh/go-spew/spew" ) func TestGetLoadBalancerSourceRanges(t *testing.T) { @@ -129,3 +134,385 @@ func TestAllowAll(t *testing.T) { checkAllowAll(true, "192.168.0.0/0") checkAllowAll(true, "192.168.0.1/32", "0.0.0.0/0") } + +func TestRequestsOnlyLocalTraffic(t *testing.T) { + checkRequestsOnlyLocalTraffic := func(requestsOnlyLocalTraffic bool, service *v1.Service) { + res := RequestsOnlyLocalTraffic(service) + if res != requestsOnlyLocalTraffic { + t.Errorf("Expected requests OnlyLocal traffic = %v, got %v", + requestsOnlyLocalTraffic, res) + } + } + + checkRequestsOnlyLocalTraffic(false, &v1.Service{}) + checkRequestsOnlyLocalTraffic(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + }) + checkRequestsOnlyLocalTraffic(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + }, + }) + checkRequestsOnlyLocalTraffic(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkRequestsOnlyLocalTraffic(true, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + checkRequestsOnlyLocalTraffic(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkRequestsOnlyLocalTraffic(true, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal, + }, + }) +} + +func TestNeedsHealthCheck(t *testing.T) { + checkNeedsHealthCheck := func(needsHealthCheck bool, service *v1.Service) { + res := NeedsHealthCheck(service) + if res != needsHealthCheck { + t.Errorf("Expected needs health check = %v, got %v", + needsHealthCheck, res) + } + } + + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + }) + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkNeedsHealthCheck(true, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal, + }, + }) + + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: "invalid", + }, + }, + }) + checkNeedsHealthCheck(false, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }) + checkNeedsHealthCheck(true, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }) +} + +func TestGetServiceHealthCheckNodePort(t *testing.T) { + checkGetServiceHealthCheckNodePort := func(healthCheckNodePort int32, service *v1.Service) { + res := GetServiceHealthCheckNodePort(service) + if res != healthCheckNodePort { + t.Errorf("Expected health check node port = %v, got %v", + healthCheckNodePort, res) + } + } + + checkGetServiceHealthCheckNodePort(0, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + }) + checkGetServiceHealthCheckNodePort(0, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkGetServiceHealthCheckNodePort(0, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }) + checkGetServiceHealthCheckNodePort(34567, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal, + HealthCheckNodePort: int32(34567), + }, + }) + checkGetServiceHealthCheckNodePort(34567, &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + BetaAnnotationHealthCheckNodePort: "34567", + }, + }, + }) +} + +func TestSetDefaultExternalTrafficPolicyIfNeeded(t *testing.T) { + testCases := []struct { + inputService *v1.Service + expectedService *v1.Service + }{ + // First class fields cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + }, + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + }, + }, + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeNodePort, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + }, + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + }, + }, + // Beta annotations cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + }, + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + }, + } + + for i, tc := range testCases { + SetDefaultExternalTrafficPolicyIfNeeded(tc.inputService) + if !reflect.DeepEqual(tc.inputService, tc.expectedService) { + t.Errorf("%v: got unexpected service", i) + spew.Dump(tc) + } + } +} + +func TestClearExternalTrafficPolicy(t *testing.T) { + testCases := []struct { + inputService *v1.Service + }{ + // First class fields cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + }, + // Beta annotations cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficLocal, + }, + }, + }, + }, + } + + for i, tc := range testCases { + ClearExternalTrafficPolicy(tc.inputService) + if _, ok := tc.inputService.Annotations[BetaAnnotationExternalTraffic]; ok || + tc.inputService.Spec.ExternalTrafficPolicy != "" { + t.Errorf("%v: failed to clear ExternalTrafficPolicy", i) + spew.Dump(tc) + } + } +} + +func TestSetServiceHealthCheckNodePort(t *testing.T) { + testCases := []struct { + inputService *v1.Service + hcNodePort int32 + beta bool + }{ + // First class fields cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + 30012, + false, + }, + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeGlobal, + }, + }, + 0, + false, + }, + // Beta annotations cases. + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + 30012, + true, + }, + { + &v1.Service{ + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + BetaAnnotationExternalTraffic: AnnotationValueExternalTrafficGlobal, + }, + }, + }, + 0, + true, + }, + } + + for i, tc := range testCases { + SetServiceHealthCheckNodePort(tc.inputService, tc.hcNodePort) + if !tc.beta { + if tc.inputService.Spec.HealthCheckNodePort != tc.hcNodePort { + t.Errorf("%v: got HealthCheckNodePort %v, want %v", i, tc.inputService.Spec.HealthCheckNodePort, tc.hcNodePort) + } + } else { + l, ok := tc.inputService.Annotations[BetaAnnotationHealthCheckNodePort] + if tc.hcNodePort == 0 { + if ok { + t.Errorf("%v: HealthCheckNodePort set, want it to be cleared", i) + } + } else { + if !ok { + t.Errorf("%v: HealthCheckNodePort unset, want %v", i, tc.hcNodePort) + } else if l != fmt.Sprintf("%v", tc.hcNodePort) { + t.Errorf("%v: got HealthCheckNodePort %v, want %v", i, l, tc.hcNodePort) + } + } + } + } +} diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 1cb675a938e..0e0352f084c 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -5656,9 +5656,19 @@ func TestValidateService(t *testing.T) { numErrs: 1, }, { - name: "LoadBalancer allows onlyLocal alpha annotations", + name: "invalid node port with clusterIP None", tweakSvc: func(s *api.Service) { - s.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + s.Spec.Type = api.ServiceTypeNodePort + s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)}) + s.Spec.ClusterIP = "None" + }, + numErrs: 1, + }, + // ESIPP section begins. + { + name: "LoadBalancer allows onlyLocal beta annotations", + tweakSvc: func(s *api.Service) { + s.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal }, numErrs: 0, }, @@ -5689,14 +5699,68 @@ func TestValidateService(t *testing.T) { numErrs: 1, }, { - name: "invalid node port with clusterIP None", + name: "valid healthCheckNodePort beta annotation", tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)}) - s.Spec.ClusterIP = "None" + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + s.Annotations[service.BetaAnnotationHealthCheckNodePort] = "31100" + }, + numErrs: 0, + }, + { + name: "invalid externalTraffic field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = "invalid" }, numErrs: 1, }, + { + name: "nagative healthCheckNodePort field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Spec.HealthCheckNodePort = -1 + }, + numErrs: 1, + }, + { + name: "nagative healthCheckNodePort field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Spec.HealthCheckNodePort = 31100 + }, + numErrs: 0, + }, + { + name: "disallows use ExternalTraffic beta annotation with first class field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + s.Spec.HealthCheckNodePort = 3001 + }, + numErrs: 1, + }, + { + name: "disallows duplicated ExternalTraffic beta annotation with first class field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + }, + numErrs: 1, + }, + { + name: "disallows use HealthCheckNodePort beta annotation with first class field", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Annotations[service.BetaAnnotationHealthCheckNodePort] = "3001" + }, + numErrs: 1, + }, + // ESIPP section ends. } for _, tc := range testCases { @@ -5709,6 +5773,75 @@ func TestValidateService(t *testing.T) { } } +func TestValidateServiceExternalTrafficFieldsCombination(t *testing.T) { + testCases := []struct { + name string + tweakSvc func(svc *api.Service) // Given a basic valid service, each test case can customize it. + numErrs int + }{ + { + name: "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Spec.HealthCheckNodePort = 34567 + }, + numErrs: 0, + }, + { + name: "valid nodePort service with externalTrafficPolicy set", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeNodePort + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + }, + numErrs: 0, + }, + { + name: "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeClusterIP + }, + numErrs: 0, + }, + { + name: "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeLoadBalancer + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeGlobal + s.Spec.HealthCheckNodePort = 34567 + }, + numErrs: 1, + }, + { + name: "cannot set healthCheckNodePort field on nodePort service", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeNodePort + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Spec.HealthCheckNodePort = 34567 + }, + numErrs: 1, + }, + { + name: "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeClusterIP + s.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + s.Spec.HealthCheckNodePort = 34567 + }, + numErrs: 2, + }, + } + + for _, tc := range testCases { + svc := makeValidService() + tc.tweakSvc(&svc) + errs := ValidateServiceExternalTrafficFieldsCombination(&svc) + if len(errs) != tc.numErrs { + t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate()) + } + } +} + func TestValidateReplicationControllerStatus(t *testing.T) { tests := []struct { name string @@ -7088,40 +7221,46 @@ func TestValidateServiceUpdate(t *testing.T) { numErrs: 1, }, { - name: "Service allows removing onlyLocal alpha annotations", + name: "Service allows removing onlyLocal beta annotations", tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal - oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = "3001" + oldSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = "3001" }, numErrs: 0, }, { - name: "Service allows modifying onlyLocal alpha annotations", + name: "Service allows modifying onlyLocal beta annotations", tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal - oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = "3001" - newSvc.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficGlobal - newSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] - }, - numErrs: 0, - }, - { - name: "Service disallows promoting one of the onlyLocal pair to beta", - tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal - oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = "3001" + oldSvc.Spec.Type = api.ServiceTypeLoadBalancer + oldSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = "3001" + newSvc.Spec.Type = api.ServiceTypeLoadBalancer newSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficGlobal - newSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] + newSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] + }, + numErrs: 0, + }, + { + name: "Service disallows promoting one of the onlyLocal pair to GA", + tweakSvc: func(oldSvc, newSvc *api.Service) { + oldSvc.Spec.Type = api.ServiceTypeLoadBalancer + oldSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = "3001" + newSvc.Spec.Type = api.ServiceTypeLoadBalancer + newSvc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + newSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] }, numErrs: 1, }, { - name: "Service allows changing both onlyLocal annotations from alpha to beta", + name: "Service allows changing both onlyLocal annotations from beta to GA", tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Annotations[service.AlphaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal - oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] = "3001" - newSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal - newSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = oldSvc.Annotations[service.AlphaAnnotationHealthCheckNodePort] + oldSvc.Spec.Type = api.ServiceTypeLoadBalancer + oldSvc.Annotations[service.BetaAnnotationExternalTraffic] = service.AnnotationValueExternalTrafficLocal + oldSvc.Annotations[service.BetaAnnotationHealthCheckNodePort] = "3001" + newSvc.Spec.Type = api.ServiceTypeLoadBalancer + newSvc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal + newSvc.Spec.HealthCheckNodePort = 3001 }, numErrs: 0, }, diff --git a/pkg/registry/core/service/rest_test.go b/pkg/registry/core/service/rest_test.go index de1c993efc7..d7f7815297e 100644 --- a/pkg/registry/core/service/rest_test.go +++ b/pkg/registry/core/service/rest_test.go @@ -959,13 +959,48 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) { } } -// Validate allocation of a nodePort when the externalTraffic=OnlyLocal annotation is set -// and type is LoadBalancer -func TestServiceRegistryExternalTrafficAnnotationHealthCheckNodePortAllocation(t *testing.T) { +// Validate allocation of a nodePort when ExternalTrafficPolicy is set to Local +// and type is LoadBalancer. +func TestServiceRegistryExternalTrafficHealthCheckNodePortAllocation(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", + ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp"}, + Spec: api.ServiceSpec{ + Selector: map[string]string{"bar": "baz"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeLoadBalancer, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: intstr.FromInt(6502), + }}, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + }, + } + created_svc, err := storage.Create(ctx, svc) + if created_svc == nil || err != nil { + t.Errorf("Unexpected failure creating service %v", err) + } + created_service := created_svc.(*api.Service) + if !service.NeedsHealthCheck(created_service) { + t.Errorf("Expecting health check needed, returned health check not needed instead") + } + port := service.GetServiceHealthCheckNodePort(created_service) + if port == 0 { + t.Errorf("Failed to allocate health check node port and set the HealthCheckNodePort") + } + +} + +// Validate allocation of a nodePort when ExternalTraffic beta annotation is set to OnlyLocal +// and type is LoadBalancer. +func TestServiceRegistryExternalTrafficHealthCheckNodePortAllocationBeta(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + storage, _ := NewTestREST(t, nil) + svc := &api.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficLocal, }, @@ -987,22 +1022,60 @@ func TestServiceRegistryExternalTrafficAnnotationHealthCheckNodePortAllocation(t } created_service := created_svc.(*api.Service) if !service.NeedsHealthCheck(created_service) { - t.Errorf("Unexpected missing annotation %s", service.BetaAnnotationExternalTraffic) + t.Errorf("Expecting health check needed, returned health check not needed instead") } port := service.GetServiceHealthCheckNodePort(created_service) if port == 0 { - t.Errorf("Failed to allocate and create the health check node port annotation %s", service.BetaAnnotationHealthCheckNodePort) + t.Errorf("Failed to allocate health check node port and set the HealthCheckNodePort") } } -// Validate using the user specified nodePort when the externalTraffic=OnlyLocal annotation is set -// and type is LoadBalancer -func TestServiceRegistryExternalTrafficBetaAnnotationHealthCheckNodePortUserAllocation(t *testing.T) { +// Validate using the user specified nodePort when ExternalTrafficPolicy is set to Local +// and type is LoadBalancer. +func TestServiceRegistryExternalTrafficHealthCheckNodePortUserAllocation(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", + ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp"}, + Spec: api.ServiceSpec{ + Selector: map[string]string{"bar": "baz"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeLoadBalancer, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: intstr.FromInt(6502), + }}, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + HealthCheckNodePort: int32(30200), + }, + } + created_svc, err := storage.Create(ctx, svc) + if created_svc == nil || err != nil { + t.Fatalf("Unexpected failure creating service :%v", err) + } + created_service := created_svc.(*api.Service) + if !service.NeedsHealthCheck(created_service) { + t.Errorf("Expecting health check needed, returned health check not needed instead") + } + port := service.GetServiceHealthCheckNodePort(created_service) + if port == 0 { + t.Errorf("Failed to allocate health check node port and set the HealthCheckNodePort") + } + if port != 30200 { + t.Errorf("Failed to allocate requested nodePort expected 30200, got %d", port) + } +} + +// Validate using the user specified nodePort when ExternalTraffic beta annotation is set to OnlyLocal +// and type is LoadBalancer. +func TestServiceRegistryExternalTrafficHealthCheckNodePortUserAllocationBeta(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + storage, _ := NewTestREST(t, nil) + svc := &api.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficLocal, service.BetaAnnotationHealthCheckNodePort: "30200", @@ -1021,27 +1094,54 @@ func TestServiceRegistryExternalTrafficBetaAnnotationHealthCheckNodePortUserAllo } created_svc, err := storage.Create(ctx, svc) if created_svc == nil || err != nil { - t.Fatalf("Unexpected failure creating service: %v", err) + t.Fatalf("Unexpected failure creating service :%v", err) } created_service := created_svc.(*api.Service) if !service.NeedsHealthCheck(created_service) { - t.Errorf("Unexpected missing annotation %s", service.BetaAnnotationExternalTraffic) + t.Errorf("Expecting health check needed, returned health check not needed instead") } port := service.GetServiceHealthCheckNodePort(created_service) if port == 0 { - t.Errorf("Failed to allocate and create the health check node port annotation %s", service.BetaAnnotationHealthCheckNodePort) + t.Errorf("Failed to allocate health check node port and set the HealthCheckNodePort") } if port != 30200 { t.Errorf("Failed to allocate requested nodePort expected 30200, got %d", port) } } -// Validate that the service creation fails when the requested port number is -1 -func TestServiceRegistryExternalTrafficAnnotationNegative(t *testing.T) { +// Validate that the service creation fails when the requested port number is -1. +func TestServiceRegistryExternalTrafficHealthCheckNodePortNegative(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", + ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp"}, + Spec: api.ServiceSpec{ + Selector: map[string]string{"bar": "baz"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeLoadBalancer, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: intstr.FromInt(6502), + }}, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal, + HealthCheckNodePort: int32(-1), + }, + } + created_svc, err := storage.Create(ctx, svc) + if created_svc == nil || err != nil { + return + } + t.Errorf("Unexpected creation of service with invalid HealthCheckNodePort specified") +} + +// Validate that the service creation fails when the requested port number in beta annotation is -1. +func TestServiceRegistryExternalTrafficHealthCheckNodePortNegativeBeta(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + storage, _ := NewTestREST(t, nil) + svc := &api.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficLocal, service.BetaAnnotationHealthCheckNodePort: "-1", @@ -1062,15 +1162,49 @@ func TestServiceRegistryExternalTrafficAnnotationNegative(t *testing.T) { if created_svc == nil || err != nil { return } - t.Errorf("Unexpected creation of service with invalid healthCheckNodePort specified") + t.Errorf("Unexpected creation of service with invalid HealthCheckNodePort specified") } -// Validate that the health check nodePort is not allocated when the externalTraffic annotation is !"OnlyLocal" -func TestServiceRegistryExternalTrafficAnnotationGlobal(t *testing.T) { +// Validate that the health check nodePort is not allocated when ExternalTrafficPolicy is set to Global. +func TestServiceRegistryExternalTrafficGlobal(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ - ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", + ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp"}, + Spec: api.ServiceSpec{ + Selector: map[string]string{"bar": "baz"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeLoadBalancer, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: intstr.FromInt(6502), + }}, + ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeGlobal, + }, + } + created_svc, err := storage.Create(ctx, svc) + if created_svc == nil || err != nil { + t.Errorf("Unexpected failure creating service %v", err) + } + created_service := created_svc.(*api.Service) + if service.NeedsHealthCheck(created_service) { + t.Errorf("Expecting health check not needed, returned health check needed instead") + } + // Make sure the service does not have the health check node port allocated + port := service.GetServiceHealthCheckNodePort(created_service) + if port != 0 { + t.Errorf("Unexpected allocation of health check node port: %v", port) + } +} + +// Validate that the health check nodePort is not allocated when ExternalTraffic beta annotation is set to Global. +func TestServiceRegistryExternalTrafficGlobalBeta(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + storage, _ := NewTestREST(t, nil) + svc := &api.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficGlobal, }, @@ -1091,14 +1225,47 @@ func TestServiceRegistryExternalTrafficAnnotationGlobal(t *testing.T) { t.Errorf("Unexpected failure creating service %v", err) } created_service := created_svc.(*api.Service) - // Make sure the service does not have the annotation if service.NeedsHealthCheck(created_service) { - t.Errorf("Unexpected value for annotation %s", service.BetaAnnotationExternalTraffic) + t.Errorf("Expecting health check not needed, returned health check needed instead") } // Make sure the service does not have the health check node port allocated port := service.GetServiceHealthCheckNodePort(created_service) if port != 0 { - t.Errorf("Unexpected allocation of health check node port annotation %s", service.BetaAnnotationHealthCheckNodePort) + t.Errorf("Unexpected allocation of health check node port: %v", port) + } +} + +// Validate that ExternalTraffic is default to Global for loadBalancer service. +func TestServiceRegistryExternalTrafficDefaultGlobal(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + storage, _ := NewTestREST(t, nil) + svc := &api.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp"}, + Spec: api.ServiceSpec{ + Selector: map[string]string{"bar": "baz"}, + SessionAffinity: api.ServiceAffinityNone, + Type: api.ServiceTypeLoadBalancer, + Ports: []api.ServicePort{{ + Port: 6502, + Protocol: api.ProtocolTCP, + TargetPort: intstr.FromInt(6502), + }}, + }, + } + created_svc, err := storage.Create(ctx, svc) + if created_svc == nil || err != nil { + t.Errorf("Unexpected failure creating service %v", err) + } + created_service := created_svc.(*api.Service) + if service.NeedsHealthCheck(created_service) { + t.Errorf("Expecting health check not needed, returned health check needed instead") + } + // Make sure the service does not have the health check node port allocated + if port := service.GetServiceHealthCheckNodePort(created_service); port != 0 { + t.Errorf("Unexpected allocation of health check node port: %v", port) + } + if created_service.Spec.ExternalTrafficPolicy != api.ServiceExternalTrafficPolicyTypeGlobal { + t.Errorf("Expecting externalTraffic to be %v, got:%v", api.ServiceExternalTrafficPolicyTypeGlobal, created_service.Spec.ExternalTrafficPolicy) } }