Skip to content

Commit 18dab9f

Browse files
digitalfishpondbryk
authored andcommitted
Add pod details page (#740)
1 parent d7b0cec commit 18dab9f

25 files changed

+725
-106
lines changed

src/app/backend/handler/apihandler.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient,
191191
podsWs.GET("").
192192
To(apiHandler.handleGetPods).
193193
Writes(pod.PodList{}))
194+
podsWs.Route(
195+
podsWs.GET("/{namespace}/{pod}").
196+
To(apiHandler.handleGetPodDetail).
197+
Writes(pod.PodDetail{}))
194198
wsContainer.Add(podsWs)
195199

196200
deploymentsWs := new(restful.WebService)
@@ -481,6 +485,20 @@ func (apiHandler *ApiHandler) handleGetPods(
481485
response.WriteHeaderAndEntity(http.StatusCreated, result)
482486
}
483487

488+
// Handles get Pod detail API call.
489+
func (apiHandler *ApiHandler) handleGetPodDetail(request *restful.Request, response *restful.Response) {
490+
491+
namespace := request.PathParameter("namespace")
492+
podName := request.PathParameter("pod")
493+
result, err := pod.GetPodDetail(apiHandler.client, apiHandler.heapsterClient, namespace, podName)
494+
if err != nil {
495+
handleInternalError(response, err)
496+
return
497+
}
498+
499+
response.WriteHeaderAndEntity(http.StatusCreated, result)
500+
}
501+
484502
// Handles get Replication Controller detail API call.
485503
func (apiHandler *ApiHandler) handleGetReplicationControllerDetail(
486504
request *restful.Request, response *restful.Response) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 pod
16+
17+
import (
18+
"github.com/kubernetes/dashboard/resource/common"
19+
"k8s.io/kubernetes/pkg/api"
20+
)
21+
22+
// Gets restart count of given pod (total number of its containers restarts).
23+
func getRestartCount(pod api.Pod) int {
24+
restartCount := 0
25+
for _, containerStatus := range pod.Status.ContainerStatuses {
26+
restartCount += containerStatus.RestartCount
27+
}
28+
return restartCount
29+
}
30+
31+
func ToPod(pod *api.Pod, metrics *MetricsByPod) Pod {
32+
podDetail := Pod{
33+
ObjectMeta: common.NewObjectMeta(pod.ObjectMeta),
34+
TypeMeta: common.NewTypeMeta(common.ResourceKindPod),
35+
PodPhase: pod.Status.Phase,
36+
PodIP: pod.Status.PodIP,
37+
RestartCount: getRestartCount(*pod),
38+
}
39+
40+
if metrics != nil && metrics.MetricsMap[pod.Namespace] != nil {
41+
metric := metrics.MetricsMap[pod.Namespace][pod.Name]
42+
podDetail.Metrics = &metric
43+
}
44+
45+
return podDetail
46+
}
47+
48+
func ToPodDetail(pod *api.Pod, metrics *MetricsByPod) PodDetail {
49+
podDetail := PodDetail{
50+
ObjectMeta: common.NewObjectMeta(pod.ObjectMeta),
51+
TypeMeta: common.NewTypeMeta(common.ResourceKindPod),
52+
PodPhase: pod.Status.Phase,
53+
PodIP: pod.Status.PodIP,
54+
RestartCount: getRestartCount(*pod),
55+
ContainerImages: GetContainerImages(&pod.Spec),
56+
NodeName: pod.Spec.NodeName,
57+
}
58+
59+
if metrics != nil && metrics.MetricsMap[pod.Namespace] != nil {
60+
metric := metrics.MetricsMap[pod.Namespace][pod.Name]
61+
podDetail.Metrics = &metric
62+
}
63+
64+
return podDetail
65+
}
66+
67+
// GetContainerImages returns container image strings from the given pod spec.
68+
func GetContainerImages(podTemplate *api.PodSpec) []string {
69+
var containerImages []string
70+
for _, container := range podTemplate.Containers {
71+
containerImages = append(containerImages, container.Image)
72+
}
73+
return containerImages
74+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 pod
16+
17+
import (
18+
"log"
19+
20+
"k8s.io/kubernetes/pkg/api"
21+
k8sClient "k8s.io/kubernetes/pkg/client/unversioned"
22+
23+
"github.com/kubernetes/dashboard/client"
24+
"github.com/kubernetes/dashboard/resource/common"
25+
)
26+
27+
// PodDetail is a presentation layer view of Kubernetes PodDetail resource.
28+
// This means it is PodDetail plus additional augumented data we can get
29+
// from other sources (like services that target it).
30+
type PodDetail struct {
31+
ObjectMeta common.ObjectMeta `json:"objectMeta"`
32+
TypeMeta common.TypeMeta `json:"typeMeta"`
33+
34+
// Container images of the Pod.
35+
ContainerImages []string `json:"containerImages"`
36+
37+
// Status of the Pod. See Kubernetes API for reference.
38+
PodPhase api.PodPhase `json:"podPhase"`
39+
40+
// IP address of the Pod.
41+
PodIP string `json:"podIP"`
42+
43+
// Name of the Node this Pod runs on.
44+
NodeName string `json:"nodeName"`
45+
46+
// Count of containers restarts.
47+
RestartCount int `json:"restartCount"`
48+
49+
// Pod metrics.
50+
Metrics *PodMetrics `json:"metrics"`
51+
}
52+
53+
// GetPodDetail returns the details (PodDetail) of a named Pod from a particular
54+
// namespace.
55+
func GetPodDetail(client k8sClient.Interface, heapsterClient client.HeapsterClient,
56+
namespace, name string) (*PodDetail, error) {
57+
58+
log.Printf("Getting details of %s pod in %s namespace", name, namespace)
59+
60+
// TODO(floreks): Use channels.
61+
pod, err := client.Pods(namespace).Get(name)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
metrics, err := getPodMetrics([]api.Pod{*pod}, heapsterClient)
67+
if err != nil {
68+
log.Printf("Skipping Heapster metrics because of error: %s\n", err)
69+
}
70+
71+
podDetail := ToPodDetail(pod, metrics)
72+
return &podDetail, nil
73+
};

src/app/backend/resource/pod/podlist.go

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,12 @@ type Pod struct {
3737
ObjectMeta common.ObjectMeta `json:"objectMeta"`
3838
TypeMeta common.TypeMeta `json:"typeMeta"`
3939

40-
// Container images of the Pod.
41-
ContainerImages []string `json:"containerImages"`
42-
4340
// Status of the Pod. See Kubernetes API for reference.
4441
PodPhase api.PodPhase `json:"podPhase"`
4542

4643
// IP address of the Pod.
4744
PodIP string `json:"podIP"`
4845

49-
// Name of the Node this Pod runs on.
50-
NodeName string `json:"nodeName"`
51-
5246
// Count of containers restarts.
5347
RestartCount int `json:"restartCount"`
5448

@@ -92,28 +86,9 @@ func CreatePodList(pods []api.Pod, heapsterClient client.HeapsterClient) PodList
9286
}
9387

9488
for _, pod := range pods {
95-
podDetail := Pod{
96-
ObjectMeta: common.NewObjectMeta(pod.ObjectMeta),
97-
TypeMeta: common.NewTypeMeta(common.ResourceKindPod),
98-
PodPhase: pod.Status.Phase,
99-
PodIP: pod.Status.PodIP,
100-
RestartCount: getRestartCount(pod),
101-
}
102-
if metrics != nil && metrics.MetricsMap[pod.Namespace] != nil {
103-
metric := metrics.MetricsMap[pod.Namespace][pod.Name]
104-
podDetail.Metrics = &metric
105-
}
89+
podDetail := ToPod(&pod, metrics)
10690
podList.Pods = append(podList.Pods, podDetail)
10791
}
10892

10993
return podList
11094
}
111-
112-
// Gets restart count of given pod (total number of its containers restarts).
113-
func getRestartCount(pod api.Pod) int {
114-
restartCount := 0
115-
for _, containerStatus := range pod.Status.ContainerStatuses {
116-
restartCount += containerStatus.RestartCount
117-
}
118-
return restartCount
119-
}

src/app/externs/backendapi.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,28 @@ backendApi.DeleteReplicationControllerSpec;
269269
* @typedef {{
270270
* objectMeta: !backendApi.ObjectMeta,
271271
* typeMeta: !backendApi.TypeMeta,
272-
* status: string,
272+
* podPhase: string,
273273
* podIP: string,
274-
* nodeName: string,
275274
* restartCount: number,
276275
* metrics: backendApi.PodMetrics
277276
* }}
278277
*/
279278
backendApi.Pod;
280279

280+
/**
281+
* @typedef {{
282+
* objectMeta: !backendApi.ObjectMeta,
283+
* typeMeta: !backendApi.TypeMeta,
284+
* containerImages: !Array<string>,
285+
* podPhase: string,
286+
* podIP: string,
287+
* nodeName: string,
288+
* restartCount: number,
289+
* metrics: backendApi.PodMetrics
290+
* }}
291+
*/
292+
backendApi.PodDetail;
293+
281294
/**
282295
* @typedef {{
283296
* objectMeta: !backendApi.ObjectMeta,

src/app/frontend/chrome/chrome.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
width: 42px;
2121
}
2222

23+
.kd-middle-ellipsised-link {
24+
display: block;
25+
}
26+
2327
.kd-toolbar {
2428
box-shadow: $whiteframe-shadow-1dp;
2529
height: $toolbar-height-size-base;

src/app/frontend/index_module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import routeConfig from './index_route';
3030
import serviceDetailModule from './servicedetail/servicedetail_module';
3131
import serviceListModule from './servicelist/servicelist_module';
3232
import workloadsModule from './workloads/workloads_module';
33+
import podDetailModule from './poddetail/poddetail_module';
3334

3435
export default angular
3536
.module(
@@ -54,6 +55,7 @@ export default angular
5455
workloadsModule.name,
5556
serviceDetailModule.name,
5657
serviceListModule.name,
58+
podDetailModule.name,
5759
])
5860
.config(indexConfig)
5961
.config(routeConfig);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!--
2+
Copyright 2015 Google Inc. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
<div layout="column">
18+
<md-content>
19+
<md-tabs md-border-bottom md-dynamic-height>
20+
<md-tab label="Overview">
21+
<kd-pod-info pod="::ctrl.podDetail"></kd-pod-info>
22+
</md-tab>
23+
</md-tabs>
24+
</md-content>
25+
</div>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
/**
16+
* @final
17+
*/
18+
export class PodDetailController {
19+
/**
20+
* @ngInject
21+
* @param {!backendApi.PodDetail} podDetail
22+
*/
23+
constructor(podDetail) {
24+
/** @export {!backendApi.PodDetail} */
25+
this.podDetail = podDetail;
26+
}
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
import componentsModule from 'common/components/components_module';
16+
import eventsModule from 'events/events_module';
17+
import filtersModule from 'common/filters/filters_module';
18+
import stateConfig from './poddetail_stateconfig';
19+
import {podInfoComponent} from './podinfo_component';
20+
21+
/**
22+
* Angular module for the Replica Set details view.
23+
*
24+
* The view shows detailed view of a Replica Set.
25+
*/
26+
export default angular
27+
.module(
28+
'kubernetesDashboard.podDetail',
29+
[
30+
'ngMaterial',
31+
'ngResource',
32+
'ui.router',
33+
componentsModule.name,
34+
filtersModule.name,
35+
eventsModule.name,
36+
])
37+
.config(stateConfig)
38+
.component('kdPodInfo', podInfoComponent);

0 commit comments

Comments
 (0)