From 07020ab42e8f1a2e9d6fe4969c01d0f971324ae1 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 24 Aug 2022 08:54:51 -0700 Subject: [PATCH] add metric and test Change-Id: Ic2bcf39caef791b2e13448a97d2c3203ed1d94b9 --- .../pkg/endpoints/handlers/metrics/OWNERS | 4 + .../pkg/endpoints/handlers/metrics/metrics.go | 36 ++++++++ .../pkg/endpoints/handlers/rest_test.go | 92 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS create mode 100644 staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS new file mode 100644 index 00000000000..433e84aa3e4 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS @@ -0,0 +1,4 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: + - logicalhan diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go new file mode 100644 index 00000000000..694ff53b25e --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go @@ -0,0 +1,36 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metrics + +import ( + "k8s.io/component-base/metrics" +) + +var ( + RequestBodySizes = metrics.NewHistogramVec( + &metrics.HistogramOpts{ + Subsystem: "apiserver", + Name: "request_body_sizes", + Help: "Apiserver request body sizes broken out by size.", + // we use 0.05 KB as the smallest bucket with 0.1 KB increments up to the + // apiserver limit. + Buckets: metrics.LinearBuckets(50000, 100000, 31), + StabilityLevel: metrics.ALPHA, + }, + []string{"resource", "verb"}, + ) +) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go index 52d169a86e4..5a7e39979ba 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "io" "net/http" "reflect" "strings" @@ -45,9 +46,12 @@ import ( "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/apis/example" examplev1 "k8s.io/apiserver/pkg/apis/example/v1" + "k8s.io/apiserver/pkg/endpoints/handlers/metrics" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/component-base/metrics/legacyregistry" + "k8s.io/component-base/metrics/testutil" utiltrace "k8s.io/utils/trace" ) @@ -107,6 +111,94 @@ func TestPatchAnonymousField(t *testing.T) { } } +func TestLimitedReadBody(t *testing.T) { + defer legacyregistry.Reset() + legacyregistry.Register(metrics.RequestBodySizes) + + testcases := []struct { + desc string + requestBody io.Reader + limit int64 + expectedMetrics string + expectedErr bool + }{ + { + desc: "aaaa with limit 1", + requestBody: strings.NewReader("aaaa"), + limit: 1, + expectedMetrics: "", + expectedErr: true, + }, + { + desc: "aaaa with limit 5", + requestBody: strings.NewReader("aaaa"), + limit: 5, + expectedMetrics: ` + # HELP apiserver_request_body_sizes [ALPHA] Apiserver request body sizes broken out by size. + # TYPE apiserver_request_body_sizes histogram + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="50000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="150000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="250000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="350000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="450000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="550000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="650000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="750000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="850000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="950000"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.05e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.15e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.25e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.35e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.45e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.55e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.65e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.75e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.85e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="1.95e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.05e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.15e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.25e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.35e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.45e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.55e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.65e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.75e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.85e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="2.95e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="3.05e+06"} 1 + apiserver_request_body_sizes_bucket{resource="resource.group",verb="create",le="+Inf"} 1 + apiserver_request_body_sizes_sum{resource="resource.group",verb="create"} 3 + apiserver_request_body_sizes_count{resource="resource.group",verb="create"} 1 +`, + expectedErr: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + // reset metrics + defer metrics.RequestBodySizes.Reset() + defer legacyregistry.Reset() + + req, err := http.NewRequest("POST", "/", tc.requestBody) + if err != nil { + t.Errorf("err not expected: got %v", err) + } + _, err = limitedReadBody(context.Background(), req, tc.limit, "resource.group", "create") + if tc.expectedErr { + if err == nil { + t.Errorf("err expected: got nil") + } + return + } + if err = testutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(tc.expectedMetrics), "apiserver_request_body_sizes"); err != nil { + t.Errorf("unexpected err: %v", err) + } + }) + } +} + func TestStrategicMergePatchInvalid(t *testing.T) { testGV := schema.GroupVersion{Group: "", Version: "v"} scheme.AddKnownTypes(testGV, &testPatchType{})