Merge pull request #96327 from robscott/app-protocol-ga

Graduating AppProtocol to GA
This commit is contained in:
Kubernetes Prow Robot 2020-11-12 13:16:39 -08:00 committed by GitHub
commit 4b46d44e0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 162 deletions

View File

@ -3731,8 +3731,6 @@ type ServicePort struct {
// RFC-6335 and http://www.iana.org/assignments/service-names). // RFC-6335 and http://www.iana.org/assignments/service-names).
// Non-standard protocols should use prefixed names such as // Non-standard protocols should use prefixed names such as
// mycompany.com/my-custom-protocol. // mycompany.com/my-custom-protocol.
// This is a beta field that is guarded by the ServiceAppProtocol feature
// gate and enabled by default.
// +optional // +optional
AppProtocol *string AppProtocol *string
@ -3883,8 +3881,6 @@ type EndpointPort struct {
// RFC-6335 and http://www.iana.org/assignments/service-names). // RFC-6335 and http://www.iana.org/assignments/service-names).
// Non-standard protocols should use prefixed names such as // Non-standard protocols should use prefixed names such as
// mycompany.com/my-custom-protocol. // mycompany.com/my-custom-protocol.
// This is a beta field that is guarded by the ServiceAppProtocol feature
// gate and enabled by default.
// +optional // +optional
AppProtocol *string AppProtocol *string
} }

View File

@ -4153,7 +4153,7 @@ var supportedServiceIPFamily = sets.NewString(string(core.IPv4Protocol), string(
var supportedServiceIPFamilyPolicy = sets.NewString(string(core.IPFamilyPolicySingleStack), string(core.IPFamilyPolicyPreferDualStack), string(core.IPFamilyPolicyRequireDualStack)) var supportedServiceIPFamilyPolicy = sets.NewString(string(core.IPFamilyPolicySingleStack), string(core.IPFamilyPolicyPreferDualStack), string(core.IPFamilyPolicyRequireDualStack))
// ValidateService tests if required fields/annotations of a Service are valid. // ValidateService tests if required fields/annotations of a Service are valid.
func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorList { func ValidateService(service *core.Service) field.ErrorList {
allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, field.NewPath("metadata")) allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, field.NewPath("metadata"))
specPath := field.NewPath("spec") specPath := field.NewPath("spec")
@ -4207,7 +4207,7 @@ func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorLi
portsPath := specPath.Child("ports") portsPath := specPath.Child("ports")
for i := range service.Spec.Ports { for i := range service.Spec.Ports {
portPath := portsPath.Index(i) portPath := portsPath.Index(i)
allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService(service), allowAppProtocol, &allPortNames, portPath)...) allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService(service), &allPortNames, portPath)...)
} }
if service.Spec.Selector != nil { if service.Spec.Selector != nil {
@ -4362,7 +4362,7 @@ func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorLi
return allErrs return allErrs
} }
func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService, allowAppProtocol bool, allNames *sets.String, fldPath *field.Path) field.ErrorList { func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bool, allNames *sets.String, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if requireName && len(sp.Name) == 0 { if requireName && len(sp.Name) == 0 {
@ -4389,12 +4389,8 @@ func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService, a
allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...) allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
if sp.AppProtocol != nil { if sp.AppProtocol != nil {
if allowAppProtocol { for _, msg := range validation.IsQualifiedName(*sp.AppProtocol) {
for _, msg := range validation.IsQualifiedName(*sp.AppProtocol) { allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), sp.AppProtocol, msg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), sp.AppProtocol, msg))
}
} else {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
} }
} }
@ -4456,10 +4452,7 @@ func ValidateServiceExternalTrafficFieldsCombination(service *core.Service) fiel
// ValidateServiceCreate validates Services as they are created. // ValidateServiceCreate validates Services as they are created.
func ValidateServiceCreate(service *core.Service) field.ErrorList { func ValidateServiceCreate(service *core.Service) field.ErrorList {
// allow AppProtocol value if the feature gate is set. return ValidateService(service)
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
return ValidateService(service, allowAppProtocol)
} }
// ValidateServiceUpdate tests if required fields in the service are set during an update // ValidateServiceUpdate tests if required fields in the service are set during an update
@ -4477,19 +4470,7 @@ func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
upgradeDowngradeIPFamiliesErrs := validateUpgradeDowngradeIPFamilies(oldService, service) upgradeDowngradeIPFamiliesErrs := validateUpgradeDowngradeIPFamilies(oldService, service)
allErrs = append(allErrs, upgradeDowngradeIPFamiliesErrs...) allErrs = append(allErrs, upgradeDowngradeIPFamiliesErrs...)
// allow AppProtocol value if the feature gate is set or the field is return append(allErrs, ValidateService(service)...)
// already set on the resource.
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol)
if !allowAppProtocol {
for _, port := range oldService.Spec.Ports {
if port.AppProtocol != nil {
allowAppProtocol = true
break
}
}
}
return append(allErrs, ValidateService(service, allowAppProtocol)...)
} }
// ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status. // ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
@ -5667,17 +5648,16 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace)
} }
// ValidateEndpoints validates Endpoints on create and update. // ValidateEndpoints validates Endpoints on create and update.
func ValidateEndpoints(endpoints *core.Endpoints, allowAppProtocol bool) field.ErrorList { func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata")) allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...) allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, allowAppProtocol, field.NewPath("subsets"))...) allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
return allErrs return allErrs
} }
// ValidateEndpointsCreate validates Endpoints on create. // ValidateEndpointsCreate validates Endpoints on create.
func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList { func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol) return ValidateEndpoints(endpoints)
return ValidateEndpoints(endpoints, allowAppProtocol)
} }
// ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are // ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are
@ -5686,22 +5666,11 @@ func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
// happens. // happens.
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList { func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata")) allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
allowAppProtocol := utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol) allErrs = append(allErrs, ValidateEndpoints(newEndpoints)...)
if !allowAppProtocol {
for _, oldSubset := range oldEndpoints.Subsets {
for _, port := range oldSubset.Ports {
if port.AppProtocol != nil {
allowAppProtocol = true
break
}
}
}
}
allErrs = append(allErrs, ValidateEndpoints(newEndpoints, allowAppProtocol)...)
return allErrs return allErrs
} }
func validateEndpointSubsets(subsets []core.EndpointSubset, allowAppProtocol bool, fldPath *field.Path) field.ErrorList { func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
for i := range subsets { for i := range subsets {
ss := &subsets[i] ss := &subsets[i]
@ -5719,7 +5688,7 @@ func validateEndpointSubsets(subsets []core.EndpointSubset, allowAppProtocol boo
allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...) allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
} }
for port := range ss.Ports { for port := range ss.Ports {
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, allowAppProtocol, idxPath.Child("ports").Index(port))...) allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
} }
} }
@ -5770,7 +5739,7 @@ func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList
return allErrs return allErrs
} }
func validateEndpointPort(port *core.EndpointPort, requireName, allowAppProtocol bool, fldPath *field.Path) field.ErrorList { func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if requireName && len(port.Name) == 0 { if requireName && len(port.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
@ -5786,12 +5755,8 @@ func validateEndpointPort(port *core.EndpointPort, requireName, allowAppProtocol
allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
} }
if port.AppProtocol != nil { if port.AppProtocol != nil {
if allowAppProtocol { for _, msg := range validation.IsQualifiedName(*port.AppProtocol) {
for _, msg := range validation.IsQualifiedName(*port.AppProtocol) { allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), port.AppProtocol, msg))
allErrs = append(allErrs, field.Invalid(fldPath.Child("appProtocol"), port.AppProtocol, msg))
}
} else {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appProtocol"), "This field can be enabled with the ServiceAppProtocol feature gate"))
} }
} }
return allErrs return allErrs

View File

@ -10016,10 +10016,9 @@ func TestValidateServiceCreate(t *testing.T) {
preferDualStack := core.IPFamilyPolicyPreferDualStack preferDualStack := core.IPFamilyPolicyPreferDualStack
testCases := []struct { testCases := []struct {
name string name string
tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
numErrs int numErrs int
appProtocolEnabled bool
}{ }{
{ {
name: "missing namespace", name: "missing namespace",
@ -11126,8 +11125,7 @@ func TestValidateServiceCreate(t *testing.T) {
AppProtocol: utilpointer.StringPtr("HTTP"), AppProtocol: utilpointer.StringPtr("HTTP"),
}} }}
}, },
appProtocolEnabled: true, numErrs: 0,
numErrs: 0,
}, },
{ {
name: `valid custom appProtocol`, name: `valid custom appProtocol`,
@ -11139,8 +11137,7 @@ func TestValidateServiceCreate(t *testing.T) {
AppProtocol: utilpointer.StringPtr("example.com/protocol"), AppProtocol: utilpointer.StringPtr("example.com/protocol"),
}} }}
}, },
appProtocolEnabled: true, numErrs: 0,
numErrs: 0,
}, },
{ {
name: `invalid appProtocol`, name: `invalid appProtocol`,
@ -11152,8 +11149,7 @@ func TestValidateServiceCreate(t *testing.T) {
AppProtocol: utilpointer.StringPtr("example.com/protocol_with{invalid}[characters]"), AppProtocol: utilpointer.StringPtr("example.com/protocol_with{invalid}[characters]"),
}} }}
}, },
appProtocolEnabled: true, numErrs: 1,
numErrs: 1,
}, },
{ {
@ -11177,8 +11173,6 @@ func TestValidateServiceCreate(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, true)()
svc := makeValidService() svc := makeValidService()
tc.tweakSvc(&svc) tc.tweakSvc(&svc)
errs := ValidateServiceCreate(&svc) errs := ValidateServiceCreate(&svc)
@ -12742,10 +12736,9 @@ func TestValidateServiceUpdate(t *testing.T) {
preferDualStack := core.IPFamilyPolicyPreferDualStack preferDualStack := core.IPFamilyPolicyPreferDualStack
singleStack := core.IPFamilyPolicySingleStack singleStack := core.IPFamilyPolicySingleStack
testCases := []struct { testCases := []struct {
name string name string
tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
numErrs int numErrs int
appProtocolEnabled bool
}{ }{
{ {
name: "no change", name: "no change",
@ -13614,47 +13607,26 @@ func TestValidateServiceUpdate(t *testing.T) {
}, },
numErrs: 1, numErrs: 1,
}, },
{ {
name: "update with valid app protocol, field unset, gate disabled", name: "update to valid app protocol",
tweakSvc: func(oldSvc, newSvc *core.Service) { tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP"}} oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP"}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}} newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
}, },
numErrs: 1,
},
{
name: "update to valid app protocol, field set, gate disabled",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("http")}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
},
numErrs: 0, numErrs: 0,
}, },
{ {
name: "update to valid app protocol, gate enabled", name: "update to invalid app protocol",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP"}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
},
appProtocolEnabled: true,
numErrs: 0,
},
{
name: "update to invalid app protocol, gate enabled",
tweakSvc: func(oldSvc, newSvc *core.Service) { tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP"}} oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP"}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("~https")}} newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt(3000), Protocol: "TCP", AppProtocol: utilpointer.StringPtr("~https")}}
}, },
appProtocolEnabled: true, numErrs: 1,
numErrs: 1,
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
oldSvc := makeValidService() oldSvc := makeValidService()
newSvc := makeValidService() newSvc := makeValidService()
tc.tweakSvc(&oldSvc, &newSvc) tc.tweakSvc(&oldSvc, &newSvc)
@ -14951,8 +14923,7 @@ func TestValidateSSHAuthSecret(t *testing.T) {
func TestValidateEndpointsCreate(t *testing.T) { func TestValidateEndpointsCreate(t *testing.T) {
successCases := map[string]struct { successCases := map[string]struct {
endpoints core.Endpoints endpoints core.Endpoints
appProtocolEnabled bool
}{ }{
"simple endpoint": { "simple endpoint": {
endpoints: core.Endpoints{ endpoints: core.Endpoints{
@ -14995,7 +14966,6 @@ func TestValidateEndpointsCreate(t *testing.T) {
}, },
}, },
}, },
appProtocolEnabled: true,
}, },
"empty ports": { "empty ports": {
endpoints: core.Endpoints{ endpoints: core.Endpoints{
@ -15011,7 +14981,6 @@ func TestValidateEndpointsCreate(t *testing.T) {
for name, tc := range successCases { for name, tc := range successCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
errs := ValidateEndpointsCreate(&tc.endpoints) errs := ValidateEndpointsCreate(&tc.endpoints)
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("Expected no validation errors, got %v", errs) t.Errorf("Expected no validation errors, got %v", errs)
@ -15021,10 +14990,9 @@ func TestValidateEndpointsCreate(t *testing.T) {
} }
errorCases := map[string]struct { errorCases := map[string]struct {
endpoints core.Endpoints endpoints core.Endpoints
appProtocolEnabled bool errorType field.ErrorType
errorType field.ErrorType errorDetail string
errorDetail string
}{ }{
"missing namespace": { "missing namespace": {
endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}}, endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}},
@ -15192,15 +15160,13 @@ func TestValidateEndpointsCreate(t *testing.T) {
}, },
}, },
}, },
appProtocolEnabled: true, errorType: "FieldValueInvalid",
errorType: "FieldValueInvalid", errorDetail: "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character",
errorDetail: "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character",
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
t.Run(k, func(t *testing.T) { t.Run(k, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, v.appProtocolEnabled)()
if errs := ValidateEndpointsCreate(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) { if errs := ValidateEndpointsCreate(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
t.Errorf("Expected error type %s with detail %q, got %v", v.errorType, v.errorDetail, errs) t.Errorf("Expected error type %s with detail %q, got %v", v.errorType, v.errorDetail, errs)
} }
@ -15217,54 +15183,32 @@ func TestValidateEndpointsUpdate(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
tweakOldEndpoints func(ep *core.Endpoints) tweakOldEndpoints func(ep *core.Endpoints)
tweakNewEndpoints func(ep *core.Endpoints) tweakNewEndpoints func(ep *core.Endpoints)
appProtocolEnabled bool numExpectedErrors int
numExpectedErrors int
}{ }{
"update with valid app protocol, field unset, gate not enabled": { "update to valid app protocol": {
tweakOldEndpoints: func(ep *core.Endpoints) { tweakOldEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}} ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}}
}, },
tweakNewEndpoints: func(ep *core.Endpoints) { tweakNewEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}} ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
}, },
numExpectedErrors: 1,
},
"update with valid app protocol, field set, gate not enabled": {
tweakOldEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("http")}}
},
tweakNewEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
},
numExpectedErrors: 0, numExpectedErrors: 0,
}, },
"update to valid app protocol, gate enabled": { "update to invalid app protocol": {
tweakOldEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}}
},
tweakNewEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("https")}}
},
appProtocolEnabled: true,
numExpectedErrors: 0,
},
"update to invalid app protocol, gate enabled": {
tweakOldEndpoints: func(ep *core.Endpoints) { tweakOldEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}} ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}}
}, },
tweakNewEndpoints: func(ep *core.Endpoints) { tweakNewEndpoints: func(ep *core.Endpoints) {
ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("~https")}} ep.Subsets[0].Ports = []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("~https")}}
}, },
appProtocolEnabled: true, numExpectedErrors: 1,
numExpectedErrors: 1,
}, },
} }
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
oldEndpoints := baseEndpoints.DeepCopy() oldEndpoints := baseEndpoints.DeepCopy()
tc.tweakOldEndpoints(oldEndpoints) tc.tweakOldEndpoints(oldEndpoints)
newEndpoints := baseEndpoints.DeepCopy() newEndpoints := baseEndpoints.DeepCopy()
@ -15814,7 +15758,7 @@ func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
updatedEndpoint := newNodeNameEndpoint("kubernetes-changed-nodename") updatedEndpoint := newNodeNameEndpoint("kubernetes-changed-nodename")
// Check that NodeName can be changed during update, this is to accommodate the case where nodeIP or PodCIDR is reused. // Check that NodeName can be changed during update, this is to accommodate the case where nodeIP or PodCIDR is reused.
// The same ip will now have a different nodeName. // The same ip will now have a different nodeName.
errList := ValidateEndpoints(updatedEndpoint, false) errList := ValidateEndpoints(updatedEndpoint)
errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...) errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...)
if len(errList) != 0 { if len(errList) != 0 {
t.Error("Endpoint should allow changing of Subset.Addresses.NodeName on update") t.Error("Endpoint should allow changing of Subset.Addresses.NodeName on update")
@ -15824,7 +15768,7 @@ func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) { func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
// Check NodeName DNS validation // Check NodeName DNS validation
endpoint := newNodeNameEndpoint("illegal*.nodename") endpoint := newNodeNameEndpoint("illegal*.nodename")
errList := ValidateEndpoints(endpoint, false) errList := ValidateEndpoints(endpoint)
if len(errList) == 0 { if len(errList) == 0 {
t.Error("Endpoint should reject invalid NodeName") t.Error("Endpoint should reject invalid NodeName")
} }
@ -15832,7 +15776,7 @@ func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
func TestEndpointAddressNodeNameCanBeAnIPAddress(t *testing.T) { func TestEndpointAddressNodeNameCanBeAnIPAddress(t *testing.T) {
endpoint := newNodeNameEndpoint("10.10.1.1") endpoint := newNodeNameEndpoint("10.10.1.1")
errList := ValidateEndpoints(endpoint, false) errList := ValidateEndpoints(endpoint)
if len(errList) != 0 { if len(errList) != 0 {
t.Error("Endpoint should accept a NodeName that is an IP address") t.Error("Endpoint should accept a NodeName that is an IP address")
} }

View File

@ -639,12 +639,10 @@ func shouldPodBeInEndpoints(pod *v1.Pod) bool {
func endpointPortFromServicePort(servicePort *v1.ServicePort, portNum int) *v1.EndpointPort { func endpointPortFromServicePort(servicePort *v1.ServicePort, portNum int) *v1.EndpointPort {
epp := &v1.EndpointPort{ epp := &v1.EndpointPort{
Name: servicePort.Name, Name: servicePort.Name,
Port: int32(portNum), Port: int32(portNum),
Protocol: servicePort.Protocol, Protocol: servicePort.Protocol,
} AppProtocol: servicePort.AppProtocol,
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol) {
epp.AppProtocol = servicePort.AppProtocol
} }
return epp return epp
} }

View File

@ -1984,27 +1984,14 @@ func TestSyncEndpointsServiceNotFound(t *testing.T) {
func TestEndpointPortFromServicePort(t *testing.T) { func TestEndpointPortFromServicePort(t *testing.T) {
http := utilpointer.StringPtr("http") http := utilpointer.StringPtr("http")
testCases := map[string]struct { testCases := map[string]struct {
featureGateEnabled bool
serviceAppProtocol *string serviceAppProtocol *string
expectedEndpointsAppProtocol *string expectedEndpointsAppProtocol *string
}{ }{
"feature gate disabled, empty app protocol": { "empty app protocol": {
featureGateEnabled: false,
serviceAppProtocol: nil, serviceAppProtocol: nil,
expectedEndpointsAppProtocol: nil, expectedEndpointsAppProtocol: nil,
}, },
"feature gate disabled, http app protocol": { "http app protocol": {
featureGateEnabled: false,
serviceAppProtocol: http,
expectedEndpointsAppProtocol: nil,
},
"feature gate enabled, empty app protocol": {
featureGateEnabled: true,
serviceAppProtocol: nil,
expectedEndpointsAppProtocol: nil,
},
"feature gate enabled, http app protocol": {
featureGateEnabled: true,
serviceAppProtocol: http, serviceAppProtocol: http,
expectedEndpointsAppProtocol: http, expectedEndpointsAppProtocol: http,
}, },
@ -2012,8 +1999,6 @@ func TestEndpointPortFromServicePort(t *testing.T) {
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.featureGateEnabled)()
epp := endpointPortFromServicePort(&v1.ServicePort{Name: "test", AppProtocol: tc.serviceAppProtocol}, 80) epp := endpointPortFromServicePort(&v1.ServicePort{Name: "test", AppProtocol: tc.serviceAppProtocol}, 80)
if epp.AppProtocol != tc.expectedEndpointsAppProtocol { if epp.AppProtocol != tc.expectedEndpointsAppProtocol {

View File

@ -581,6 +581,7 @@ const (
// owner: @robscott // owner: @robscott
// alpha: v1.18 // alpha: v1.18
// beta: v1.19 // beta: v1.19
// ga: v1.20
// //
// Enables AppProtocol field for Services and Endpoints. // Enables AppProtocol field for Services and Endpoints.
ServiceAppProtocol featuregate.Feature = "ServiceAppProtocol" ServiceAppProtocol featuregate.Feature = "ServiceAppProtocol"
@ -788,7 +789,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
PodDisruptionBudget: {Default: true, PreRelease: featuregate.Beta}, PodDisruptionBudget: {Default: true, PreRelease: featuregate.Beta},
CronJobControllerV2: {Default: false, PreRelease: featuregate.Alpha}, CronJobControllerV2: {Default: false, PreRelease: featuregate.Alpha},
ServiceTopology: {Default: false, PreRelease: featuregate.Alpha}, ServiceTopology: {Default: false, PreRelease: featuregate.Alpha},
ServiceAppProtocol: {Default: true, PreRelease: featuregate.Beta}, ServiceAppProtocol: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
ImmutableEphemeralVolumes: {Default: true, PreRelease: featuregate.Beta}, ImmutableEphemeralVolumes: {Default: true, PreRelease: featuregate.Beta},
HugePageStorageMediumSize: {Default: true, PreRelease: featuregate.Beta}, HugePageStorageMediumSize: {Default: true, PreRelease: featuregate.Beta},
DownwardAPIHugePages: {Default: false, PreRelease: featuregate.Alpha}, DownwardAPIHugePages: {Default: false, PreRelease: featuregate.Alpha},