Merge pull request #88936 from jpbetz/http-trace

Add nested tracing and endpoint filter tracing to apiserver
This commit is contained in:
Kubernetes Prow Robot 2020-07-13 11:27:21 -07:00 committed by GitHub
commit 240a72b5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 196 additions and 43 deletions

View File

@ -23,6 +23,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",

View File

@ -30,6 +30,7 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
utilfeature "k8s.io/apiserver/pkg/util/feature"
corelisters "k8s.io/client-go/listers/core/v1"
extenderv1 "k8s.io/kube-scheduler/extender/v1"
@ -136,7 +137,7 @@ func (g *genericScheduler) snapshot() error {
// If it succeeds, it will return the name of the node.
// If it fails, it will return a FitError error with reasons.
func (g *genericScheduler) Schedule(ctx context.Context, prof *profile.Profile, state *framework.CycleState, pod *v1.Pod) (result ScheduleResult, err error) {
trace := utiltrace.New("Scheduling", utiltrace.Field{Key: "namespace", Value: pod.Namespace}, utiltrace.Field{Key: "name", Value: pod.Name})
ctx, trace := genericapirequest.WithTrace(ctx, "Scheduling", utiltrace.Field{Key: "namespace", Value: pod.Namespace}, utiltrace.Field{Key: "name", Value: pod.Name})
defer trace.LogIfLong(100 * time.Millisecond)
if err := podPassesBasicChecks(pod, g.pvcLister); err != nil {

View File

@ -24,6 +24,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",

View File

@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/rest"
utiltrace "k8s.io/utils/trace"
@ -259,7 +260,7 @@ func (c *webhookConverter) Convert(in runtime.Object, toGV schema.GroupVersion)
return out, nil
}
trace := utiltrace.New("Call conversion webhook",
ctx, trace := genericapirequest.WithTrace(context.TODO(), "Call conversion webhook",
utiltrace.Field{"custom-resource-definition", c.name},
utiltrace.Field{"desired-api-version", desiredAPIVersion},
utiltrace.Field{"object-count", objCount},
@ -269,8 +270,6 @@ func (c *webhookConverter) Convert(in runtime.Object, toGV schema.GroupVersion)
// the conversion request on the apiserver side (~4ms per object).
defer trace.LogIfLong(time.Duration(50+8*objCount) * time.Millisecond)
// TODO: Figure out if adding one second timeout make sense here.
ctx := context.TODO()
r := c.restClient.Post().Body(request).Do(ctx)
if err := r.Into(response); err != nil {
// TODO: Return a webhook specific error to be able to convert it to meta.Status

View File

@ -29,6 +29,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/warning:go_default_library",
"//vendor/github.com/evanphx/json-patch:go_default_library",

View File

@ -42,6 +42,7 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
webhookrequest "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/pkg/warning"
utiltrace "k8s.io/utils/trace"
@ -221,7 +222,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
if err != nil {
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
}
trace := utiltrace.New("Call mutating webhook",
ctx, trace := genericapirequest.WithTrace(ctx, "Call mutating webhook",
utiltrace.Field{"configuration", configurationName},
utiltrace.Field{"webhook", h.Name},
utiltrace.Field{"resource", attr.GetResource()},

View File

@ -22,6 +22,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/warning:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library",

View File

@ -32,6 +32,7 @@ import (
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
webhookrequest "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/pkg/warning"
"k8s.io/klog/v2"
@ -181,7 +182,7 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1.ValidatingWeb
if err != nil {
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
}
trace := utiltrace.New("Call validating webhook",
ctx, trace := genericapirequest.WithTrace(ctx, "Call validating webhook",
utiltrace.Field{"configuration", invocation.Webhook.GetConfigurationName()},
utiltrace.Field{"webhook", h.Name},
utiltrace.Field{"resource", attr.GetResource()},

View File

@ -112,6 +112,7 @@ filegroup(
"//staging/src/k8s.io/apiserver/pkg/endpoints/discovery:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/filters:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/internal:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/metrics:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:all-srcs",

View File

@ -55,6 +55,7 @@ go_library(
"impersonation.go",
"metrics.go",
"requestinfo.go",
"trace.go",
"warning.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/filters",
@ -76,12 +77,14 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/internal:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/httplog:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/warning:go_default_library",
"//staging/src/k8s.io/component-base/metrics:go_default_library",
"//staging/src/k8s.io/component-base/metrics/legacyregistry:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library",
"//vendor/k8s.io/utils/trace:go_default_library",
],
)

View File

@ -47,6 +47,7 @@ func WithAuthentication(handler http.Handler, auth authenticator.Request, failed
req = req.WithContext(authenticator.WithAudiences(req.Context(), apiAuds))
}
resp, ok, err := auth.AuthenticateRequest(req)
traceFilterStep(req.Context(), "Authenticate check done")
defer recordAuthMetrics(resp, ok, err, apiAuds, authenticationStart)
if err != nil || !ok {
if err != nil {

View File

@ -57,6 +57,7 @@ func WithAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.
return
}
authorized, reason, err := a.Authorize(ctx, attributes)
traceFilterStep(ctx, "Authorize check done")
// an authorizer like RBAC could encounter evaluation errors and still allow the request, so authorizer decision is checked before error here.
if authorized == authorizer.DecisionAllow {
audit.LogAnnotation(ae, decisionAnnotationKey, decisionAllow)

View File

@ -110,6 +110,7 @@ func WithImpersonation(handler http.Handler, a authorizer.Authorizer, s runtime.
}
decision, reason, err := a.Authorize(ctx, actingAsAttributes)
traceFilterStep(ctx, "Impersonation authorize check done")
if err != nil || decision != authorizer.DecisionAllow {
klog.V(4).Infof("Forbidden: %#v, Reason: %s, Error: %v", req.RequestURI, reason, err)
responsewriters.Forbidden(ctx, actingAsAttributes, w, req, reason, s)
@ -144,7 +145,6 @@ func WithImpersonation(handler http.Handler, a authorizer.Authorizer, s runtime.
req.Header.Del(headerName)
}
}
handler.ServeHTTP(w, req)
})
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2020 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 filters
import (
"context"
"net/http"
"time"
utiltrace "k8s.io/utils/trace"
"k8s.io/apiserver/pkg/endpoints/internal"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
)
// WithTrace decorates a http.Handler with tracing for all the non-long running
// requests coming to the server.
func WithTrace(handler http.Handler, longRunningCheck genericapirequest.LongRunningRequestCheck) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ri, ok := genericapirequest.RequestInfoFrom(req.Context())
isLongRunning := false
if longRunningCheck != nil {
if ok && longRunningCheck(req, ri) {
isLongRunning = true
}
}
if isLongRunning {
handler.ServeHTTP(w, req)
return
}
ctx, trace := genericapirequest.WithTrace(req.Context(), "HTTP Request",
utiltrace.Field{Key: "method", Value: req.Method},
utiltrace.Field{Key: "url", Value: req.URL.Path},
utiltrace.Field{Key: "verb", Value: ri.Verb},
utiltrace.Field{Key: "name", Value: ri.Name},
utiltrace.Field{Key: "resource", Value: ri.Resource},
utiltrace.Field{Key: "subresource", Value: ri.Subresource},
utiltrace.Field{Key: "namespace", Value: ri.Namespace},
utiltrace.Field{Key: "api-group", Value: ri.APIGroup},
utiltrace.Field{Key: "api-version", Value: ri.APIVersion},
utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}},
utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
req = req.Clone(ctx)
// Set trace as root trace in context for nested tracing
defer trace.LogIfLong(30 * time.Second)
handler.ServeHTTP(w, req)
})
}
func traceFilterStep(ctx context.Context, msg string, field ...utiltrace.Field) {
if trace, ok := genericapirequest.TraceFrom(ctx); ok {
trace.Step(msg, field...)
}
}

View File

@ -10,7 +10,6 @@ go_test(
name = "go_default_test",
srcs = [
"create_test.go",
"helpers_test.go",
"namer_test.go",
"response_test.go",
"rest_test.go",
@ -42,7 +41,6 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//vendor/github.com/evanphx/json-patch:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/utils/trace:go_default_library",
],
)
@ -54,7 +52,6 @@ go_library(
"delete.go",
"doc.go",
"get.go",
"helpers.go",
"namer.go",
"patch.go",
"response.go",
@ -82,7 +79,6 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
@ -94,6 +90,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/internal:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",

View File

@ -33,9 +33,11 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/internal"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest"
@ -47,7 +49,7 @@ import (
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes.
trace := utiltrace.New("Create", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "Create", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
defer trace.LogIfLong(500 * time.Millisecond)
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {

View File

@ -30,9 +30,11 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/internal"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest"
@ -46,7 +48,7 @@ import (
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes.
trace := utiltrace.New("Delete", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "Delete", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
defer trace.LogIfLong(500 * time.Millisecond)
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
@ -164,7 +166,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
// DeleteCollection returns a function that will handle a collection deletion
func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
trace := utiltrace.New("Delete", utiltrace.Field{"url", req.URL.Path})
req, trace := requestWithTrace(req, "Delete", utiltrace.Field{"url", req.URL.Path})
defer trace.LogIfLong(500 * time.Millisecond)
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {

View File

@ -36,7 +36,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/internal"
"k8s.io/apiserver/pkg/endpoints/metrics"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
@ -51,7 +53,7 @@ type getterFunc func(ctx context.Context, name string, req *http.Request, trace
// passed-in getterFunc to perform the actual get.
func getResourceHandler(scope *RequestScope, getter getterFunc) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
trace := utiltrace.New("Get", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "Get", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
defer trace.LogIfLong(500 * time.Millisecond)
namespace, name, err := scope.Namer.Name(req)
@ -168,7 +170,7 @@ func getRequestOptions(req *http.Request, scope *RequestScope, into runtime.Obje
func ListResource(r rest.Lister, rw rest.Watcher, scope *RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes.
trace := utiltrace.New("List", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "List", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
namespace, err := scope.Namer.Namespace(req)
if err != nil {

View File

@ -38,18 +38,20 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/yaml"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/internal"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/util/dryrun"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utiltrace "k8s.io/utils/trace"
"sigs.k8s.io/yaml"
)
const (
@ -61,7 +63,7 @@ const (
func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes.
trace := utiltrace.New("Patch", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "Patch", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
defer trace.LogIfLong(500 * time.Millisecond)
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {

View File

@ -47,6 +47,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
utiltrace "k8s.io/utils/trace"
)
// RequestScope encapsulates common fields across all RESTful handler methods.
@ -445,3 +446,10 @@ func isTooLargeError(err error) bool {
}
return false
}
// requestWithTrace returns a new trace using the provided msg and fields, nested within any trace already in the
//context of the provided req. Also returns a request with the new trace in the context.
func requestWithTrace(req *http.Request, msg string, fields ...utiltrace.Field) (*http.Request, *utiltrace.Trace) {
ctx, trace := request.WithTrace(req.Context(), msg, fields...)
return req.Clone(ctx), trace
}

View File

@ -30,10 +30,12 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
"k8s.io/apiserver/pkg/endpoints/internal"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest"
@ -46,7 +48,7 @@ import (
func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// For performance tracking purposes.
trace := utiltrace.New("Update", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &lazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &lazyClientIP{req}})
req, trace := requestWithTrace(req, "Update", utiltrace.Field{Key: "url", Value: req.URL.Path}, utiltrace.Field{Key: "user-agent", Value: &internal.LazyTruncatedUserAgent{req}}, utiltrace.Field{Key: "client", Value: &internal.LazyClientIP{req}})
defer trace.LogIfLong(500 * time.Millisecond)
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {

View File

@ -0,0 +1,31 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["helpers.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/internal",
importpath = "k8s.io/apiserver/pkg/endpoints/internal",
visibility = ["//staging/src/k8s.io/apiserver/pkg/endpoints:__subpackages__"],
deps = ["//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["helpers_test.go"],
embed = [":go_default_library"],
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package handlers
package internal
import (
"net/http"
@ -27,16 +27,16 @@ const (
userAgentTruncateSuffix = "...TRUNCATED"
)
// lazyTruncatedUserAgent implements String() string and it will
// LazyTruncatedUserAgent implements String() string and it will
// return user-agent which may be truncated.
type lazyTruncatedUserAgent struct {
req *http.Request
type LazyTruncatedUserAgent struct {
Req *http.Request
}
func (lazy *lazyTruncatedUserAgent) String() string {
func (lazy *LazyTruncatedUserAgent) String() string {
ua := "unknown"
if lazy.req != nil {
ua = utilnet.GetHTTPClient(lazy.req)
if lazy.Req != nil {
ua = utilnet.GetHTTPClient(lazy.Req)
if len(ua) > maxUserAgentLength {
ua = ua[:maxUserAgentLength] + userAgentTruncateSuffix
}
@ -46,13 +46,13 @@ func (lazy *lazyTruncatedUserAgent) String() string {
// LazyClientIP implements String() string and it will
// calls GetClientIP() lazily only when required.
type lazyClientIP struct {
req *http.Request
type LazyClientIP struct {
Req *http.Request
}
func (lazy *lazyClientIP) String() string {
if lazy.req != nil {
if ip := utilnet.GetClientIP(lazy.req); ip != nil {
func (lazy *LazyClientIP) String() string {
if lazy.Req != nil {
if ip := utilnet.GetClientIP(lazy.Req); ip != nil {
return ip.String()
}
}

View File

@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package handlers
package internal
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLazyTruncatedUserAgent(t *testing.T) {
@ -29,7 +30,7 @@ func TestLazyTruncatedUserAgent(t *testing.T) {
ua := "short-agent"
req.Header.Set("User-Agent", ua)
uaNotTruncated := &lazyTruncatedUserAgent{req}
uaNotTruncated := &LazyTruncatedUserAgent{req}
assert.Equal(t, ua, fmt.Sprintf("%v", uaNotTruncated))
ua = ""
@ -37,10 +38,10 @@ func TestLazyTruncatedUserAgent(t *testing.T) {
ua = ua + "a"
}
req.Header.Set("User-Agent", ua)
uaTruncated := &lazyTruncatedUserAgent{req}
uaTruncated := &LazyTruncatedUserAgent{req}
assert.NotEqual(t, ua, fmt.Sprintf("%v", uaTruncated))
usUnknown := &lazyTruncatedUserAgent{}
usUnknown := &LazyTruncatedUserAgent{}
assert.Equal(t, "unknown", fmt.Sprintf("%v", usUnknown))
}
@ -51,9 +52,9 @@ func TestLazyClientIP(t *testing.T) {
ip := "127.0.0.1"
req.Header.Set("X-Forwarded-For", ip)
clientIPWithReq := &lazyClientIP{req}
clientIPWithReq := &LazyClientIP{req}
assert.Equal(t, ip, fmt.Sprintf("%v", clientIPWithReq))
clientIPWithoutReq := &lazyClientIP{}
clientIPWithoutReq := &LazyClientIP{}
assert.Equal(t, "unknown", fmt.Sprintf("%v", clientIPWithoutReq))
}

View File

@ -38,6 +38,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library",
"//vendor/k8s.io/utils/trace:go_default_library",
],
)

View File

@ -22,6 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authentication/user"
utiltrace "k8s.io/utils/trace"
)
// The key type is unexported to prevent collisions
@ -39,6 +40,9 @@ const (
// audiencesKey is the context key for request audiences.
audiencesKey
// traceKey is the context key for nested tracing.
traceKey
)
// NewContext instantiates a base context object for request flows.
@ -94,3 +98,17 @@ func AuditEventFrom(ctx context.Context) *audit.Event {
ev, _ := ctx.Value(auditKey).(*audit.Event)
return ev
}
// TraceFrom returns the value of the trace key on the ctx
func TraceFrom(ctx context.Context) (*utiltrace.Trace, bool) {
trace, ok := ctx.Value(traceKey).(*utiltrace.Trace)
return trace, ok
}
// WithTrace returns a new trace using the provided msg and fields, nested within any trace already in the
// provided context. Also returns a context containing the new trace
func WithTrace(ctx context.Context, msg string, fields ...utiltrace.Field) (context.Context, *utiltrace.Trace) {
parent, _ := TraceFrom(ctx) // ignore ok since Nest can be called with a nil receiver to create root traces
trace := parent.Nest(msg, fields...)
return context.WithValue(ctx, traceKey, trace), trace
}

View File

@ -682,6 +682,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
handler = genericapifilters.WithTrace(handler, c.LongRunningFunc)
handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
if c.SecureServing != nil && !c.SecureServing.DisableHTTP2 && c.GoawayChance > 0 {
handler = genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance)

View File

@ -27,6 +27,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",

View File

@ -35,6 +35,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -604,7 +605,7 @@ func (c *Cacher) GetToList(ctx context.Context, key string, opts storage.ListOpt
return c.storage.GetToList(ctx, key, opts, listObj)
}
trace := utiltrace.New("cacher list", utiltrace.Field{"type", c.objectType.String()})
ctx, trace := genericapirequest.WithTrace(ctx, "cacher list", utiltrace.Field{"type", c.objectType.String()})
defer trace.LogIfLong(500 * time.Millisecond)
c.ready.wait()
@ -678,7 +679,7 @@ func (c *Cacher) List(ctx context.Context, key string, opts storage.ListOptions,
return c.storage.List(ctx, key, opts, listObj)
}
trace := utiltrace.New("cacher list", utiltrace.Field{"type", c.objectType.String()})
ctx, trace := genericapirequest.WithTrace(ctx, "cacher list", utiltrace.Field{"type", c.objectType.String()})
defer trace.LogIfLong(500 * time.Millisecond)
c.ready.wait()

View File

@ -75,6 +75,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd3/metrics:go_default_library",

View File

@ -38,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
@ -239,7 +240,7 @@ func (s *store) conditionalDelete(ctx context.Context, key string, out runtime.O
func (s *store) GuaranteedUpdate(
ctx context.Context, key string, out runtime.Object, ignoreNotFound bool,
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, suggestion ...runtime.Object) error {
trace := utiltrace.New("GuaranteedUpdate etcd3", utiltrace.Field{"type", getTypeName(out)})
ctx, trace := genericapirequest.WithTrace(ctx, "GuaranteedUpdate etcd3", utiltrace.Field{"type", getTypeName(out)})
defer trace.LogIfLong(500 * time.Millisecond)
v, err := conversion.EnforcePtr(out)
@ -382,7 +383,7 @@ func (s *store) GetToList(ctx context.Context, key string, listOpts storage.List
resourceVersion := listOpts.ResourceVersion
match := listOpts.ResourceVersionMatch
pred := listOpts.Predicate
trace := utiltrace.New("GetToList etcd3",
ctx, trace := genericapirequest.WithTrace(ctx, "GetToList etcd3",
utiltrace.Field{"key", key},
utiltrace.Field{"resourceVersion", resourceVersion},
utiltrace.Field{"resourceVersionMatch", match},
@ -526,7 +527,7 @@ func (s *store) List(ctx context.Context, key string, opts storage.ListOptions,
resourceVersion := opts.ResourceVersion
match := opts.ResourceVersionMatch
pred := opts.Predicate
trace := utiltrace.New("List etcd3",
ctx, trace := genericapirequest.WithTrace(ctx, "List etcd3",
utiltrace.Field{"key", key},
utiltrace.Field{"resourceVersion", resourceVersion},
utiltrace.Field{"resourceVersionMatch", match},

1
vendor/modules.txt vendored
View File

@ -1329,6 +1329,7 @@ k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager
k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal
k8s.io/apiserver/pkg/endpoints/handlers/negotiation
k8s.io/apiserver/pkg/endpoints/handlers/responsewriters
k8s.io/apiserver/pkg/endpoints/internal
k8s.io/apiserver/pkg/endpoints/metrics
k8s.io/apiserver/pkg/endpoints/openapi
k8s.io/apiserver/pkg/endpoints/request