Merge pull request #3447 from Nefelim4ag/fix-node-ports

fix: nodePort #2704 - publish ready endpoints
This commit is contained in:
Kubernetes Prow Robot 2023-09-14 19:12:12 -07:00 committed by GitHub
commit 435eb2056f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 158 additions and 1 deletions

View File

@ -590,6 +590,30 @@ func extractLoadBalancerTargets(svc *v1.Service, resolveLoadBalancerHostname boo
return targets return targets
} }
func isPodStatusReady(status v1.PodStatus) bool {
_, condition := getPodCondition(&status, v1.PodReady)
return condition != nil && condition.Status == v1.ConditionTrue
}
func getPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
if status == nil {
return -1, nil
}
return getPodConditionFromList(status.Conditions, conditionType)
}
func getPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
if conditions == nil {
return -1, nil
}
for i := range conditions {
if conditions[i].Type == conditionType {
return i, &conditions[i]
}
}
return -1, nil
}
func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targets, error) { func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targets, error) {
var ( var (
internalIPs endpoint.Targets internalIPs endpoint.Targets
@ -615,6 +639,8 @@ func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targe
return nil, err return nil, err
} }
var nodesReady []*v1.Node
var nodesRunning []*v1.Node
for _, v := range pods { for _, v := range pods {
if v.Status.Phase == v1.PodRunning { if v.Status.Phase == v1.PodRunning {
node, err := sc.nodeInformer.Lister().Get(v.Spec.NodeName) node, err := sc.nodeInformer.Lister().Get(v.Spec.NodeName)
@ -622,12 +648,33 @@ func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targe
log.Debugf("Unable to find node where Pod %s is running", v.Spec.Hostname) log.Debugf("Unable to find node where Pod %s is running", v.Spec.Hostname)
continue continue
} }
if _, ok := nodesMap[node]; !ok { if _, ok := nodesMap[node]; !ok {
nodesMap[node] = *new(struct{}) nodesMap[node] = *new(struct{})
nodesRunning = append(nodesRunning, node)
if isPodStatusReady(v.Status) {
nodesReady = append(nodesReady, node)
// Check pod not terminating
if v.GetDeletionTimestamp() == nil {
nodes = append(nodes, node) nodes = append(nodes, node)
} }
} }
} }
}
}
if len(nodes) > 0 {
// Works same as service endpoints
} else if len(nodesReady) > 0 {
// 2 level of panic modes as safe guard, because old wrong behavior can be used by someone
// Publish all endpoints not always a bad thing
log.Debugf("All pods in terminating state, use ready")
nodes = nodesReady
} else {
log.Debugf("All pods not ready, use all running")
nodes = nodesRunning
}
default: default:
nodes, err = sc.nodeInformer.Lister().List(labels.Everything()) nodes, err = sc.nodeInformer.Lister().List(labels.Everything())
if err != nil { if err != nil {

View File

@ -1630,7 +1630,9 @@ func TestServiceSourceNodePortServices(t *testing.T) {
podNames []string podNames []string
nodeIndex []int nodeIndex []int
phases []v1.PodPhase phases []v1.PodPhase
conditions []v1.PodCondition
labelSelector labels.Selector labelSelector labels.Selector
deletionTimestamp []*metav1.Time
}{ }{
{ {
title: "annotated NodePort services return an endpoint with IP addresses of the cluster's nodes", title: "annotated NodePort services return an endpoint with IP addresses of the cluster's nodes",
@ -1817,6 +1819,8 @@ func TestServiceSourceNodePortServices(t *testing.T) {
podNames: []string{"pod-0"}, podNames: []string{"pod-0"},
nodeIndex: []int{1}, nodeIndex: []int{1},
phases: []v1.PodPhase{v1.PodRunning}, phases: []v1.PodPhase{v1.PodRunning},
conditions: []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionFalse}},
deletionTimestamp: []*metav1.Time{{}},
}, },
{ {
title: "annotated NodePort services with ExternalTrafficPolicy=Local and multiple pods on a single node return an endpoint with unique IP addresses of the cluster's nodes where pods is running only", title: "annotated NodePort services with ExternalTrafficPolicy=Local and multiple pods on a single node return an endpoint with unique IP addresses of the cluster's nodes where pods is running only",
@ -1859,6 +1863,110 @@ func TestServiceSourceNodePortServices(t *testing.T) {
podNames: []string{"pod-0", "pod-1"}, podNames: []string{"pod-0", "pod-1"},
nodeIndex: []int{1, 1}, nodeIndex: []int{1, 1},
phases: []v1.PodPhase{v1.PodRunning, v1.PodRunning}, phases: []v1.PodPhase{v1.PodRunning, v1.PodRunning},
conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.PodReady, Status: v1.ConditionFalse},
},
deletionTimestamp: []*metav1.Time{{},{}},
},
{
title: "annotated NodePort services with ExternalTrafficPolicy=Local return pods in Ready & Running state",
svcNamespace: "testing",
svcName: "foo",
svcType: v1.ServiceTypeNodePort,
svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
labels: map[string]string{},
annotations: map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
expected: []*endpoint.Endpoint{
{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA},
},
nodes: []*v1.Node{{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
},
},
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "node2",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
},
},
}},
podNames: []string{"pod-0", "pod-1"},
nodeIndex: []int{0, 1},
phases: []v1.PodPhase{v1.PodRunning, v1.PodRunning},
conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionTrue},
{Type: v1.PodReady, Status: v1.ConditionFalse},
},
deletionTimestamp: []*metav1.Time{{},{}},
},
{
title: "annotated NodePort services with ExternalTrafficPolicy=Local return pods in Ready & Running state & not in Terminating",
svcNamespace: "testing",
svcName: "foo",
svcType: v1.ServiceTypeNodePort,
svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeLocal,
labels: map[string]string{},
annotations: map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
expected: []*endpoint.Endpoint{
{DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV},
{DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA},
},
nodes: []*v1.Node{{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "54.10.11.1"},
{Type: v1.NodeInternalIP, Address: "10.0.1.1"},
},
},
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "node2",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "54.10.11.2"},
{Type: v1.NodeInternalIP, Address: "10.0.1.2"},
},
},
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "node3",
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{Type: v1.NodeExternalIP, Address: "54.10.11.3"},
{Type: v1.NodeInternalIP, Address: "10.0.1.3"},
},
},
}},
podNames: []string{"pod-0", "pod-1", "pod-2"},
nodeIndex: []int{0, 1, 2},
phases: []v1.PodPhase{v1.PodRunning, v1.PodRunning, v1.PodRunning},
conditions: []v1.PodCondition{
{Type: v1.PodReady, Status: v1.ConditionTrue},
{Type: v1.PodReady, Status: v1.ConditionFalse},
{Type: v1.PodReady, Status: v1.ConditionTrue},
},
deletionTimestamp: []*metav1.Time{nil, nil, {}},
}, },
{ {
title: "access=private annotation NodePort services return an endpoint with private IP addresses of the cluster's nodes", title: "access=private annotation NodePort services return an endpoint with private IP addresses of the cluster's nodes",
@ -2150,9 +2258,11 @@ func TestServiceSourceNodePortServices(t *testing.T) {
Name: podname, Name: podname,
Labels: tc.labels, Labels: tc.labels,
Annotations: tc.annotations, Annotations: tc.annotations,
DeletionTimestamp: tc.deletionTimestamp[i],
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: tc.phases[i], Phase: tc.phases[i],
Conditions: []v1.PodCondition{tc.conditions[i]},
}, },
} }