Skip to content

Commit 6a33727

Browse files
committed
Adding AppProtocol to Service and Endpoints Ports
1 parent 4e79344 commit 6a33727

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
@@ -3532,6 +3532,16 @@ type ServicePort struct {
35323532
// The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
35333533
Protocol Protocol
35343534

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

@@ -3672,6 +3682,16 @@ type EndpointPort struct {
36723682

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

36773697
// +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
@@ -3908,7 +3908,7 @@ var supportedServiceType = sets.NewString(string(core.ServiceTypeClusterIP), str
39083908
var supportedServiceIPFamily = sets.NewString(string(core.IPv4Protocol), string(core.IPv6Protocol))
39093909

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

39143914
specPath := field.NewPath("spec")
@@ -3953,7 +3953,7 @@ func ValidateService(service *core.Service) field.ErrorList {
39533953
portsPath := specPath.Child("ports")
39543954
for i := range service.Spec.Ports {
39553955
portPath := portsPath.Index(i)
3956-
allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService, &allPortNames, portPath)...)
3956+
allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService, allowAppProtocol, &allPortNames, portPath)...)
39573957
}
39583958

39593959
if service.Spec.Selector != nil {
@@ -4125,7 +4125,7 @@ func ValidateService(service *core.Service) field.ErrorList {
41254125
return allErrs
41264126
}
41274127

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

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

41524152
allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
41534153

4154+
if sp.AppProtocol != nil {
4155+
if allowAppProtocol {
4156+
for _, msg := range validation.IsQualifiedName(*sp.AppProtocol) {
4157+
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), sp.AppProtocol, msg))
4158+
}
4159+
} else {
4160+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
4161+
}
4162+
}
4163+
41544164
// in the v1 API, targetPorts on headless services were tolerated.
41554165
// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
41564166
//
@@ -4207,6 +4217,14 @@ func ValidateServiceExternalTrafficFieldsCombination(service *core.Service) fiel
42074217
return allErrs
42084218
}
42094219

4220+
// ValidateServiceCreate validates Services as they are created.
4221+
func ValidateServiceCreate(service *core.Service) field.ErrorList {
4222+
// allow AppProtocol value if the feature gate is set.
4223+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
4224+
4225+
return ValidateService(service, allowAppProtocol)
4226+
}
4227+
42104228
// ValidateServiceUpdate tests if required fields in the service are set during an update
42114229
func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
42124230
allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
@@ -4226,8 +4244,19 @@ func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
42264244
}
42274245
}
42284246

4229-
allErrs = append(allErrs, ValidateService(service)...)
4230-
return allErrs
4247+
// allow AppProtocol value if the feature gate is set or the field is
4248+
// already set on the resource.
4249+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
4250+
if !allowAppProtocol {
4251+
for _, port := range oldService.Spec.Ports {
4252+
if port.AppProtocol != nil {
4253+
allowAppProtocol = true
4254+
break
4255+
}
4256+
}
4257+
}
4258+
4259+
return append(allErrs, ValidateService(service, allowAppProtocol)...)
42314260
}
42324261

42334262
// ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
@@ -5443,15 +5472,42 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace)
54435472
return allErrs
54445473
}
54455474

5446-
// ValidateEndpoints tests if required fields are set.
5447-
func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
5475+
// ValidateEndpoints validates Endpoints on create and update.
5476+
func ValidateEndpoints(endpoints *core.Endpoints, allowAppProtocol bool) field.ErrorList {
54485477
allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
54495478
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
5450-
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
5479+
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, allowAppProtocol, field.NewPath("subsets"))...)
5480+
return allErrs
5481+
}
5482+
5483+
// ValidateEndpointsCreate validates Endpoints on create.
5484+
func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
5485+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
5486+
return ValidateEndpoints(endpoints, allowAppProtocol)
5487+
}
5488+
5489+
// ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are
5490+
// allowed during update to accommodate the case where nodeIP or PodCIDR is
5491+
// reused. An existing endpoint ip will have a different nodeName if this
5492+
// happens.
5493+
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
5494+
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
5495+
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
5496+
if !allowAppProtocol {
5497+
for _, oldSubset := range oldEndpoints.Subsets {
5498+
for _, port := range oldSubset.Ports {
5499+
if port.AppProtocol != nil {
5500+
allowAppProtocol = true
5501+
break
5502+
}
5503+
}
5504+
}
5505+
}
5506+
allErrs = append(allErrs, ValidateEndpoints(newEndpoints, allowAppProtocol)...)
54515507
return allErrs
54525508
}
54535509

5454-
func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
5510+
func validateEndpointSubsets(subsets []core.EndpointSubset, allowAppProtocol bool, fldPath *field.Path) field.ErrorList {
54555511
allErrs := field.ErrorList{}
54565512
for i := range subsets {
54575513
ss := &subsets[i]
@@ -5469,7 +5525,7 @@ func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path)
54695525
allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
54705526
}
54715527
for port := range ss.Ports {
5472-
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
5528+
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, allowAppProtocol, idxPath.Child("ports").Index(port))...)
54735529
}
54745530
}
54755531

@@ -5520,7 +5576,7 @@ func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList
55205576
return allErrs
55215577
}
55225578

5523-
func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
5579+
func validateEndpointPort(port *core.EndpointPort, requireName, allowAppProtocol bool, fldPath *field.Path) field.ErrorList {
55245580
allErrs := field.ErrorList{}
55255581
if requireName && len(port.Name) == 0 {
55265582
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
@@ -5535,16 +5591,15 @@ func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *fi
55355591
} else if !supportedPortProtocols.Has(string(port.Protocol)) {
55365592
allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
55375593
}
5538-
return allErrs
5539-
}
5540-
5541-
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
5542-
// NodeName changes are allowed during update to accommodate the case where nodeIP or PodCIDR is reused.
5543-
// An existing endpoint ip will have a different nodeName if this happens.
5544-
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
5545-
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
5546-
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, field.NewPath("subsets"))...)
5547-
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...)
5594+
if port.AppProtocol != nil {
5595+
if allowAppProtocol {
5596+
for _, msg := range validation.IsQualifiedName(*port.AppProtocol) {
5597+
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), port.AppProtocol, msg))
5598+
}
5599+
} else {
5600+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
5601+
}
5602+
}
55485603
return allErrs
55495604
}
55505605

0 commit comments

Comments
 (0)