Adding AppProtocol to Service and Endpoints Ports

This commit is contained in:
Rob Scott
2020-02-18 17:30:57 -08:00
parent 4e79344501
commit 6a33727632
29 changed files with 1598 additions and 986 deletions

View File

@@ -65,6 +65,7 @@ go_test(
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
],
)

View File

@@ -444,17 +444,14 @@ func (e *EndpointController) syncService(key string) error {
} else {
for i := range service.Spec.Ports {
servicePort := &service.Spec.Ports[i]
portName := servicePort.Name
portProto := servicePort.Protocol
portNum, err := podutil.FindPort(pod, servicePort)
if err != nil {
klog.V(4).Infof("Failed to find port for service %s/%s: %v", service.Namespace, service.Name, err)
continue
}
epp := endpointPortFromServicePort(servicePort, portNum)
var readyEps, notReadyEps int
epp := &v1.EndpointPort{Name: portName, Port: int32(portNum), Protocol: portProto}
subsets, readyEps, notReadyEps = addEndpointSubset(subsets, pod, epa, epp, tolerateUnreadyEndpoints)
totalReadyEps = totalReadyEps + readyEps
totalNotReadyEps = totalNotReadyEps + notReadyEps
@@ -608,3 +605,15 @@ func shouldPodBeInEndpoints(pod *v1.Pod) bool {
return true
}
}
func endpointPortFromServicePort(servicePort *v1.ServicePort, portNum int) *v1.EndpointPort {
epp := &v1.EndpointPort{
Name: servicePort.Name,
Port: int32(portNum),
Protocol: servicePort.Protocol,
}
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAppProtocol) {
epp.AppProtocol = servicePort.AppProtocol
}
return epp
}

View File

@@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/pkg/controller"
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
var alwaysReady = func() bool { return true }
@@ -1947,6 +1948,55 @@ func TestSyncEndpointsServiceNotFound(t *testing.T) {
endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "DELETE", nil)
}
func TestEndpointPortFromServicePort(t *testing.T) {
http := utilpointer.StringPtr("http")
testCases := map[string]struct {
featureGateEnabled bool
serviceAppProtocol *string
expectedEndpointsAppProtocol *string
}{
"feature gate disabled, empty app protocol": {
featureGateEnabled: false,
serviceAppProtocol: nil,
expectedEndpointsAppProtocol: nil,
},
"feature gate disabled, 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,
expectedEndpointsAppProtocol: http,
},
}
for name, tc := range testCases {
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)
if epp.AppProtocol != tc.expectedEndpointsAppProtocol {
t.Errorf("Expected Endpoints AppProtocol to be %s, got %s", stringVal(tc.expectedEndpointsAppProtocol), stringVal(epp.AppProtocol))
}
})
}
}
func stringVal(str *string) string {
if str == nil {
return "nil"
}
return *str
}
func podChangedHelper(oldPod, newPod *v1.Pod, endpointChanged endpointutil.EndpointsMatch) bool {
podChanged, _ := endpointutil.PodChanged(oldPod, newPod, endpointChanged)
return podChanged

View File

@@ -117,9 +117,10 @@ func getEndpointPorts(service *corev1.Service, pod *corev1.Pod) []discovery.Endp
i32PortNum := int32(portNum)
endpointPorts = append(endpointPorts, discovery.EndpointPort{
Name: &portName,
Port: &i32PortNum,
Protocol: &portProto,
Name: &portName,
Port: &i32PortNum,
Protocol: &portProto,
AppProtocol: servicePort.AppProtocol,
})
}

View File

@@ -353,6 +353,97 @@ func TestServiceControllerKey(t *testing.T) {
}
}
func TestGetEndpointPorts(t *testing.T) {
protoTCP := v1.ProtocolTCP
testCases := map[string]struct {
service *v1.Service
pod *v1.Pod
expectedPorts []*discovery.EndpointPort
}{
"service with AppProtocol on one port": {
service: &v1.Service{
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{
Name: "http",
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: protoTCP,
AppProtocol: utilpointer.StringPtr("example.com/custom-protocol"),
}},
},
},
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{{
Ports: []v1.ContainerPort{},
}},
},
},
expectedPorts: []*discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Port: utilpointer.Int32Ptr(80),
Protocol: &protoTCP,
AppProtocol: utilpointer.StringPtr("example.com/custom-protocol"),
}},
},
"service with named port and AppProtocol on one port": {
service: &v1.Service{
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{
Name: "http",
Port: 80,
TargetPort: intstr.FromInt(80),
Protocol: protoTCP,
}, {
Name: "https",
Protocol: protoTCP,
TargetPort: intstr.FromString("https"),
AppProtocol: utilpointer.StringPtr("https"),
}},
},
},
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{{
Ports: []v1.ContainerPort{{
Name: "https",
ContainerPort: int32(443),
Protocol: protoTCP,
}},
}},
},
},
expectedPorts: []*discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Port: utilpointer.Int32Ptr(80),
Protocol: &protoTCP,
}, {
Name: utilpointer.StringPtr("https"),
Port: utilpointer.Int32Ptr(443),
Protocol: &protoTCP,
AppProtocol: utilpointer.StringPtr("https"),
}},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
actualPorts := getEndpointPorts(tc.service, tc.pod)
if len(actualPorts) != len(tc.expectedPorts) {
t.Fatalf("Expected %d ports, got %d", len(tc.expectedPorts), len(actualPorts))
}
for i, actualPort := range actualPorts {
if !reflect.DeepEqual(&actualPort, tc.expectedPorts[i]) {
t.Errorf("Expected port: %+v, got %+v", tc.expectedPorts[i], &actualPort)
}
}
})
}
}
// Test helpers
func newPod(n int, namespace string, ready bool, nPorts int) *v1.Pod {