diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json
index 68b572200d7..75602ec705f 100644
--- a/api/swagger-spec/v1.json
+++ b/api/swagger-spec/v1.json
@@ -17772,7 +17772,7 @@
},
"targetPort": {
"type": "string",
- "description": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of Port is used (an identity map). Defaults to the service port. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service"
+ "description": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service"
},
"nodePort": {
"type": "integer",
diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html
index 5c82050f0aa..4eded177ee0 100755
--- a/docs/api-reference/v1/definitions.html
+++ b/docs/api-reference/v1/definitions.html
@@ -5513,7 +5513,7 @@ The resulting set of endpoints can be viewed as:
targetPort |
-Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod’s container ports. If this is not specified, the value of Port is used (an identity map). Defaults to the service port. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service |
+Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod’s container ports. If this is not specified, the value of the port field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the port field. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service |
false |
string |
|
diff --git a/pkg/api/types.go b/pkg/api/types.go
index d632a2090ee..6fc62fe4795 100644
--- a/pkg/api/types.go
+++ b/pkg/api/types.go
@@ -1526,8 +1526,10 @@ type ServicePort struct {
// Optional: The target port on pods selected by this service. If this
// is a string, it will be looked up as a named port in the target
- // Pod's container ports. If this is not specified, the default value
- // is the sames as the Port field (an identity map).
+ // Pod's container ports. If this is not specified, the value
+ // of the 'port' field is used (an identity map).
+ // This field is ignored for services with clusterIP=None, and should be
+ // omitted or set equal to the 'port' field.
TargetPort intstr.IntOrString `json:"targetPort"`
// The port on each node on which this service is exposed.
diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go
index 05e3cefbcb7..47ceeec5cee 100644
--- a/pkg/api/v1/types.go
+++ b/pkg/api/v1/types.go
@@ -1848,8 +1848,9 @@ type ServicePort struct {
// Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
// If this is a string, it will be looked up as a named port in the
// target Pod's container ports. If this is not specified, the value
- // of Port is used (an identity map).
- // Defaults to the service port.
+ // of the 'port' field is used (an identity map).
+ // This field is ignored for services with clusterIP=None, and should be
+ // omitted or set equal to the 'port' field.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service
TargetPort intstr.IntOrString `json:"targetPort,omitempty"`
diff --git a/pkg/api/v1/types_swagger_doc_generated.go b/pkg/api/v1/types_swagger_doc_generated.go
index ae9ebaf5ddf..a9c7864528b 100644
--- a/pkg/api/v1/types_swagger_doc_generated.go
+++ b/pkg/api/v1/types_swagger_doc_generated.go
@@ -1500,7 +1500,7 @@ var map_ServicePort = map[string]string{
"name": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service.",
"protocol": "The IP protocol for this port. Supports \"TCP\" and \"UDP\". Default is TCP.",
"port": "The port that will be exposed by this service.",
- "targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of Port is used (an identity map). Defaults to the service port. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service",
+ "targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service",
"nodePort": "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#type--nodeport",
}
diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go
index 4546a2f5ff2..47d94242f6b 100644
--- a/pkg/api/validation/validation.go
+++ b/pkg/api/validation/validation.go
@@ -1770,11 +1770,14 @@ func validateServicePort(sp *api.ServicePort, requireName, isHeadlessService boo
allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, PortNameErrorMsg))
}
- if isHeadlessService {
- if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) {
- allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, "must be equal to targetPort when clusterIP = None"))
- }
- }
+ // in the v1 API, targetPorts on headless services were tolerated.
+ // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
+ //
+ // if isHeadlessService {
+ // if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) {
+ // allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, "must be equal to the value of 'port' when clusterIP = None"))
+ // }
+ // }
return allErrs
}
diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go
index 04c27126aaa..4c60d66f895 100644
--- a/pkg/api/validation/validation_test.go
+++ b/pkg/api/validation/validation_test.go
@@ -2620,22 +2620,28 @@ func TestValidateService(t *testing.T) {
numErrs: 0,
},
{
- name: "invalid port headless",
+ name: "invalid port headless 1",
tweakSvc: func(s *api.Service) {
s.Spec.Ports[0].Port = 11722
s.Spec.Ports[0].TargetPort = intstr.FromInt(11721)
s.Spec.ClusterIP = api.ClusterIPNone
},
- numErrs: 1,
+ // in the v1 API, targetPorts on headless services were tolerated.
+ // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
+ // numErrs: 1,
+ numErrs: 0,
},
{
- name: "invalid port headless",
+ name: "invalid port headless 2",
tweakSvc: func(s *api.Service) {
s.Spec.Ports[0].Port = 11722
s.Spec.Ports[0].TargetPort = intstr.FromString("target")
s.Spec.ClusterIP = api.ClusterIPNone
},
- numErrs: 1,
+ // in the v1 API, targetPorts on headless services were tolerated.
+ // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
+ // numErrs: 1,
+ numErrs: 0,
},
{
name: "invalid publicIPs localhost",