Skip to content

Commit b52f73d

Browse files
committed
Display CPU metrics and hide metrics when heapster not running
1 parent 58b776b commit b52f73d

File tree

14 files changed

+307
-159
lines changed

14 files changed

+307
-159
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"q": "~1.4.1",
6262
"semver": "~5.1.0",
6363
"uglify-save-license": "~0.4.1",
64+
"watchify": "^3.7.0",
6465
"webpack-stream": "~3.1.0",
6566
"wiredep": "~3.0.0",
6667
"wrench": "~1.5.8"

src/app/backend/apihandler.go

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

2323
restful "github.com/emicklei/go-restful"
2424
client "k8s.io/kubernetes/pkg/client/unversioned"
25-
unversioned "k8s.io/kubernetes/pkg/client/unversioned"
2625
)
2726

2827
const (
@@ -56,7 +55,7 @@ func FormatResponseLog(resp *restful.Response, req *restful.Request) string {
5655
}
5756

5857
// Creates a new HTTP handler that handles all requests to the API of the backend.
59-
func CreateHttpApiHandler(client *client.Client, heapsterClient *unversioned.RESTClient) http.Handler {
58+
func CreateHttpApiHandler(client *client.Client, heapsterClient HeapsterClient) http.Handler {
6059
apiHandler := ApiHandler{client, heapsterClient}
6160
wsContainer := restful.NewContainer()
6261

@@ -181,7 +180,7 @@ func CreateHttpApiHandler(client *client.Client, heapsterClient *unversioned.RES
181180

182181
type ApiHandler struct {
183182
client *client.Client
184-
heapsterClient *unversioned.RESTClient
183+
heapsterClient HeapsterClient
185184
}
186185

187186
// Handles deploy API call.

src/app/backend/heapsterclient.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,23 @@
1515
package main
1616

1717
import (
18+
"bytes"
1819
"log"
1920

20-
"k8s.io/kubernetes/pkg/client/unversioned"
2121
client "k8s.io/kubernetes/pkg/client/unversioned"
22-
23-
"bytes"
2422
)
2523

24+
// Clients for making requests to a Heapster instance.
25+
type HeapsterClient interface {
26+
// Creates a new GET http request to heapster.
27+
Get() *client.Request
28+
}
29+
2630
// Creates new Heapster REST client. When heapsterHost param is empty string the function
2731
// assumes that it is running inside a Kubernetes cluster and connects via service proxy.
2832
// heapsterHost param is in the format of protocol://address:port, e.g., http://localhost:8002.
2933
func CreateHeapsterRESTClient(heapsterHost string, apiclient *client.Client) (
30-
*unversioned.RESTClient, error) {
34+
HeapsterClient, error) {
3135

3236
cfg := client.Config{}
3337

src/app/backend/replicasetcommon.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type ReplicaSetPodInfo struct {
4646
}
4747

4848
// Returns structure containing ReplicaSet and Pods for the given replica set.
49-
func getRawReplicaSetWithPods(client *client.Client, namespace, name string) (
49+
func getRawReplicaSetWithPods(client client.Interface, namespace, name string) (
5050
*ReplicaSetWithPods, error) {
5151
replicaSet, err := client.ReplicationControllers(namespace).Get(name)
5252
if err != nil {
@@ -72,7 +72,7 @@ func getRawReplicaSetWithPods(client *client.Client, namespace, name string) (
7272
}
7373

7474
// Retrieves Pod list that belongs to a Replica Set.
75-
func getRawReplicaSetPods(client *client.Client, namespace, name string) (*api.PodList, error) {
75+
func getRawReplicaSetPods(client client.Interface, namespace, name string) (*api.PodList, error) {
7676
replicaSetAndPods, err := getRawReplicaSetWithPods(client, namespace, name)
7777
if err != nil {
7878
return nil, err

src/app/backend/replicasetdetail.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"k8s.io/kubernetes/pkg/api"
2222
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
2323
client "k8s.io/kubernetes/pkg/client/unversioned"
24-
unversionedClient "k8s.io/kubernetes/pkg/client/unversioned"
2524
"k8s.io/kubernetes/pkg/fields"
2625
"k8s.io/kubernetes/pkg/labels"
2726
)
@@ -51,6 +50,9 @@ type ReplicaSetDetail struct {
5150

5251
// Detailed information about service related to Replica Set.
5352
Services []ServiceDetail `json:"services"`
53+
54+
// True when the data contains at least one pod with metrics information, false otherwise.
55+
HasMetrics bool `json:"hasMetrics"`
5456
}
5557

5658
// Detailed information about a Pod that belongs to a Replica Set.
@@ -74,7 +76,7 @@ type ReplicaSetPod struct {
7476
RestartCount int `json:"restartCount"`
7577

7678
// Pod metrics.
77-
Metrics PodMetrics `json:"metrics"`
79+
Metrics *PodMetrics `json:"metrics"`
7880
}
7981

8082
// Detailed information about a Service connected to Replica Set.
@@ -121,7 +123,7 @@ type ReplicaSetSpec struct {
121123
}
122124

123125
// Returns detailed information about the given replica set in the given namespace.
124-
func GetReplicaSetDetail(client *client.Client, heapsterClient *unversionedClient.RESTClient,
126+
func GetReplicaSetDetail(client client.Interface, heapsterClient HeapsterClient,
125127
namespace, name string) (*ReplicaSetDetail, error) {
126128
log.Printf("Getting details of %s replica set in %s namespace", name, namespace)
127129

@@ -134,7 +136,7 @@ func GetReplicaSetDetail(client *client.Client, heapsterClient *unversionedClien
134136

135137
replicaSetMetricsByPod, err := getReplicaSetPodsMetrics(pods, heapsterClient, namespace, name)
136138
if err != nil {
137-
return nil, err
139+
log.Printf("Skipping Heapster metrics because of error: %s\n", err)
138140
}
139141

140142
services, err := client.Services(namespace).List(unversioned.ListOptions{
@@ -172,7 +174,11 @@ func GetReplicaSetDetail(client *client.Client, heapsterClient *unversionedClien
172174
PodIP: pod.Status.PodIP,
173175
NodeName: pod.Spec.NodeName,
174176
RestartCount: getRestartCount(pod),
175-
Metrics: replicaSetMetricsByPod.MetricsMap[pod.Name],
177+
}
178+
if replicaSetMetricsByPod != nil {
179+
metric := replicaSetMetricsByPod.MetricsMap[pod.Name]
180+
podDetail.Metrics = &metric
181+
replicaSetDetail.HasMetrics = true
176182
}
177183
replicaSetDetail.Pods = append(replicaSetDetail.Pods, podDetail)
178184
}
@@ -183,7 +189,7 @@ func GetReplicaSetDetail(client *client.Client, heapsterClient *unversionedClien
183189
// TODO(floreks): This should be transactional to make sure that RC will not be deleted without
184190
// TODO(floreks): Should related services be deleted also?
185191
// Deletes replica set with given name in given namespace and related pods
186-
func DeleteReplicaSetWithPods(client *client.Client, namespace, name string) error {
192+
func DeleteReplicaSetWithPods(client client.Interface, namespace, name string) error {
187193
log.Printf("Deleting %s replica set from %s namespace", name, namespace)
188194

189195
pods, err := getRawReplicaSetPods(client, namespace, name)

src/app/backend/replicasetpodsmetrics.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
heapster "k8s.io/heapster/api/v1/types"
2525
"k8s.io/kubernetes/pkg/api"
26-
unversioned "k8s.io/kubernetes/pkg/client/unversioned"
2726
)
2827

2928
const (
@@ -40,19 +39,16 @@ type ReplicaSetMetricsByPod struct {
4039
// Pod metrics structure.
4140
type PodMetrics struct {
4241
// Cumulative CPU usage on all cores in nanoseconds.
43-
CpuUsage uint64 `json:"cpuUsage"`
42+
CpuUsage *uint64 `json:"cpuUsage"`
4443
// Pod memory usage in bytes.
45-
MemoryUsage uint64 `json:"memoryUsage"`
44+
MemoryUsage *uint64 `json:"memoryUsage"`
4645
}
4746

4847
// Return Pods metrics for Replica Set or error when occurred.
49-
func getReplicaSetPodsMetrics(podList *api.PodList, heapsterClient *unversioned.RESTClient,
48+
func getReplicaSetPodsMetrics(podList *api.PodList, heapsterClient HeapsterClient,
5049
namespace string, replicaSet string) (*ReplicaSetMetricsByPod, error) {
5150
log.Printf("Getting Pods metrics for Replica Set %s in %s namespace", replicaSet, namespace)
5251
podNames := make([]string, 0)
53-
emptyMetrics := &ReplicaSetMetricsByPod{
54-
MetricsMap: make(map[string]PodMetrics),
55-
}
5652

5753
pods := podList.Items
5854
for _, pod := range pods {
@@ -64,14 +60,12 @@ func getReplicaSetPodsMetrics(podList *api.PodList, heapsterClient *unversioned.
6460

6561
resultCpuUsageRaw, err := getRawMetrics(heapsterClient, metricCpuUsagePath)
6662
if err != nil {
67-
log.Print(err)
68-
return emptyMetrics, nil
63+
return nil, err
6964
}
7065

7166
resultMemUsageRaw, err := getRawMetrics(heapsterClient, metricMemUsagePath)
7267
if err != nil {
73-
log.Print(err)
74-
return emptyMetrics, nil
68+
return nil, err
7569
}
7670

7771
cpuMetricResult, err := unmarshalMetrics(resultCpuUsageRaw)
@@ -94,7 +88,7 @@ func createMetricPath(namespace string, podNames []string, metricName string) st
9488
}
9589

9690
// Retrieves raw metrics from Heapster.
97-
func getRawMetrics(heapsterClient *unversioned.RESTClient, metricPath string) ([]byte, error) {
91+
func getRawMetrics(heapsterClient HeapsterClient, metricPath string) ([]byte, error) {
9892

9993
resultRaw, err := heapsterClient.Get().Suffix(metricPath).DoRaw()
10094

@@ -121,15 +115,15 @@ func createResponse(cpuMetrics []heapster.MetricResult, memMetrics []heapster.Me
121115

122116
if len(cpuMetrics) == len(podNames) && len(memMetrics) == len(podNames) {
123117
for iterator, podName := range podNames {
124-
var memValue uint64
125-
var cpuValue uint64
118+
var memValue *uint64
119+
var cpuValue *uint64
126120
memMetricsList := memMetrics[iterator].Metrics
127121
cpuMetricsList := cpuMetrics[iterator].Metrics
128122
if len(memMetricsList) > 0 {
129-
memValue = memMetricsList[0].Value
123+
memValue = &memMetricsList[0].Value
130124
}
131125
if len(cpuMetricsList) > 0 {
132-
cpuValue = cpuMetricsList[0].Value
126+
cpuValue = &cpuMetricsList[0].Value
133127
}
134128
podResources := PodMetrics{
135129
CpuUsage: cpuValue,

src/app/externs/backendapi.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ backendApi.ReplicaSet;
133133
* containerImages: !Array<string>,
134134
* podInfo: !backendApi.ReplicaSetPodInfo,
135135
* pods: !Array<!backendApi.ReplicaSetPod>,
136-
* services: !Array<!backendApi.ServiceDetail>
136+
* services: !Array<!backendApi.ServiceDetail>,
137+
* hasMetrics: boolean
137138
* }}
138139
*/
139140
backendApi.ReplicaSetDetail;
@@ -152,7 +153,8 @@ backendApi.ReplicaSetSpec;
152153
* status: string,
153154
* podIP: string,
154155
* nodeName: string,
155-
* restartCount: number
156+
* restartCount: number,
157+
* metrics: {cpuUsage: ?number, memoryUsage: ?number}
156158
* }}
157159
*/
158160
backendApi.ReplicaSetPod;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
/** Base for prefixes */
16+
const base = 1000;
17+
18+
/** Names of the suffixes where I-th name is for base^I suffix. */
19+
const powerSuffixes = ['m', '', 'k', 'M', 'G'];
20+
21+
/**
22+
* Returns filter function that formats cores usage.
23+
* @param {function(number): string} numberFilter
24+
* @return {Function}
25+
* @ngInject
26+
*/
27+
export default function coresFilter(numberFilter) {
28+
/**
29+
* Formats cores usage in millicores to a decimal prefix format, e.g., 321,20 kCPU.
30+
* @param {number} value Value to be formatted in millicores.
31+
* @return {string}
32+
*/
33+
let formatCores = function(value) {
34+
let divider = 1;
35+
let power = 0;
36+
37+
while (value / divider > base && power < powerSuffixes.length - 1) {
38+
divider *= base;
39+
power += 1;
40+
}
41+
let formatted = numberFilter(value / divider);
42+
return `${formatted} ${powerSuffixes[power]}CPU`;
43+
};
44+
45+
return formatCores;
46+
}

src/app/frontend/common/filters/filters_module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import coresFilter from './cores_filter';
1516
import memoryFilter from './memory_filter';
1617
import middleEllipsisFilter from './middleellipsis_filter';
1718
import relativeTimeFilter from './relativetime_filter';
@@ -27,4 +28,5 @@ export default angular
2728
])
2829
.filter('middleEllipsis', middleEllipsisFilter)
2930
.filter('kdMemory', memoryFilter)
31+
.filter('kdCores', coresFilter)
3032
.filter('relativeTime', relativeTimeFilter);

0 commit comments

Comments
 (0)