forked from kyma-project/kyma
/
metrics.go
149 lines (132 loc) · 4.11 KB
/
metrics.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
package monitoring
import (
"context"
"encoding/json"
"fmt"
"time"
prometheus "github.com/prometheus/client_golang/api"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
const (
prometheusDomain = "http://monitoring-prometheus.kyma-system"
prometheusPort = "9090"
metricsQuery = "max(sum(kube_pod_container_resource_requests_cpu_cores) by (instance))"
metricsConfigMapName = "metrics-upgrade-test"
metricsDataField = "response"
metricsTimeField = "collected_at"
)
// MetricsUpgradeTest compares metrics from before and after upgrade
type MetricsUpgradeTest struct {
k8sCli kubernetes.Interface
namespace string
prometheusAPI v1.API
log logrus.FieldLogger
}
// NewMetricsUpgradeTest returns a new instance of the MetricsUpgradeTest
func NewMetricsUpgradeTest(k8sCli kubernetes.Interface) (*MetricsUpgradeTest, error) {
client, err := prometheus.NewClient(prometheus.Config{Address: fmt.Sprintf("%v:%v", prometheusDomain, prometheusPort)})
if err != nil {
return nil, err
}
promAPI := v1.NewAPI(client)
return &MetricsUpgradeTest{
prometheusAPI: promAPI,
k8sCli: k8sCli,
}, nil
}
// CreateResources retrieves metrics and stores the value in an configmap
func (ut *MetricsUpgradeTest) CreateResources(stop <-chan struct{}, log logrus.FieldLogger, namespace string) error {
ut.namespace = namespace
ut.log = log
time := time.Now()
result, err := ut.collectMetrics(time)
if err != nil {
return err
}
err = ut.storeMetrics(result, time)
return err
}
// TestResources retrieves previously installed metrics values and compares it to the metrics returned for the same query and the same time
func (ut *MetricsUpgradeTest) TestResources(stop <-chan struct{}, log logrus.FieldLogger, namespace string) error {
ut.namespace = namespace
ut.log = log
return ut.compareMetrics()
}
func (ut *MetricsUpgradeTest) collectMetrics(time time.Time) (model.Vector, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
result, _, err := ut.prometheusAPI.Query(ctx, metricsQuery, time)
if err != nil {
return nil, err
}
if result.Type() == model.ValVector {
ut.log.Debugln(result.(model.Vector))
return result.(model.Vector), nil
}
return nil, fmt.Errorf("%v should be of type vector but is of type %t", result, result)
}
func (ut *MetricsUpgradeTest) storeMetrics(value model.Vector, time time.Time) error {
promValue, err := json.Marshal(value)
if err != nil {
return err
}
timeValue, err := json.Marshal(time)
if err != nil {
return err
}
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: metricsConfigMapName,
},
Data: map[string]string{
metricsDataField: string(promValue),
metricsTimeField: string(timeValue),
},
}
_, err = ut.k8sCli.CoreV1().ConfigMaps(ut.namespace).Create(cm)
return err
}
func (ut *MetricsUpgradeTest) retrievePreviousMetrics() (time.Time, model.Vector, error) {
cm, err := ut.k8sCli.CoreV1().ConfigMaps(ut.namespace).Get(metricsConfigMapName, metav1.GetOptions{})
if err != nil {
return time.Time{}, nil, err
}
value := model.Vector{}
err = json.Unmarshal([]byte(cm.Data[metricsDataField]), &value)
if err != nil {
return time.Time{}, nil, err
}
timeValue := time.Time{}
err = json.Unmarshal([]byte(cm.Data[metricsTimeField]), &timeValue)
if err != nil {
return time.Time{}, nil, err
}
return timeValue, value, nil
}
func (ut *MetricsUpgradeTest) compareMetrics() error {
time, previous, err := ut.retrievePreviousMetrics()
if err != nil {
return err
}
if len(previous) < 1 {
return fmt.Errorf("previous metric is empty")
}
current, err := ut.collectMetrics(time)
if err != nil {
return err
}
if len(current) < 1 {
return fmt.Errorf("current metric is empty")
}
ut.log.Debugln(previous)
ut.log.Debugln(current)
if float32(previous[0].Value) != float32(current[0].Value) {
return fmt.Errorf("retrieved data not equal: before: %+v, after: %+v", previous, current)
}
return nil
}