Skip to content

Commit 12e827d

Browse files
author
Sebastian Florek
committed
Refactor endpoints and generify selectors matching backend
1 parent e153a8c commit 12e827d

File tree

15 files changed

+755
-617
lines changed

15 files changed

+755
-617
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright 2015 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package common
16+
17+
import (
18+
"bytes"
19+
20+
"k8s.io/kubernetes/pkg/api"
21+
22+
"github.com/kubernetes/dashboard/resource/node"
23+
)
24+
25+
// Endpoint describes an endpoint that is host and a list of available ports for that host.
26+
type Endpoint struct {
27+
// Hostname, either as a domain name or IP address.
28+
Host string `json:"host"`
29+
30+
// List of ports opened for this endpoint on the hostname.
31+
Ports []ServicePort `json:"ports"`
32+
}
33+
34+
// GetExternalEndpoints returns array of external endpoints for resources targeted by given
35+
// selector.
36+
func GetExternalEndpoints(resourceSelector map[string]string, allPods []api.Pod,
37+
service api.Service, nodes []api.Node) []Endpoint {
38+
39+
var externalEndpoints []Endpoint
40+
resourcePods := FilterPodsBySelector(allPods, resourceSelector)
41+
42+
if service.Spec.Type == api.ServiceTypeNodePort {
43+
externalEndpoints = getNodePortEndpoints(resourcePods, service, nodes)
44+
} else if service.Spec.Type == api.ServiceTypeLoadBalancer {
45+
for _, ingress := range service.Status.LoadBalancer.Ingress {
46+
externalEndpoints = append(externalEndpoints, getExternalEndpoint(
47+
ingress, service.Spec.Ports))
48+
}
49+
50+
if len(externalEndpoints) == 0 {
51+
externalEndpoints = getNodePortEndpoints(resourcePods,
52+
service, nodes)
53+
}
54+
}
55+
56+
if len(externalEndpoints) == 0 && (service.Spec.Type == api.ServiceTypeNodePort ||
57+
service.Spec.Type == api.ServiceTypeLoadBalancer) {
58+
externalEndpoints = getLocalhostEndpoints(service)
59+
}
60+
61+
return externalEndpoints
62+
}
63+
64+
// GetInternalEndpoint returns internal endpoint name for the given service properties, e.g.,
65+
// "my-service.namespace 80/TCP" or "my-service 53/TCP,53/UDP".
66+
func GetInternalEndpoint(serviceName, namespace string, ports []api.ServicePort) Endpoint {
67+
name := serviceName
68+
69+
if namespace != api.NamespaceDefault && len(namespace) > 0 && len(serviceName) > 0 {
70+
bufferName := bytes.NewBufferString(name)
71+
bufferName.WriteString(".")
72+
bufferName.WriteString(namespace)
73+
name = bufferName.String()
74+
}
75+
76+
return Endpoint{
77+
Host: name,
78+
Ports: GetServicePorts(ports),
79+
}
80+
}
81+
82+
// Returns array of external endpoints for specified pods.
83+
func getNodePortEndpoints(pods []api.Pod, service api.Service, nodes []api.Node) []Endpoint {
84+
var externalEndpoints []Endpoint
85+
var addresses []api.NodeAddress
86+
87+
for _, pod := range pods {
88+
node := node.GetNodeByName(nodes, pod.Spec.NodeName)
89+
if node == nil {
90+
continue
91+
}
92+
93+
addresses = append(addresses, node.Status.Addresses...)
94+
}
95+
96+
addresses = getUniqueExternalAddresses(addresses)
97+
98+
for _, address := range addresses {
99+
for _, port := range service.Spec.Ports {
100+
externalEndpoints = append(externalEndpoints, Endpoint{
101+
Host: address.Address,
102+
Ports: []ServicePort{
103+
{
104+
Protocol: port.Protocol,
105+
Port: port.NodePort,
106+
},
107+
},
108+
})
109+
}
110+
}
111+
112+
return externalEndpoints
113+
}
114+
115+
// Returns localhost endpoints for specified node port or load balancer service.
116+
func getLocalhostEndpoints(service api.Service) []Endpoint {
117+
var externalEndpoints []Endpoint
118+
for _, port := range service.Spec.Ports {
119+
externalEndpoints = append(externalEndpoints, Endpoint{
120+
Host: "localhost",
121+
Ports: []ServicePort{
122+
{
123+
Protocol: port.Protocol,
124+
Port: port.NodePort,
125+
},
126+
},
127+
})
128+
}
129+
return externalEndpoints
130+
}
131+
132+
// Returns external endpoint name for the given service properties.
133+
func getExternalEndpoint(ingress api.LoadBalancerIngress, ports []api.ServicePort) Endpoint {
134+
var host string
135+
if ingress.Hostname != "" {
136+
host = ingress.Hostname
137+
} else {
138+
host = ingress.IP
139+
}
140+
return Endpoint{
141+
Host: host,
142+
Ports: GetServicePorts(ports),
143+
}
144+
}
145+
146+
// Returns only unique external ip addresses.
147+
func getUniqueExternalAddresses(addresses []api.NodeAddress) []api.NodeAddress {
148+
visited := make(map[string]bool, 0)
149+
result := make([]api.NodeAddress, 0)
150+
151+
for _, elem := range addresses {
152+
if !visited[elem.Address] && elem.Type == api.NodeExternalIP {
153+
visited[elem.Address] = true
154+
result = append(result, elem)
155+
}
156+
}
157+
158+
return result
159+
}

src/app/backend/resource/common/podinfo.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ package common
1616

1717
import (
1818
"k8s.io/kubernetes/pkg/api"
19-
"k8s.io/kubernetes/pkg/api/unversioned"
20-
"k8s.io/kubernetes/pkg/labels"
2119
)
2220

2321
// PodInfo represents aggregate information about controller's pods.
@@ -62,38 +60,3 @@ func GetPodInfo(current int, desired int, pods []api.Pod) PodInfo {
6260

6361
return result
6462
}
65-
66-
// IsLabelSelectorMatching returns true when an object with the given
67-
// selector targets the same Pods (or subset) that
68-
// the tested object with the given selector.
69-
func IsLabelSelectorMatching(labelSelector map[string]string,
70-
testedObjectLabels map[string]string) bool {
71-
72-
// If service has no selectors, then assume it targets different Pods.
73-
if len(labelSelector) == 0 {
74-
return false
75-
}
76-
for label, value := range labelSelector {
77-
if rsValue, ok := testedObjectLabels[label]; !ok || rsValue != value {
78-
return false
79-
}
80-
}
81-
return true
82-
}
83-
84-
// GetMatchingPods returns pods matching the given selector and namespace
85-
func GetMatchingPods(labelSelector *unversioned.LabelSelector, namespace string,
86-
pods []api.Pod) []api.Pod {
87-
88-
selector, _ := unversioned.LabelSelectorAsSelector(labelSelector)
89-
90-
var matchingPods []api.Pod
91-
for _, pod := range pods {
92-
if pod.ObjectMeta.Namespace == namespace &&
93-
selector.Matches(labels.Set(pod.ObjectMeta.Labels)) {
94-
matchingPods = append(matchingPods, pod)
95-
}
96-
}
97-
98-
return matchingPods
99-
}

src/app/backend/resource/common/types.go

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
package common
1616

1717
import (
18-
"bytes"
19-
2018
"k8s.io/kubernetes/pkg/api"
2119
"k8s.io/kubernetes/pkg/api/unversioned"
2220
)
@@ -65,15 +63,6 @@ type TypeMeta struct {
6563
Kind ResourceKind `json:"kind,omitempty"`
6664
}
6765

68-
// Endpoint describes an endpoint that is host and a list of available ports for that host.
69-
type Endpoint struct {
70-
// Hostname, either as a domain name or IP address.
71-
Host string `json:"host"`
72-
73-
// List of ports opened for this endpoint on the hostname.
74-
Ports []ServicePort `json:"ports"`
75-
}
76-
7766
// ServicePort is a pair of port and protocol, e.g. a service endpoint.
7867
type ServicePort struct {
7968
// Positive port number.
@@ -169,24 +158,6 @@ var kindToAPIPathMapping = map[string]string{
169158
ResourceKindReplicaSet: "replicasets",
170159
}
171160

172-
// GetInternalEndpoint returns internal endpoint name for the given service properties, e.g.,
173-
// "my-service.namespace 80/TCP" or "my-service 53/TCP,53/UDP".
174-
func GetInternalEndpoint(serviceName, namespace string, ports []api.ServicePort) Endpoint {
175-
176-
name := serviceName
177-
if namespace != api.NamespaceDefault && len(namespace) > 0 && len(serviceName) > 0 {
178-
bufferName := bytes.NewBufferString(name)
179-
bufferName.WriteString(".")
180-
bufferName.WriteString(namespace)
181-
name = bufferName.String()
182-
}
183-
184-
return Endpoint{
185-
Host: name,
186-
Ports: GetServicePorts(ports),
187-
}
188-
}
189-
190161
// GetServicePorts returns human readable name for the given service ports list.
191162
func GetServicePorts(apiPorts []api.ServicePort) []ServicePort {
192163
var ports []ServicePort
@@ -195,3 +166,49 @@ func GetServicePorts(apiPorts []api.ServicePort) []ServicePort {
195166
}
196167
return ports
197168
}
169+
170+
// IsLabelSelectorMatching returns true when an object with the given
171+
// selector targets the same Resources (or subset) that
172+
// the tested object with the given selector.
173+
func IsLabelSelectorMatching(labelSelector map[string]string,
174+
testedObjectLabels map[string]string) bool {
175+
176+
// If there are no label selectors, then assume it targets different Resource.
177+
if len(labelSelector) == 0 {
178+
return false
179+
}
180+
181+
for label, value := range labelSelector {
182+
if rsValue, ok := testedObjectLabels[label]; !ok || rsValue != value {
183+
return false
184+
}
185+
}
186+
187+
return true
188+
}
189+
190+
func FilterNamespacedPodsBySelector(pods []api.Pod, namespace string,
191+
resourceSelector map[string]string) []api.Pod {
192+
193+
var matchingPods []api.Pod
194+
for _, pod := range pods {
195+
if pod.ObjectMeta.Namespace == namespace &&
196+
IsLabelSelectorMatching(resourceSelector, pod.Labels) {
197+
matchingPods = append(matchingPods, pod)
198+
}
199+
}
200+
201+
return matchingPods
202+
}
203+
204+
// Returns pods targeted by given selector.
205+
func FilterPodsBySelector(pods []api.Pod, resourceSelector map[string]string) []api.Pod {
206+
207+
var matchingPods []api.Pod
208+
for _, pod := range pods {
209+
if IsLabelSelectorMatching(resourceSelector, pod.Labels) {
210+
matchingPods = append(matchingPods, pod)
211+
}
212+
}
213+
return matchingPods
214+
}

src/app/backend/resource/deployment/deploymentlist.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ func getDeploymentList(deployments []extensions.Deployment,
110110

111111
for _, deployment := range deployments {
112112

113-
matchingPods := common.GetMatchingPods(deployment.Spec.Selector,
114-
deployment.ObjectMeta.Namespace, pods)
113+
matchingPods := common.FilterNamespacedPodsBySelector(pods, deployment.ObjectMeta.Namespace,
114+
deployment.Spec.Selector.MatchLabels)
115115
podInfo := getPodInfo(&deployment, matchingPods)
116116

117117
deploymentList.Deployments = append(deploymentList.Deployments,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2015 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package node
16+
17+
import (
18+
"k8s.io/kubernetes/pkg/api"
19+
)
20+
21+
// GetNodeByName returns the node with the given name from the list
22+
func GetNodeByName(nodes []api.Node, nodeName string) *api.Node {
23+
for _, node := range nodes {
24+
if node.ObjectMeta.Name == nodeName {
25+
return &node
26+
}
27+
}
28+
29+
return nil
30+
}

src/app/backend/resource/replicaset/replicasetdetail.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ func GetReplicaSetDetail(client k8sClient.Interface, heapsterClient client.Heaps
8080
func getReplicaSetDetail(replicaSet *extensions.ReplicaSet, heapsterClient client.HeapsterClient,
8181
events *common.EventList, pods []api.Pod) ReplicaSetDetail {
8282

83-
matchingPods := common.GetMatchingPods(replicaSet.Spec.Selector,
84-
replicaSet.ObjectMeta.Namespace, pods)
83+
matchingPods := common.FilterNamespacedPodsBySelector(pods, replicaSet.ObjectMeta.Namespace,
84+
replicaSet.Spec.Selector.MatchLabels)
8585

8686
podInfo := getPodInfo(replicaSet, matchingPods)
8787

src/app/backend/resource/replicaset/replicasetlist.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"github.com/kubernetes/dashboard/resource/common"
2121
"github.com/kubernetes/dashboard/resource/replicationcontroller"
22+
2223
"k8s.io/kubernetes/pkg/api"
2324
k8serrors "k8s.io/kubernetes/pkg/api/errors"
2425
"k8s.io/kubernetes/pkg/apis/extensions"
@@ -109,9 +110,8 @@ func getReplicaSetList(replicaSets []extensions.ReplicaSet,
109110
}
110111

111112
for _, replicaSet := range replicaSets {
112-
113-
matchingPods := common.GetMatchingPods(replicaSet.Spec.Selector,
114-
replicaSet.ObjectMeta.Namespace, pods)
113+
matchingPods := common.FilterNamespacedPodsBySelector(pods, replicaSet.ObjectMeta.Namespace,
114+
replicaSet.Spec.Selector.MatchLabels)
115115
podInfo := getPodInfo(&replicaSet, matchingPods)
116116

117117
replicaSetList.ReplicaSets = append(replicaSetList.ReplicaSets,

0 commit comments

Comments
 (0)