@@ -25,6 +25,9 @@ import (
25
25
"runtime"
26
26
"testing"
27
27
28
+ "github.com/prometheus/common/model"
29
+
30
+ v1 "k8s.io/api/core/v1"
28
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
32
"k8s.io/apimachinery/pkg/runtime/schema"
30
33
clientset "k8s.io/client-go/kubernetes"
@@ -111,3 +114,130 @@ func TestApiserverMetrics(t *testing.T) {
111
114
"etcd_request_duration_seconds_sum" ,
112
115
})
113
116
}
117
+
118
+ func TestApiserverMetricsLabels (t * testing.T ) {
119
+ _ , s , closeFn := framework .RunAMaster (nil )
120
+ defer closeFn ()
121
+
122
+ client , err := clientset .NewForConfig (& restclient.Config {Host : s .URL , QPS : - 1 })
123
+ if err != nil {
124
+ t .Fatalf ("Error in create clientset: %v" , err )
125
+ }
126
+
127
+ expectedMetrics := []model.Metric {}
128
+
129
+ metricLabels := func (group , version , resource , subresource , scope , verb string ) model.Metric {
130
+ return map [model.LabelName ]model.LabelValue {
131
+ model .LabelName ("group" ): model .LabelValue (group ),
132
+ model .LabelName ("version" ): model .LabelValue (version ),
133
+ model .LabelName ("resource" ): model .LabelValue (resource ),
134
+ model .LabelName ("subresource" ): model .LabelValue (subresource ),
135
+ model .LabelName ("scope" ): model .LabelValue (scope ),
136
+ model .LabelName ("verb" ): model .LabelValue (verb ),
137
+ }
138
+ }
139
+
140
+ callOrDie := func (_ interface {}, err error ) {
141
+ if err != nil {
142
+ t .Fatalf ("unexpected error: %v" , err )
143
+ }
144
+ }
145
+
146
+ appendExpectedMetric := func (metric model.Metric ) {
147
+ expectedMetrics = append (expectedMetrics , metric )
148
+ }
149
+
150
+ // Call appropriate endpoints to ensure particular metrics will be exposed
151
+
152
+ // Namespace-scoped resource
153
+ c := client .CoreV1 ().Pods (metav1 .NamespaceDefault )
154
+ makePod := func (labelValue string ) * v1.Pod {
155
+ return & v1.Pod {
156
+ ObjectMeta : metav1.ObjectMeta {
157
+ Name : "foo" ,
158
+ Labels : map [string ]string {"foo" : labelValue },
159
+ },
160
+ Spec : v1.PodSpec {
161
+ Containers : []v1.Container {
162
+ {
163
+ Name : "container" ,
164
+ Image : "image" ,
165
+ },
166
+ },
167
+ },
168
+ }
169
+ }
170
+
171
+ callOrDie (c .Create (context .TODO (), makePod ("foo" ), metav1.CreateOptions {}))
172
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "resource" , "POST" ))
173
+ callOrDie (c .Update (context .TODO (), makePod ("bar" ), metav1.UpdateOptions {}))
174
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "resource" , "PUT" ))
175
+ callOrDie (c .UpdateStatus (context .TODO (), makePod ("bar" ), metav1.UpdateOptions {}))
176
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "status" , "resource" , "PUT" ))
177
+ callOrDie (c .Get (context .TODO (), "foo" , metav1.GetOptions {}))
178
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "resource" , "GET" ))
179
+ callOrDie (c .List (context .TODO (), metav1.ListOptions {}))
180
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "namespace" , "LIST" ))
181
+ callOrDie (nil , c .Delete (context .TODO (), "foo" , metav1.DeleteOptions {}))
182
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "resource" , "DELETE" ))
183
+ // cluster-scoped LIST of namespace-scoped resources
184
+ callOrDie (client .CoreV1 ().Pods (metav1 .NamespaceAll ).List (context .TODO (), metav1.ListOptions {}))
185
+ appendExpectedMetric (metricLabels ("" , "v1" , "pods" , "" , "cluster" , "LIST" ))
186
+
187
+ // Cluster-scoped resource
188
+ cn := client .CoreV1 ().Namespaces ()
189
+ makeNamespace := func (labelValue string ) * v1.Namespace {
190
+ return & v1.Namespace {
191
+ ObjectMeta : metav1.ObjectMeta {
192
+ Name : "foo" ,
193
+ Labels : map [string ]string {"foo" : labelValue },
194
+ },
195
+ }
196
+ }
197
+
198
+ callOrDie (cn .Create (context .TODO (), makeNamespace ("foo" ), metav1.CreateOptions {}))
199
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "" , "resource" , "POST" ))
200
+ callOrDie (cn .Update (context .TODO (), makeNamespace ("bar" ), metav1.UpdateOptions {}))
201
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "" , "resource" , "PUT" ))
202
+ callOrDie (cn .UpdateStatus (context .TODO (), makeNamespace ("bar" ), metav1.UpdateOptions {}))
203
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "status" , "resource" , "PUT" ))
204
+ callOrDie (cn .Get (context .TODO (), "foo" , metav1.GetOptions {}))
205
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "" , "resource" , "GET" ))
206
+ callOrDie (cn .List (context .TODO (), metav1.ListOptions {}))
207
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "" , "cluster" , "LIST" ))
208
+ callOrDie (nil , cn .Delete (context .TODO (), "foo" , metav1.DeleteOptions {}))
209
+ appendExpectedMetric (metricLabels ("" , "v1" , "namespaces" , "" , "resource" , "DELETE" ))
210
+
211
+ // Verify if all metrics were properly exported.
212
+ metrics , err := scrapeMetrics (s )
213
+ if err != nil {
214
+ t .Fatal (err )
215
+ }
216
+
217
+ samples , ok := metrics ["apiserver_request_total" ]
218
+ if ! ok {
219
+ t .Fatalf ("apiserver_request_total metric not exposed" )
220
+ }
221
+
222
+ hasLabels := func (current , expected model.Metric ) bool {
223
+ for key , value := range expected {
224
+ if current [key ] != value {
225
+ return false
226
+ }
227
+ }
228
+ return true
229
+ }
230
+
231
+ for _ , expectedMetric := range expectedMetrics {
232
+ found := false
233
+ for _ , sample := range samples {
234
+ if hasLabels (sample .Metric , expectedMetric ) {
235
+ found = true
236
+ break
237
+ }
238
+ }
239
+ if ! found {
240
+ t .Errorf ("No sample found for %#v" , expectedMetric )
241
+ }
242
+ }
243
+ }
0 commit comments