Merge pull request #88503 from robscott/app-protocol
Adding AppProtocol to Service and Endpoints Ports
This commit is contained in:
@@ -9459,14 +9459,15 @@ func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateService(t *testing.T) {
|
||||
func TestValidateServiceCreate(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceTopology, true)()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
|
||||
numErrs int
|
||||
name string
|
||||
tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
|
||||
numErrs int
|
||||
appProtocolEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "missing namespace",
|
||||
@@ -10200,15 +10201,57 @@ func TestValidateService(t *testing.T) {
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: `valid appProtocol`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.Ports = []core.ServicePort{{
|
||||
Port: 12345,
|
||||
TargetPort: intstr.FromInt(12345),
|
||||
Protocol: "TCP",
|
||||
AppProtocol: utilpointer.StringPtr("HTTP"),
|
||||
}}
|
||||
},
|
||||
appProtocolEnabled: true,
|
||||
numErrs: 0,
|
||||
},
|
||||
{
|
||||
name: `valid custom appProtocol`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.Ports = []core.ServicePort{{
|
||||
Port: 12345,
|
||||
TargetPort: intstr.FromInt(12345),
|
||||
Protocol: "TCP",
|
||||
AppProtocol: utilpointer.StringPtr("example.com/protocol"),
|
||||
}}
|
||||
},
|
||||
appProtocolEnabled: true,
|
||||
numErrs: 0,
|
||||
},
|
||||
{
|
||||
name: `invalid appProtocol`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.Ports = []core.ServicePort{{
|
||||
Port: 12345,
|
||||
TargetPort: intstr.FromInt(12345),
|
||||
Protocol: "TCP",
|
||||
AppProtocol: utilpointer.StringPtr("example.com/protocol_with{invalid}[characters]"),
|
||||
}}
|
||||
},
|
||||
appProtocolEnabled: true,
|
||||
numErrs: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
svc := makeValidService()
|
||||
tc.tweakSvc(&svc)
|
||||
errs := ValidateService(&svc)
|
||||
if len(errs) != tc.numErrs {
|
||||
t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
|
||||
}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, true)()
|
||||
svc := makeValidService()
|
||||
tc.tweakSvc(&svc)
|
||||
errs := ValidateServiceCreate(&svc)
|
||||
if len(errs) != tc.numErrs {
|
||||
t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11808,9 +11851,10 @@ func TestValidateNodeUpdate(t *testing.T) {
|
||||
|
||||
func TestValidateServiceUpdate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
|
||||
numErrs int
|
||||
name string
|
||||
tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
|
||||
numErrs int
|
||||
appProtocolEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "no change",
|
||||
@@ -12254,16 +12298,54 @@ func TestValidateServiceUpdate(t *testing.T) {
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "update with valid app protocol, field unset, gate disabled",
|
||||
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")}}
|
||||
},
|
||||
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,
|
||||
},
|
||||
{
|
||||
name: "update to valid app protocol, gate enabled",
|
||||
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) {
|
||||
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: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
oldSvc := makeValidService()
|
||||
newSvc := makeValidService()
|
||||
tc.tweakSvc(&oldSvc, &newSvc)
|
||||
errs := ValidateServiceUpdate(&newSvc, &oldSvc)
|
||||
if len(errs) != tc.numErrs {
|
||||
t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate())
|
||||
}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
|
||||
|
||||
oldSvc := makeValidService()
|
||||
newSvc := makeValidService()
|
||||
tc.tweakSvc(&oldSvc, &newSvc)
|
||||
errs := ValidateServiceUpdate(&newSvc, &oldSvc)
|
||||
if len(errs) != tc.numErrs {
|
||||
t.Errorf("Expected %d errors, got %d: %v", tc.numErrs, len(errs), errs.ToAggregate())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13550,53 +13632,82 @@ func TestValidateSSHAuthSecret(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEndpoints(t *testing.T) {
|
||||
successCases := map[string]core.Endpoints{
|
||||
func TestValidateEndpointsCreate(t *testing.T) {
|
||||
successCases := map[string]struct {
|
||||
endpoints core.Endpoints
|
||||
appProtocolEnabled bool
|
||||
}{
|
||||
"simple endpoint": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
|
||||
Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
|
||||
},
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
|
||||
Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
|
||||
Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
|
||||
},
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
|
||||
Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"empty subsets": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
},
|
||||
},
|
||||
"no name required for singleton port": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
|
||||
Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
|
||||
Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"valid appProtocol": {
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
|
||||
Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("HTTP")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
appProtocolEnabled: true,
|
||||
},
|
||||
"empty ports": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range successCases {
|
||||
if errs := ValidateEndpoints(&v); len(errs) != 0 {
|
||||
t.Errorf("Expected success for %s, got %v", k, errs)
|
||||
}
|
||||
for name, tc := range successCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
|
||||
errs := ValidateEndpointsCreate(&tc.endpoints)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Expected no validation errors, got %v", errs)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
errorCases := map[string]struct {
|
||||
endpoints core.Endpoints
|
||||
errorType field.ErrorType
|
||||
errorDetail string
|
||||
endpoints core.Endpoints
|
||||
appProtocolEnabled bool
|
||||
errorType field.ErrorType
|
||||
errorDetail string
|
||||
}{
|
||||
"missing namespace": {
|
||||
endpoints: core.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}},
|
||||
@@ -13754,12 +13865,100 @@ func TestValidateEndpoints(t *testing.T) {
|
||||
errorType: "FieldValueInvalid",
|
||||
errorDetail: "link-local multicast",
|
||||
},
|
||||
"Invalid AppProtocol": {
|
||||
endpoints: core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
|
||||
Subsets: []core.EndpointSubset{
|
||||
{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
|
||||
Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP", AppProtocol: utilpointer.StringPtr("lots-of[invalid]-{chars}")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
appProtocolEnabled: true,
|
||||
errorType: "FieldValueInvalid",
|
||||
errorDetail: "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range errorCases {
|
||||
if errs := ValidateEndpoints(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
|
||||
t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
|
||||
}
|
||||
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) {
|
||||
t.Errorf("Expected error type %s with detail %q, got %v", v.errorType, v.errorDetail, errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEndpointsUpdate(t *testing.T) {
|
||||
baseEndpoints := core.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace", ResourceVersion: "1234"},
|
||||
Subsets: []core.EndpointSubset{{
|
||||
Addresses: []core.EndpointAddress{{IP: "10.1.2.3"}},
|
||||
}},
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
tweakOldEndpoints func(ep *core.Endpoints)
|
||||
tweakNewEndpoints func(ep *core.Endpoints)
|
||||
appProtocolEnabled bool
|
||||
numExpectedErrors int
|
||||
}{
|
||||
"update with valid app protocol, field unset, gate not enabled": {
|
||||
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")}}
|
||||
},
|
||||
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,
|
||||
},
|
||||
"update to valid app protocol, gate enabled": {
|
||||
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) {
|
||||
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: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAppProtocol, tc.appProtocolEnabled)()
|
||||
oldEndpoints := baseEndpoints.DeepCopy()
|
||||
tc.tweakOldEndpoints(oldEndpoints)
|
||||
newEndpoints := baseEndpoints.DeepCopy()
|
||||
tc.tweakNewEndpoints(newEndpoints)
|
||||
|
||||
errs := ValidateEndpointsUpdate(newEndpoints, oldEndpoints)
|
||||
if len(errs) != tc.numExpectedErrors {
|
||||
t.Errorf("Expected %d validation errors, got %d: %v", tc.numExpectedErrors, len(errs), errs)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14298,7 +14497,7 @@ func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
|
||||
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.
|
||||
// The same ip will now have a different nodeName.
|
||||
errList := ValidateEndpoints(updatedEndpoint)
|
||||
errList := ValidateEndpoints(updatedEndpoint, false)
|
||||
errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...)
|
||||
if len(errList) != 0 {
|
||||
t.Error("Endpoint should allow changing of Subset.Addresses.NodeName on update")
|
||||
@@ -14308,7 +14507,7 @@ func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
|
||||
func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
|
||||
// Check NodeName DNS validation
|
||||
endpoint := newNodeNameEndpoint("illegal*.nodename")
|
||||
errList := ValidateEndpoints(endpoint)
|
||||
errList := ValidateEndpoints(endpoint, false)
|
||||
if len(errList) == 0 {
|
||||
t.Error("Endpoint should reject invalid NodeName")
|
||||
}
|
||||
@@ -14316,7 +14515,7 @@ func TestEndpointAddressNodeNameInvalidDNSSubdomain(t *testing.T) {
|
||||
|
||||
func TestEndpointAddressNodeNameCanBeAnIPAddress(t *testing.T) {
|
||||
endpoint := newNodeNameEndpoint("10.10.1.1")
|
||||
errList := ValidateEndpoints(endpoint)
|
||||
errList := ValidateEndpoints(endpoint, false)
|
||||
if len(errList) != 0 {
|
||||
t.Error("Endpoint should accept a NodeName that is an IP address")
|
||||
}
|
||||
|
Reference in New Issue
Block a user