From 2197c8da5a0477486596e2856c5d7e9c056e8e5a Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 22 May 2015 17:54:19 -0400 Subject: [PATCH] Add NodePort to ServicePort We prevent it from being set by validation --- pkg/api/types.go | 4 ++++ pkg/api/v1/conversion_generated.go | 2 ++ pkg/api/v1/types.go | 4 ++++ pkg/api/v1beta1/conversion.go | 2 ++ pkg/api/v1beta1/types.go | 4 ++++ pkg/api/v1beta2/conversion.go | 2 ++ pkg/api/v1beta2/types.go | 4 ++++ pkg/api/v1beta3/conversion_generated.go | 2 ++ pkg/api/v1beta3/types.go | 4 ++++ pkg/api/validation/validation.go | 22 ++++++++++++++++++++++ pkg/api/validation/validation_test.go | 16 ++++++++++++++++ 11 files changed, 66 insertions(+) diff --git a/pkg/api/types.go b/pkg/api/types.go index 02fefea78bc..2eea3ede178 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1102,6 +1102,10 @@ type ServicePort struct { // of v1beta3 the default value is the sames as the Port field (an // identity map). TargetPort util.IntOrString `json:"targetPort"` + + // The port on each node on which this service is exposed. + // Default is to auto-allocate a port if the visibility of this Service requires one. + NodePort int `json:"nodePort" description:"the port on each node on which this service is exposed"` } // Service is a named abstraction of software service (for example, mysql) consisting of local port diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go index 7caa4e574ca..154ddec2e66 100644 --- a/pkg/api/v1/conversion_generated.go +++ b/pkg/api/v1/conversion_generated.go @@ -2071,6 +2071,7 @@ func convert_api_ServicePort_To_v1_ServicePort(in *api.ServicePort, out *Service if err := s.Convert(&in.TargetPort, &out.TargetPort, 0); err != nil { return err } + out.NodePort = in.NodePort return nil } @@ -4326,6 +4327,7 @@ func convert_v1_ServicePort_To_api_ServicePort(in *ServicePort, out *api.Service if err := s.Convert(&in.TargetPort, &out.TargetPort, 0); err != nil { return err } + out.NodePort = in.NodePort return nil } diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 520b922c620..0fc6a6a74cc 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -1078,6 +1078,10 @@ type ServicePort struct { // target Pod's container ports. If this is not specified, the value // of Port is used (an identity map). TargetPort util.IntOrString `json:"targetPort,omitempty" description:"the port to access on the pods targeted by the service; defaults to the service port"` + + // The port on each node on which this service is exposed. + // Default is to auto-allocate a port if the visibility of this Service requires one. + NodePort int `json:"nodePort" description:"the port on each node on which this service is exposed"` } // Service is a named abstraction of software service (for example, mysql) consisting of local port diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index 199fd85b9a0..f138bb14de2 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -774,6 +774,7 @@ func addConversionFuncs() { Port: in.Spec.Ports[i].Port, Protocol: Protocol(in.Spec.Ports[i].Protocol), ContainerPort: in.Spec.Ports[i].TargetPort, + NodePort: in.Spec.Ports[i].NodePort, }) } @@ -823,6 +824,7 @@ func addConversionFuncs() { Port: in.Ports[i].Port, Protocol: api.Protocol(in.Ports[i].Protocol), TargetPort: in.Ports[i].ContainerPort, + NodePort: in.Ports[i].NodePort, }) } } diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 08484ef524c..768b3d54e33 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -958,6 +958,10 @@ type ServicePort struct { // of Port is used (an identity map) - note this is a different default // than Service.ContainerPort. ContainerPort util.IntOrString `json:"containerPort" description:"the port to access on the containers belonging to pods targeted by the service; defaults to the service port"` + + // The port on each node on which this service is exposed. + // Default is to auto-allocate a port if the visibility of this Service requires one. + NodePort int `json:"nodePort" description:"the port on each node on which this service is exposed"` } // ServiceAccount binds together: diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index a45e1909ae7..ce2311500c9 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -696,6 +696,7 @@ func addConversionFuncs() { Port: in.Spec.Ports[i].Port, Protocol: Protocol(in.Spec.Ports[i].Protocol), ContainerPort: in.Spec.Ports[i].TargetPort, + NodePort: in.Spec.Ports[i].NodePort, }) } @@ -745,6 +746,7 @@ func addConversionFuncs() { Port: in.Ports[i].Port, Protocol: api.Protocol(in.Ports[i].Protocol), TargetPort: in.Ports[i].ContainerPort, + NodePort: in.Ports[i].NodePort, }) } } diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 74cf5676161..06f483ae673 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -962,6 +962,10 @@ type ServicePort struct { // of Port is used (an identity map) - note this is a different default // than Service.ContainerPort. ContainerPort util.IntOrString `json:"containerPort" description:"the port to access on the containers belonging to pods targeted by the service; defaults to the service port"` + + // The port on each node on which this service is exposed. + // Default is to auto-allocate a port if the visibility of this Service requires one. + NodePort int `json:"nodePort" description:"the port on each node on which this service is exposed"` } // ServiceAccount binds together: diff --git a/pkg/api/v1beta3/conversion_generated.go b/pkg/api/v1beta3/conversion_generated.go index 8ec294103e1..ac9d881b7e9 100644 --- a/pkg/api/v1beta3/conversion_generated.go +++ b/pkg/api/v1beta3/conversion_generated.go @@ -2010,6 +2010,7 @@ func convert_api_ServicePort_To_v1beta3_ServicePort(in *api.ServicePort, out *Se if err := s.Convert(&in.TargetPort, &out.TargetPort, 0); err != nil { return err } + out.NodePort = in.NodePort return nil } @@ -4198,6 +4199,7 @@ func convert_v1beta3_ServicePort_To_api_ServicePort(in *ServicePort, out *api.Se if err := s.Convert(&in.TargetPort, &out.TargetPort, 0); err != nil { return err } + out.NodePort = in.NodePort return nil } diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 4c5757410cc..e00ac2d4350 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -1085,6 +1085,10 @@ type ServicePort struct { // target Pod's container ports. If this is not specified, the value // of Port is used (an identity map). TargetPort util.IntOrString `json:"targetPort,omitempty" description:"the port to access on the pods targeted by the service; defaults to the service port"` + + // The port on each node on which this service is exposed. + // Default is to auto-allocate a port if the visibility of this Service requires one. + NodePort int `json:"nodePort" description:"the port on each node on which this service is exposed"` } // Service is a named abstraction of software service (for example, mysql) consisting of local port diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 6bf657527ce..a4a13c0b811 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1086,6 +1086,28 @@ func ValidateService(service *api.Service) errs.ValidationErrorList { } } + // Check for duplicate NodePorts, considering (protocol,port) pairs + nodePorts := make(map[api.ServicePort]bool) + for i := range service.Spec.Ports { + port := &service.Spec.Ports[i] + if port.NodePort == 0 { + continue + } + var key api.ServicePort + key.Protocol = port.Protocol + key.NodePort = port.NodePort + _, found := nodePorts[key] + if found { + allErrs = append(allErrs, errs.NewFieldInvalid("spec.ports", *port, "duplicate nodePort specified")) + } + nodePorts[key] = true + } + + // Temporary validation to prevent people creating NodePorts before we have the full infrastructure in place + if len(nodePorts) > 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("spec.ports", service.Spec.Ports[0], "nodePorts not (yet) enabled")) + } + return allErrs } diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 6a455a59bf9..df71462e45b 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -1720,6 +1720,22 @@ func TestValidateService(t *testing.T) { }, numErrs: 0, }, + { + name: "duplicate nodeports", + tweakSvc: func(s *api.Service) { + s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1}) + s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1}) + }, + numErrs: 2, // TODO(justinsb): change to 1 when NodePorts enabled + }, + { + name: "duplicate nodeports (different protocols)", + tweakSvc: func(s *api.Service) { + s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1}) + s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1}) + }, + numErrs: 1, // TODO(justinsb): change to 0 when NodePorts enabled + }, } for _, tc := range testCases {