Skip to content

Commit 86141c0

Browse files
authored
Merge pull request kubernetes#88503 from robscott/app-protocol
Adding AppProtocol to Service and Endpoints Ports
2 parents 16a7650 + 6a33727 commit 86141c0

29 files changed

+1598
-986
lines changed

api/openapi-spec/swagger.json

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/core/types.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,6 +3533,16 @@ type ServicePort struct {
35333533
// The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
35343534
Protocol Protocol
35353535

3536+
// The application protocol for this port.
3537+
// This field follows standard Kubernetes label syntax.
3538+
// Un-prefixed names are reserved for IANA standard service names (as per
3539+
// RFC-6335 and http://www.iana.org/assignments/service-names).
3540+
// Non-standard protocols should use prefixed names such as
3541+
// mycompany.com/my-custom-protocol.
3542+
// Field can be enabled with ServiceAppProtocol feature gate.
3543+
// +optional
3544+
AppProtocol *string
3545+
35363546
// The port that will be exposed on the service.
35373547
Port int32
35383548

@@ -3673,6 +3683,16 @@ type EndpointPort struct {
36733683

36743684
// The IP protocol for this port.
36753685
Protocol Protocol
3686+
3687+
// The application protocol for this port.
3688+
// This field follows standard Kubernetes label syntax.
3689+
// Un-prefixed names are reserved for IANA standard service names (as per
3690+
// RFC-6335 and http://www.iana.org/assignments/service-names).
3691+
// Non-standard protocols should use prefixed names such as
3692+
// mycompany.com/my-custom-protocol.
3693+
// Field can be enabled with ServiceAppProtocol feature gate.
3694+
// +optional
3695+
AppProtocol *string
36763696
}
36773697

36783698
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

pkg/apis/core/v1/zz_generated.conversion.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/core/validation/validation.go

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3928,7 +3928,7 @@ var supportedServiceType = sets.NewString(string(core.ServiceTypeClusterIP), str
39283928
var supportedServiceIPFamily = sets.NewString(string(core.IPv4Protocol), string(core.IPv6Protocol))
39293929

39303930
// ValidateService tests if required fields/annotations of a Service are valid.
3931-
func ValidateService(service *core.Service) field.ErrorList {
3931+
func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorList {
39323932
allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, field.NewPath("metadata"))
39333933

39343934
specPath := field.NewPath("spec")
@@ -3973,7 +3973,7 @@ func ValidateService(service *core.Service) field.ErrorList {
39733973
portsPath := specPath.Child("ports")
39743974
for i := range service.Spec.Ports {
39753975
portPath := portsPath.Index(i)
3976-
allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService, &allPortNames, portPath)...)
3976+
allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService, allowAppProtocol, &allPortNames, portPath)...)
39773977
}
39783978

39793979
if service.Spec.Selector != nil {
@@ -4145,7 +4145,7 @@ func ValidateService(service *core.Service) field.ErrorList {
41454145
return allErrs
41464146
}
41474147

4148-
func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bool, allNames *sets.String, fldPath *field.Path) field.ErrorList {
4148+
func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService, allowAppProtocol bool, allNames *sets.String, fldPath *field.Path) field.ErrorList {
41494149
allErrs := field.ErrorList{}
41504150

41514151
if requireName && len(sp.Name) == 0 {
@@ -4171,6 +4171,16 @@ func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bo
41714171

41724172
allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
41734173

4174+
if sp.AppProtocol != nil {
4175+
if allowAppProtocol {
4176+
for _, msg := range validation.IsQualifiedName(*sp.AppProtocol) {
4177+
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), sp.AppProtocol, msg))
4178+
}
4179+
} else {
4180+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
4181+
}
4182+
}
4183+
41744184
// in the v1 API, targetPorts on headless services were tolerated.
41754185
// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
41764186
//
@@ -4227,6 +4237,14 @@ func ValidateServiceExternalTrafficFieldsCombination(service *core.Service) fiel
42274237
return allErrs
42284238
}
42294239

4240+
// ValidateServiceCreate validates Services as they are created.
4241+
func ValidateServiceCreate(service *core.Service) field.ErrorList {
4242+
// allow AppProtocol value if the feature gate is set.
4243+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
4244+
4245+
return ValidateService(service, allowAppProtocol)
4246+
}
4247+
42304248
// ValidateServiceUpdate tests if required fields in the service are set during an update
42314249
func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
42324250
allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
@@ -4246,8 +4264,19 @@ func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
42464264
}
42474265
}
42484266

4249-
allErrs = append(allErrs, ValidateService(service)...)
4250-
return allErrs
4267+
// allow AppProtocol value if the feature gate is set or the field is
4268+
// already set on the resource.
4269+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
4270+
if !allowAppProtocol {
4271+
for _, port := range oldService.Spec.Ports {
4272+
if port.AppProtocol != nil {
4273+
allowAppProtocol = true
4274+
break
4275+
}
4276+
}
4277+
}
4278+
4279+
return append(allErrs, ValidateService(service, allowAppProtocol)...)
42514280
}
42524281

42534282
// ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
@@ -5463,15 +5492,42 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace)
54635492
return allErrs
54645493
}
54655494

5466-
// ValidateEndpoints tests if required fields are set.
5467-
func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
5495+
// ValidateEndpoints validates Endpoints on create and update.
5496+
func ValidateEndpoints(endpoints *core.Endpoints, allowAppProtocol bool) field.ErrorList {
54685497
allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
54695498
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
5470-
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
5499+
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, allowAppProtocol, field.NewPath("subsets"))...)
5500+
return allErrs
5501+
}
5502+
5503+
// ValidateEndpointsCreate validates Endpoints on create.
5504+
func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
5505+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
5506+
return ValidateEndpoints(endpoints, allowAppProtocol)
5507+
}
5508+
5509+
// ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are
5510+
// allowed during update to accommodate the case where nodeIP or PodCIDR is
5511+
// reused. An existing endpoint ip will have a different nodeName if this
5512+
// happens.
5513+
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
5514+
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
5515+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
5516+
if !allowAppProtocol {
5517+
for _, oldSubset := range oldEndpoints.Subsets {
5518+
for _, port := range oldSubset.Ports {
5519+
if port.AppProtocol != nil {
5520+
allowAppProtocol = true
5521+
break
5522+
}
5523+
}
5524+
}
5525+
}
5526+
allErrs = append(allErrs, ValidateEndpoints(newEndpoints, allowAppProtocol)...)
54715527
return allErrs
54725528
}
54735529

5474-
func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
5530+
func validateEndpointSubsets(subsets []core.EndpointSubset, allowAppProtocol bool, fldPath *field.Path) field.ErrorList {
54755531
allErrs := field.ErrorList{}
54765532
for i := range subsets {
54775533
ss := &subsets[i]
@@ -5489,7 +5545,7 @@ func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path)
54895545
allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
54905546
}
54915547
for port := range ss.Ports {
5492-
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
5548+
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, allowAppProtocol, idxPath.Child("ports").Index(port))...)
54935549
}
54945550
}
54955551

@@ -5540,7 +5596,7 @@ func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList
55405596
return allErrs
55415597
}
55425598

5543-
func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
5599+
func validateEndpointPort(port *core.EndpointPort, requireName, allowAppProtocol bool, fldPath *field.Path) field.ErrorList {
55445600
allErrs := field.ErrorList{}
55455601
if requireName && len(port.Name) == 0 {
55465602
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
@@ -5555,16 +5611,15 @@ func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *fi
55555611
} else if !supportedPortProtocols.Has(string(port.Protocol)) {
55565612
allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
55575613
}
5558-
return allErrs
5559-
}
5560-
5561-
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
5562-
// NodeName changes are allowed during update to accommodate the case where nodeIP or PodCIDR is reused.
5563-
// An existing endpoint ip will have a different nodeName if this happens.
5564-
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
5565-
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
5566-
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, field.NewPath("subsets"))...)
5567-
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...)
5614+
if port.AppProtocol != nil {
5615+
if allowAppProtocol {
5616+
for _, msg := range validation.IsQualifiedName(*port.AppProtocol) {
5617+
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), port.AppProtocol, msg))
5618+
}
5619+
} else {
5620+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
5621+
}
5622+
}
55685623
return allErrs
55695624
}
55705625

0 commit comments

Comments
 (0)