forked from cloudability/metrics-agent
/
heapster.go
161 lines (146 loc) · 5.23 KB
/
heapster.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package kubernetes
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/dfroberg/metrics-agent/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
type heapsterMetricExport []struct {
Metrics struct {
CPUUsage []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"cpu/usage,omitempty"`
MemoryCache []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/cache,omitempty"`
MemoryMajorPageFaults []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/major_page_faults,omitempty"`
MemoryPageFaults []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/page_faults,omitempty"`
MemoryRss []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/rss,omitempty"`
MemoryUsage []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/usage,omitempty"`
MemoryWorkingSet []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"memory/working_set,omitempty"`
Uptime []struct {
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
Value int `json:"value,omitempty"`
} `json:"uptime,omitempty"`
} `json:"metrics,omitempty"`
Labels struct {
ContainerName string `json:"container_name,omitempty"`
HostID string `json:"host_id,omitempty"`
Hostname string `json:"hostname,omitempty"`
Nodename string `json:"nodename,omitempty"`
} `json:"labels,omitempty"`
}
// returns the proxy url of heapster in the cluster (returns last found based on match)
func getHeapsterURL(ctx context.Context, clientset kubernetes.Interface, clusterHostURL string) (
URL url.URL, err error) {
pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{})
if err != nil {
log.Fatalf("cloudability metric agent is unable to get a list of pods: %v", err)
}
services, err := clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{})
if err != nil {
log.Fatalf("cloudability metric agent is unable to get a list of services: %v", err)
}
for _, pod := range pods.Items {
if strings.Contains(pod.Name, "heapster") {
URL.Host = clusterHostURL
//nolint staticcheck
URL.Path = pod.SelfLink + ":8082/proxy/api/v1/metric-export"
}
}
// prefer accessing via service if present
// nolint dupl
for _, service := range services.Items {
if service.Name == "heapster" && service.Namespace == "cloudability" {
URL.Host = "http://heapster.cloudability:8082"
URL.Path = "/api/v1/metric-export"
return URL, nil
} else if service.Name == "heapster" {
URL.Host = clusterHostURL
if len(service.Spec.Ports) > 0 {
URL.Path = service.SelfLink + ":" + strconv.Itoa(
int(service.Spec.Ports[0].Port)) + "/proxy/api/v1/metric-export"
} else {
URL.Path = service.SelfLink + "/proxy/api/v1/metric-export"
}
}
}
return URL, err
}
func validateHeapster(config KubeAgentConfig, client rest.HTTPClient) error {
outerTest, body, err := util.TestHTTPConnection(
client, config.HeapsterURL, http.MethodGet, config.BearerToken, retryCount, true)
if err != nil {
return err
}
if !outerTest {
return fmt.Errorf("no heapster found")
}
var me heapsterMetricExport
if err := json.Unmarshal(*body, &me); err != nil {
return fmt.Errorf("malformed response from heapster running at: %v", config.HeapsterURL)
}
if len(me) < 10 {
return fmt.Errorf("received empty or malformed response from heapster running at: %v",
config.HeapsterURL)
}
log.Debugf("Connected to heapster at: %v", config.HeapsterURL)
return err
}
func handleBaselineHeapsterMetrics(msExportDirectory, msd, baselineMetricSample, heapsterMetricExport string) error {
// copy into the current sample directory the most recent baseline metric export
err := util.CopyFileContents(msd+"/"+filepath.Base(baselineMetricSample), baselineMetricSample)
if err != nil {
log.Warn("Warning previous baseline not found or incomplete")
}
// remove the baseline metric if it is not json
if baselineMetricSample != "" && filepath.Base(baselineMetricSample) != "baseline-metrics-export.json" {
if err = os.Remove(baselineMetricSample); err != nil {
return fmt.Errorf("error cleaning up invalid baseline metric export: %s", err)
}
}
// update the baseline metric export with the most recent sample from this collection
err = util.CopyFileContents(
filepath.Dir(msExportDirectory)+"/"+"baseline-metrics-export"+filepath.Ext(
heapsterMetricExport), heapsterMetricExport)
if err != nil {
return fmt.Errorf("error updating baseline metric export: %s", err)
}
return nil
}