@@ -28,7 +28,6 @@ import (
28
28
"time"
29
29
30
30
restful "github.com/emicklei/go-restful"
31
-
32
31
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
33
32
"k8s.io/apimachinery/pkg/types"
34
33
utilsets "k8s.io/apimachinery/pkg/util/sets"
@@ -48,6 +47,8 @@ type resettableCollector interface {
48
47
49
48
const (
50
49
APIServerComponent string = "apiserver"
50
+ OtherContentType string = "other"
51
+ OtherRequestMethod string = "other"
51
52
)
52
53
53
54
/*
@@ -172,6 +173,37 @@ var (
172
173
currentInflightRequests ,
173
174
requestTerminationsTotal ,
174
175
}
176
+
177
+ // these are the known (e.g. whitelisted/known) content types which we will report for
178
+ // request metrics. Any other RFC compliant content types will be aggregated under 'unknown'
179
+ knownMetricContentTypes = utilsets .NewString (
180
+ "application/apply-patch+yaml" ,
181
+ "application/json" ,
182
+ "application/json-patch+json" ,
183
+ "application/merge-patch+json" ,
184
+ "application/strategic-merge-patch+json" ,
185
+ "application/vnd.kubernetes.protobuf" ,
186
+ "application/vnd.kubernetes.protobuf;stream=watch" ,
187
+ "application/yaml" ,
188
+ "text/plain" ,
189
+ "text/plain;charset=utf-8" )
190
+ // these are the valid request methods which we report in our metrics. Any other request methods
191
+ // will be aggregated under 'unknown'
192
+ validRequestMethods = utilsets .NewString (
193
+ "APPLY" ,
194
+ "CONNECT" ,
195
+ "CREATE" ,
196
+ "DELETE" ,
197
+ "DELETECOLLECTION" ,
198
+ "GET" ,
199
+ "LIST" ,
200
+ "PATCH" ,
201
+ "POST" ,
202
+ "PROXY" ,
203
+ "PUT" ,
204
+ "UPDATE" ,
205
+ "WATCH" ,
206
+ "WATCHLIST" )
175
207
)
176
208
177
209
const (
@@ -219,6 +251,10 @@ func RecordRequestTermination(req *http.Request, requestInfo *request.RequestInf
219
251
// translated to RequestInfo).
220
252
// However, we need to tweak it e.g. to differentiate GET from LIST.
221
253
verb := canonicalVerb (strings .ToUpper (req .Method ), scope )
254
+ // set verbs to a bounded set of known and expected verbs
255
+ if ! validRequestMethods .Has (verb ) {
256
+ verb = OtherRequestMethod
257
+ }
222
258
if requestInfo .IsResourceRequest {
223
259
requestTerminationsTotal .WithLabelValues (cleanVerb (verb , req ), requestInfo .APIGroup , requestInfo .APIVersion , requestInfo .Resource , requestInfo .Subresource , scope , component , codeToString (code )).Inc ()
224
260
} else {
@@ -256,7 +292,8 @@ func MonitorRequest(req *http.Request, verb, group, version, resource, subresour
256
292
reportedVerb := cleanVerb (verb , req )
257
293
dryRun := cleanDryRun (req .URL )
258
294
elapsedSeconds := elapsed .Seconds ()
259
- requestCounter .WithLabelValues (reportedVerb , dryRun , group , version , resource , subresource , scope , component , contentType , codeToString (httpCode )).Inc ()
295
+ cleanContentType := cleanContentType (contentType )
296
+ requestCounter .WithLabelValues (reportedVerb , dryRun , group , version , resource , subresource , scope , component , cleanContentType , codeToString (httpCode )).Inc ()
260
297
requestLatencies .WithLabelValues (reportedVerb , dryRun , group , version , resource , subresource , scope , component ).Observe (elapsedSeconds )
261
298
// We are only interested in response sizes of read requests.
262
299
if verb == "GET" || verb == "LIST" {
@@ -311,6 +348,19 @@ func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope, c
311
348
}
312
349
}
313
350
351
+ // cleanContentType binds the contentType (for metrics related purposes) to a
352
+ // bounded set of known/expected content-types.
353
+ func cleanContentType (contentType string ) string {
354
+ normalizedContentType := strings .ToLower (contentType )
355
+ if strings .HasSuffix (contentType , " stream=watch" ) || strings .HasSuffix (contentType , " charset=utf-8" ) {
356
+ normalizedContentType = strings .ReplaceAll (contentType , " " , "" )
357
+ }
358
+ if knownMetricContentTypes .Has (normalizedContentType ) {
359
+ return normalizedContentType
360
+ }
361
+ return OtherContentType
362
+ }
363
+
314
364
// CleanScope returns the scope of the request.
315
365
func CleanScope (requestInfo * request.RequestInfo ) string {
316
366
if requestInfo .Namespace != "" {
@@ -355,7 +405,10 @@ func cleanVerb(verb string, request *http.Request) string {
355
405
if verb == "PATCH" && request .Header .Get ("Content-Type" ) == string (types .ApplyPatchType ) && utilfeature .DefaultFeatureGate .Enabled (features .ServerSideApply ) {
356
406
reportedVerb = "APPLY"
357
407
}
358
- return reportedVerb
408
+ if validRequestMethods .Has (reportedVerb ) {
409
+ return reportedVerb
410
+ }
411
+ return OtherRequestMethod
359
412
}
360
413
361
414
func cleanDryRun (u * url.URL ) string {
0 commit comments