提交 eeffe551 编写于 作者: B bryk

Display running/failed/waiting pods in the RC cards

This gives way more information than just current/desired numbers as it
was previously. Fixes #247.
上级 643832d0
......@@ -24,13 +24,13 @@ import (
"k8s.io/kubernetes/pkg/labels"
)
// List of Replica Sets in the cluster.
// ReplicaSetList contains a list of Replica Sets in the cluster.
type ReplicaSetList struct {
// Unordered list of Replica Sets.
ReplicaSets []ReplicaSet `json:"replicaSets"`
}
// Kubernetes Replica Set (aka. Replication Controller) plus zero or more Kubernetes services that
// ReplicaSet (aka. Replication Controller) plus zero or more Kubernetes services that
// target the Replica Set.
type ReplicaSet struct {
// Name of the Replica Set.
......@@ -45,11 +45,8 @@ type ReplicaSet struct {
// Label of this Replica Set.
Labels map[string]string `json:"labels"`
// Number of pods that are currently running.
PodsRunning int `json:"podsRunning"`
// Number of pods that are pending in this Replica Set.
PodsPending int `json:"podsPending"`
// Aggergate information about pods belonging to this repolica set.
Pods ReplicaSetPodInfo `json:"pods"`
// Container images of the Replica Set.
ContainerImages []string `json:"containerImages"`
......@@ -64,38 +61,59 @@ type ReplicaSet struct {
ExternalEndpoints []Endpoint `json:"externalEndpoints"`
}
// Returns a list of all Replica Sets in the cluster.
// ReplicaSetPodInfo represents aggregate information about replica set pods.
type ReplicaSetPodInfo struct {
// Number of pods that are created.
Curret int `json:"current"`
// Number of pods that are desired in this Replica Set.
Desired int `json:"desired"`
// Number of pods that are currently running.
Running int `json:"running"`
// Number of pods that are currently waiting.
Waiting int `json:"waiting"`
// Number of pods that are failed.
Failed int `json:"failed"`
}
// GetReplicaSetList returns a list of all Replica Sets in the cluster.
func GetReplicaSetList(client *client.Client) (*ReplicaSetList, error) {
log.Printf("Getting list of all replica sets in the cluster")
replicaSets, err := client.ReplicationControllers(api.NamespaceAll).List(
unversioned.ListOptions{
LabelSelector: unversioned.LabelSelector{labels.Everything()},
FieldSelector: unversioned.FieldSelector{fields.Everything()},
})
listEverything := unversioned.ListOptions{
LabelSelector: unversioned.LabelSelector{labels.Everything()},
FieldSelector: unversioned.FieldSelector{fields.Everything()},
}
replicaSets, err := client.ReplicationControllers(api.NamespaceAll).List(listEverything)
if err != nil {
return nil, err
}
services, err := client.Services(api.NamespaceAll).List(
unversioned.ListOptions{
LabelSelector: unversioned.LabelSelector{labels.Everything()},
FieldSelector: unversioned.FieldSelector{fields.Everything()},
})
services, err := client.Services(api.NamespaceAll).List(listEverything)
if err != nil {
return nil, err
}
pods, err := client.Pods(api.NamespaceAll).List(listEverything)
if err != nil {
return nil, err
}
return getReplicaSetList(replicaSets.Items, services.Items), nil
return getReplicaSetList(replicaSets.Items, services.Items, pods.Items), nil
}
// Returns a list of all Replica Set model objects in the cluster, based on all Kubernetes
// Replica Set and Service API objects.
// The function processes all Replica Sets API objects and finds matching Services for them.
func getReplicaSetList(
replicaSets []api.ReplicationController, services []api.Service) *ReplicaSetList {
func getReplicaSetList(replicaSets []api.ReplicationController, services []api.Service,
pods []api.Pod) *ReplicaSetList {
replicaSetList := &ReplicaSetList{ReplicaSets: make([]ReplicaSet, 0)}
......@@ -111,19 +129,20 @@ func getReplicaSetList(
for _, service := range matchingServices {
internalEndpoints = append(internalEndpoints,
getInternalEndpoint(service.Name, service.Namespace, service.Spec.Ports))
for _, externalIp := range service.Status.LoadBalancer.Ingress {
for _, externalIP := range service.Status.LoadBalancer.Ingress {
externalEndpoints = append(externalEndpoints,
getExternalEndpoint(externalIp, service.Spec.Ports))
getExternalEndpoint(externalIP, service.Spec.Ports))
}
}
podInfo := getReplicaSetPodInfo(&replicaSet, pods)
replicaSetList.ReplicaSets = append(replicaSetList.ReplicaSets, ReplicaSet{
Name: replicaSet.ObjectMeta.Name,
Namespace: replicaSet.ObjectMeta.Namespace,
Description: replicaSet.Annotations[DescriptionAnnotationKey],
Labels: replicaSet.ObjectMeta.Labels,
PodsRunning: replicaSet.Status.Replicas,
PodsPending: replicaSet.Spec.Replicas - replicaSet.Status.Replicas,
Pods: podInfo,
ContainerImages: containerImages,
CreationTime: replicaSet.ObjectMeta.CreationTimestamp,
InternalEndpoints: internalEndpoints,
......@@ -134,14 +153,38 @@ func getReplicaSetList(
return replicaSetList
}
func getReplicaSetPodInfo(replicaSet *api.ReplicationController, pods []api.Pod) ReplicaSetPodInfo {
result := ReplicaSetPodInfo{
Curret: replicaSet.Status.Replicas,
Desired: replicaSet.Spec.Replicas,
}
for _, pod := range pods {
if pod.ObjectMeta.Namespace == replicaSet.ObjectMeta.Namespace &&
isLabelSelectorMatching(replicaSet.Spec.Selector, pod.ObjectMeta.Labels) {
switch pod.Status.Phase {
case api.PodRunning:
result.Running++
case api.PodPending:
result.Waiting++
case api.PodFailed:
result.Failed++
}
}
}
return result
}
// Returns all services that target the same Pods (or subset) as the given Replica Set.
func getMatchingServices(services []api.Service,
replicaSet *api.ReplicationController) []api.Service {
// TODO(bryk): Match namespace too.
var matchingServices []api.Service
for _, service := range services {
if isServiceMatchingReplicaSet(service.Spec.Selector, replicaSet.Spec.Selector) {
if service.ObjectMeta.Namespace == replicaSet.ObjectMeta.Namespace &&
isLabelSelectorMatching(service.Spec.Selector, replicaSet.Spec.Selector) {
matchingServices = append(matchingServices, service)
}
}
......@@ -150,15 +193,15 @@ func getMatchingServices(services []api.Service,
// Returns true when a Service with the given selector targets the same Pods (or subset) that
// a Replica Set with the given selector.
func isServiceMatchingReplicaSet(serviceSelector map[string]string,
replicaSetSpecSelector map[string]string) bool {
func isLabelSelectorMatching(labelSelector map[string]string,
testedObjectLabels map[string]string) bool {
// If service has no selectors, then assume it targets different Pods.
if len(serviceSelector) == 0 {
if len(labelSelector) == 0 {
return false
}
for label, value := range serviceSelector {
if rsValue, ok := replicaSetSpecSelector[label]; !ok || rsValue != value {
for label, value := range labelSelector {
if rsValue, ok := testedObjectLabels[label]; !ok || rsValue != value {
return false
}
}
......
......@@ -96,8 +96,7 @@ backendApi.ReplicaSetList;
* namespace: string,
* description: string,
* labels: !Object<string, string>,
* podsRunning: number,
* podsPending: number,
* pods: {current: number, running: number, desired: number, waiting: number, failed: number},
* containerImages: !Array<string>,
* creationTime: string,
* internalEndpoints: !Array<!backendApi.Endpoint>,
......
......@@ -24,7 +24,6 @@ limitations under the License.
{{::ctrl.replicaSet.name}}
</a>
</h3>
<kd-replica-set-card-menu replica-set="::ctrl.replicaSet"></kd-replica-set-card-menu>
</div>
<div flex class="md-caption">
......@@ -34,7 +33,19 @@ limitations under the License.
<div class="md-caption">
<div layout="row" layout-align="center end">
<span flex="60">
{{::ctrl.replicaSet.podsRunning}} pods running, {{::ctrl.replicaSet.podsPending}} pending
<ng-pluralize class="kd-replicase-card-pods-stat" count="::ctrl.replicaSet.pods.running"
when="{'one': '1 pod running,',
'other': '{} pods running,'}">
</ng-pluralize>
<span ng-if="::ctrl.replicaSet.pods.waiting" class="kd-replicase-card-pods-stat">
{{::ctrl.replicaSet.pods.waiting}} waiting,
</span>
<span ng-if="::ctrl.replicaSet.pods.failed" class="kd-replicase-card-pods-stat">
{{::ctrl.replicaSet.pods.failed}} failed,
</span>
<span class="kd-replicase-card-pods-stat">
{{::ctrl.replicaSet.pods.desired}} desired
</span>
</span>
<logs-menu flex="40" namespace="::ctrl.replicaSet.namespace"
replica-set-name="::ctrl.replicaSet.name">
......
......@@ -16,7 +16,7 @@
.kd-replicaset-card {
margin: $baseline-grid;
width: ($layout-breakpoint-lg - (2 * $baseline-grid) * 2) / 3;
width: ($layout-breakpoint-lg - (2 * $baseline-grid) * 2) / 3;
}
.kd-replicaset-card-name {
......@@ -78,6 +78,10 @@
margin-right: 2 * $baseline-grid;
}
.kd-replicase-card-pods-stat {
white-space: nowrap;
}
.kd-menu-logs-item-header {
box-sizing: inherit;
color: $foreground-2;
......
......@@ -21,7 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api"
)
func TestIsServiceMatchingReplicaSet(t *testing.T) {
func TestIsLabelSelectorMatching(t *testing.T) {
cases := []struct {
serviceSelector, replicaSetSelector map[string]string
expected bool
......@@ -42,9 +42,9 @@ func TestIsServiceMatchingReplicaSet(t *testing.T) {
map[string]string{"app": "my-name", "version": "1.1"}, true},
}
for _, c := range cases {
actual := isServiceMatchingReplicaSet(c.serviceSelector, c.replicaSetSelector)
actual := isLabelSelectorMatching(c.serviceSelector, c.replicaSetSelector)
if actual != c.expected {
t.Errorf("isServiceMatchingReplicaSet(%+v, %+v) == %+v, expected %+v",
t.Errorf("isLabelSelectorMatching(%+v, %+v) == %+v, expected %+v",
c.serviceSelector, c.replicaSetSelector, actual, c.expected)
}
}
......@@ -86,12 +86,17 @@ func TestGetReplicaSetList(t *testing.T) {
cases := []struct {
replicaSets []api.ReplicationController
services []api.Service
pods []api.Pod
expected *ReplicaSetList
}{
{nil, nil, &ReplicaSetList{ReplicaSets: []ReplicaSet{}}},
{nil, nil, nil, &ReplicaSetList{ReplicaSets: []ReplicaSet{}}},
{
[]api.ReplicationController{
{
ObjectMeta: api.ObjectMeta{
Name: "my-app-1",
Namespace: "namespace-1",
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"app": "my-name-1"},
Template: &api.PodTemplateSpec{
......@@ -100,6 +105,10 @@ func TestGetReplicaSetList(t *testing.T) {
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "my-app-2",
Namespace: "namespace-2",
},
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{"app": "my-name-2", "ver": "2"},
Template: &api.PodTemplateSpec{
......@@ -124,23 +133,98 @@ func TestGetReplicaSetList(t *testing.T) {
},
},
},
[]api.Pod{
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodFailed,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodFailed,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodPending,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-2",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodPending,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodRunning,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodSucceeded,
},
},
api.Pod{
ObjectMeta: api.ObjectMeta{
Namespace: "namespace-1",
Labels: map[string]string{"app": "my-name-1"},
},
Status: api.PodStatus{
Phase: api.PodUnknown,
},
},
},
&ReplicaSetList{
ReplicaSets: []ReplicaSet{
{
Name: "my-app-1",
Namespace: "namespace-1",
ContainerImages: []string{"my-container-image-1"},
InternalEndpoints: []Endpoint{{Host: "my-app-1.namespace-1"}},
Pods: ReplicaSetPodInfo{
Failed: 2,
Waiting: 1,
Running: 1,
},
}, {
Name: "my-app-2",
Namespace: "namespace-2",
ContainerImages: []string{"my-container-image-2"},
InternalEndpoints: []Endpoint{{Host: "my-app-2.namespace-2"}},
Pods: ReplicaSetPodInfo{},
},
},
},
},
}
for _, c := range cases {
actual := getReplicaSetList(c.replicaSets, c.services)
actual := getReplicaSetList(c.replicaSets, c.services, c.pods)
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("getReplicaSetList(%#v, %#v) == %#v, expected %#v",
t.Errorf("getReplicaSetList(%#v, %#v) == \n%#v\nexpected \n%#v\n",
c.replicaSets, c.services, actual, c.expected)
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册