Bump sigs.k8s.io/controller-runtime to v0.14.4 (#5507)
* Bump sigs.k8s.io/controller-runtime to v0.14.4 * Update gofmt
This commit is contained in:
39
vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go
generated
vendored
39
vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go
generated
vendored
@@ -26,6 +26,8 @@ import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
@@ -37,28 +39,23 @@ import (
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
||||
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"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"
|
||||
"k8s.io/component-base/tracing"
|
||||
"k8s.io/klog/v2"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
)
|
||||
|
||||
var namespaceGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
|
||||
|
||||
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
// For performance tracking purposes.
|
||||
trace := utiltrace.New("Create", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
ctx, span := tracing.Start(ctx, "Create", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
namespace, name, err := scope.Namer.Name(req)
|
||||
if err != nil {
|
||||
@@ -78,7 +75,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
|
||||
// enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided
|
||||
// timeout inside the parent context is lower than requestTimeoutUpperBound.
|
||||
ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound)
|
||||
ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound)
|
||||
defer cancel()
|
||||
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
|
||||
if err != nil {
|
||||
@@ -93,12 +90,13 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
return
|
||||
}
|
||||
|
||||
body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
|
||||
trace.Step("limitedReadBody done", utiltrace.Field{"len", len(body)}, utiltrace.Field{"err", err})
|
||||
body, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.Create)
|
||||
if err != nil {
|
||||
span.AddEvent("limitedReadBody failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(body)))
|
||||
|
||||
options := &metav1.CreateOptions{}
|
||||
values := req.URL.Query()
|
||||
@@ -124,7 +122,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
}
|
||||
|
||||
decoder := scope.Serializer.DecoderToVersion(decodeSerializer, scope.HubGroupVersion)
|
||||
trace.Step("About to convert to expected version")
|
||||
span.AddEvent("About to convert to expected version")
|
||||
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
|
||||
if err != nil {
|
||||
strictError, isStrictError := runtime.AsStrictDecodingError(err)
|
||||
@@ -147,7 +145,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Conversion done")
|
||||
span.AddEvent("Conversion done")
|
||||
|
||||
// On create, get name from new object if unset
|
||||
if len(name) == 0 {
|
||||
@@ -174,7 +172,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
}
|
||||
}
|
||||
|
||||
trace.Step("About to store object in database")
|
||||
span.AddEvent("About to store object in database")
|
||||
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
|
||||
requestFunc := func() (runtime.Object, error) {
|
||||
return r.Create(
|
||||
@@ -214,11 +212,12 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
}
|
||||
return result, err
|
||||
})
|
||||
trace.Step("Write to database call finished", utiltrace.Field{"len", len(body)}, utiltrace.Field{"err", err})
|
||||
if err != nil {
|
||||
span.AddEvent("Write to database call failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("Write to database call succeeded", attribute.Int("len", len(body)))
|
||||
|
||||
code := http.StatusCreated
|
||||
status, ok := result.(*metav1.Status)
|
||||
@@ -226,9 +225,9 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
||||
status.Code = int32(code)
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, code, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, code, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
generated
vendored
61
vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
generated
vendored
@@ -22,6 +22,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
@@ -33,27 +35,22 @@ import (
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
||||
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"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"
|
||||
"k8s.io/component-base/tracing"
|
||||
)
|
||||
|
||||
// DeleteResource returns a function that will handle a resource deletion
|
||||
// TODO admission here becomes solely validating admission
|
||||
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
// For performance tracking purposes.
|
||||
trace := utiltrace.New("Delete", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
ctx, span := tracing.Start(ctx, "Delete", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
namespace, name, err := scope.Namer.Name(req)
|
||||
if err != nil {
|
||||
@@ -63,7 +60,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
|
||||
// enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided
|
||||
// timeout inside the parent context is lower than requestTimeoutUpperBound.
|
||||
ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound)
|
||||
ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound)
|
||||
defer cancel()
|
||||
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
@@ -77,11 +74,13 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
|
||||
options := &metav1.DeleteOptions{}
|
||||
if allowsOptions {
|
||||
body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
|
||||
body, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.Delete)
|
||||
if err != nil {
|
||||
span.AddEvent("limitedReadBody failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(body)))
|
||||
if len(body) > 0 {
|
||||
s, err := negotiation.NegotiateInputSerializer(req, false, metainternalversionscheme.Codecs)
|
||||
if err != nil {
|
||||
@@ -100,11 +99,11 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Decoded delete options")
|
||||
span.AddEvent("Decoded delete options")
|
||||
|
||||
objGV := gvk.GroupVersion()
|
||||
audit.LogRequestObject(req.Context(), obj, objGV, scope.Resource, scope.Subresource, metainternalversionscheme.Codecs)
|
||||
trace.Step("Recorded the audit event")
|
||||
span.AddEvent("Recorded the audit event")
|
||||
} else {
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, options); err != nil {
|
||||
err = errors.NewBadRequest(err.Error())
|
||||
@@ -120,7 +119,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
}
|
||||
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("DeleteOptions"))
|
||||
|
||||
trace.Step("About to delete object from database")
|
||||
span.AddEvent("About to delete object from database")
|
||||
wasDeleted := true
|
||||
userInfo, _ := request.UserFrom(ctx)
|
||||
staticAdmissionAttrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, options, dryrun.IsDryRun(options.DryRun), userInfo)
|
||||
@@ -133,7 +132,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Object deleted from database")
|
||||
span.AddEvent("Object deleted from database")
|
||||
|
||||
status := http.StatusOK
|
||||
// Return http.StatusAccepted if the resource was not deleted immediately and
|
||||
@@ -160,22 +159,18 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
||||
}
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
// 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", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
ctx := req.Context()
|
||||
ctx, span := tracing.Start(ctx, "Delete", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
namespace, err := scope.Namer.Namespace(req)
|
||||
if err != nil {
|
||||
@@ -185,7 +180,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
|
||||
|
||||
// enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided
|
||||
// timeout inside the parent context is lower than requestTimeoutUpperBound.
|
||||
ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound)
|
||||
ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound)
|
||||
defer cancel()
|
||||
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
@@ -225,13 +220,15 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
|
||||
|
||||
options := &metav1.DeleteOptions{}
|
||||
if checkBody {
|
||||
body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
|
||||
body, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.DeleteCollection)
|
||||
if err != nil {
|
||||
span.AddEvent("limitedReadBody failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(body)))
|
||||
if len(body) > 0 {
|
||||
s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer)
|
||||
s, err := negotiation.NegotiateInputSerializer(req, false, metainternalversionscheme.Codecs)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
@@ -289,8 +286,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
|
||||
}
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
67
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go
generated
vendored
67
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/equality.go
generated
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
@@ -33,42 +34,41 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func determineAvoidNoopTimestampUpdatesEnabled() bool {
|
||||
if avoidNoopTimestampUpdatesString, exists := os.LookupEnv("KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES"); exists {
|
||||
if ret, err := strconv.ParseBool(avoidNoopTimestampUpdatesString); err == nil {
|
||||
return ret
|
||||
} else {
|
||||
klog.Errorf("failed to parse envar KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// enabled by default
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
avoidNoopTimestampUpdatesEnabled = determineAvoidNoopTimestampUpdatesEnabled()
|
||||
avoidTimestampEqualities conversion.Equalities
|
||||
initAvoidTimestampEqualities sync.Once
|
||||
)
|
||||
|
||||
var avoidTimestampEqualities = func() conversion.Equalities {
|
||||
var eqs = equality.Semantic.Copy()
|
||||
func getAvoidTimestampEqualities() conversion.Equalities {
|
||||
initAvoidTimestampEqualities.Do(func() {
|
||||
if avoidNoopTimestampUpdatesString, exists := os.LookupEnv("KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES"); exists {
|
||||
if ret, err := strconv.ParseBool(avoidNoopTimestampUpdatesString); err == nil && !ret {
|
||||
// leave avoidTimestampEqualities empty.
|
||||
return
|
||||
} else {
|
||||
klog.Errorf("failed to parse envar KUBE_APISERVER_AVOID_NOOP_SSA_TIMESTAMP_UPDATES: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
err := eqs.AddFunc(
|
||||
func(a, b metav1.ManagedFieldsEntry) bool {
|
||||
// Two objects' managed fields are equivalent if, ignoring timestamp,
|
||||
// the objects are deeply equal.
|
||||
a.Time = nil
|
||||
b.Time = nil
|
||||
return reflect.DeepEqual(a, b)
|
||||
},
|
||||
)
|
||||
var eqs = equality.Semantic.Copy()
|
||||
err := eqs.AddFunc(
|
||||
func(a, b metav1.ManagedFieldsEntry) bool {
|
||||
// Two objects' managed fields are equivalent if, ignoring timestamp,
|
||||
// the objects are deeply equal.
|
||||
a.Time = nil
|
||||
b.Time = nil
|
||||
return reflect.DeepEqual(a, b)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to instantiate semantic equalities: %w", err))
|
||||
}
|
||||
|
||||
return eqs
|
||||
}()
|
||||
avoidTimestampEqualities = eqs
|
||||
})
|
||||
return avoidTimestampEqualities
|
||||
}
|
||||
|
||||
// IgnoreManagedFieldsTimestampsTransformer reverts timestamp updates
|
||||
// if the non-managed parts of the object are equivalent
|
||||
@@ -77,7 +77,8 @@ func IgnoreManagedFieldsTimestampsTransformer(
|
||||
newObj runtime.Object,
|
||||
oldObj runtime.Object,
|
||||
) (res runtime.Object, err error) {
|
||||
if !avoidNoopTimestampUpdatesEnabled {
|
||||
equalities := getAvoidTimestampEqualities()
|
||||
if len(equalities.Equalities) == 0 {
|
||||
return newObj, nil
|
||||
}
|
||||
|
||||
@@ -154,11 +155,11 @@ func IgnoreManagedFieldsTimestampsTransformer(
|
||||
// This condition ensures the managed fields are always compared first. If
|
||||
// this check fails, the if statement will short circuit. If the check
|
||||
// succeeds the slow path is taken which compares entire objects.
|
||||
if !avoidTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) {
|
||||
if !equalities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) {
|
||||
return newObj, nil
|
||||
}
|
||||
|
||||
if avoidTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) {
|
||||
if equalities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) {
|
||||
// Remove any changed timestamps, so that timestamp is not the only
|
||||
// change seen by etcd.
|
||||
//
|
||||
|
||||
3
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go
generated
vendored
3
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go
generated
vendored
@@ -202,8 +202,7 @@ func (f *FieldManager) UpdateNoErrors(liveObj, newObj runtime.Object, manager st
|
||||
if err != nil {
|
||||
atMostEverySecond.Do(func() {
|
||||
ns, name := "unknown", "unknown"
|
||||
accessor, err := meta.Accessor(newObj)
|
||||
if err == nil {
|
||||
if accessor, err := meta.Accessor(newObj); err == nil {
|
||||
ns = accessor.GetNamespace()
|
||||
name = accessor.GetName()
|
||||
}
|
||||
|
||||
55
vendor/k8s.io/apiserver/pkg/endpoints/handlers/get.go
generated
vendored
55
vendor/k8s.io/apiserver/pkg/endpoints/handlers/get.go
generated
vendored
@@ -25,42 +25,42 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metainternalversionvalidation "k8s.io/apimachinery/pkg/apis/meta/internalversion/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metainternalversionvalidation "k8s.io/apimachinery/pkg/apis/meta/internalversion/validation"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
"k8s.io/component-base/tracing"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// getterFunc performs a get request with the given context and object name. The request
|
||||
// may be used to deserialize an options object to pass to the getter.
|
||||
type getterFunc func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error)
|
||||
type getterFunc func(ctx context.Context, name string, req *http.Request) (runtime.Object, error)
|
||||
|
||||
// getResourceHandler is an HTTP handler function for get requests. It delegates to the
|
||||
// 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", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
ctx := req.Context()
|
||||
ctx, span := tracing.Start(ctx, "Get", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
namespace, name, err := scope.Namer.Name(req)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
ctx := req.Context()
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
|
||||
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
|
||||
@@ -69,22 +69,22 @@ func getResourceHandler(scope *RequestScope, getter getterFunc) http.HandlerFunc
|
||||
return
|
||||
}
|
||||
|
||||
result, err := getter(ctx, name, req, trace)
|
||||
result, err := getter(ctx, name, req)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
|
||||
func GetResource(r rest.Getter, scope *RequestScope) http.HandlerFunc {
|
||||
return getResourceHandler(scope,
|
||||
func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
|
||||
func(ctx context.Context, name string, req *http.Request) (runtime.Object, error) {
|
||||
// check for export
|
||||
options := metav1.GetOptions{}
|
||||
if values := req.URL.Query(); len(values) > 0 {
|
||||
@@ -104,9 +104,7 @@ func GetResource(r rest.Getter, scope *RequestScope) http.HandlerFunc {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if trace != nil {
|
||||
trace.Step("About to Get from storage")
|
||||
}
|
||||
tracing.SpanFromContext(ctx).AddEvent("About to Get from storage")
|
||||
return r.Get(ctx, name, &options)
|
||||
})
|
||||
}
|
||||
@@ -114,16 +112,15 @@ func GetResource(r rest.Getter, scope *RequestScope) http.HandlerFunc {
|
||||
// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
|
||||
func GetResourceWithOptions(r rest.GetterWithOptions, scope *RequestScope, isSubresource bool) http.HandlerFunc {
|
||||
return getResourceHandler(scope,
|
||||
func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
|
||||
func(ctx context.Context, name string, req *http.Request) (runtime.Object, error) {
|
||||
opts, subpath, subpathKey := r.NewGetOptions()
|
||||
trace.Step("About to process Get options")
|
||||
span := tracing.SpanFromContext(ctx)
|
||||
span.AddEvent("About to process Get options")
|
||||
if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
|
||||
err = errors.NewBadRequest(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if trace != nil {
|
||||
trace.Step("About to Get from storage")
|
||||
}
|
||||
span.AddEvent("About to Get from storage")
|
||||
return r.Get(ctx, name, opts)
|
||||
})
|
||||
}
|
||||
@@ -168,8 +165,9 @@ 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) {
|
||||
ctx := req.Context()
|
||||
// For performance tracking purposes.
|
||||
trace := utiltrace.New("List", traceFields(req)...)
|
||||
ctx, span := tracing.Start(ctx, "List", traceFields(req)...)
|
||||
|
||||
namespace, err := scope.Namer.Namespace(req)
|
||||
if err != nil {
|
||||
@@ -185,7 +183,6 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope *RequestScope, forceWatc
|
||||
hasName = false
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
|
||||
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
|
||||
@@ -273,15 +270,15 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope *RequestScope, forceWatc
|
||||
}
|
||||
|
||||
// Log only long List requests (ignore Watch).
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
trace.Step("About to List from storage")
|
||||
defer span.End(500 * time.Millisecond)
|
||||
span.AddEvent("About to List from storage")
|
||||
result, err := r.List(ctx, &opts)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Listing from storage done")
|
||||
defer trace.Step("Writing http response done", utiltrace.Field{"count", meta.LenList(result)})
|
||||
transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
|
||||
span.AddEvent("Listing from storage done")
|
||||
defer span.AddEvent("Writing http response done", attribute.Int("count", meta.LenList(result)))
|
||||
transformResponseObject(ctx, scope, req, w, http.StatusOK, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
55
vendor/k8s.io/apiserver/pkg/endpoints/handlers/helpers.go
generated
vendored
55
vendor/k8s.io/apiserver/pkg/endpoints/handlers/helpers.go
generated
vendored
@@ -20,7 +20,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -83,7 +85,56 @@ type lazyAuditID struct {
|
||||
|
||||
func (lazy *lazyAuditID) String() string {
|
||||
if lazy.req != nil {
|
||||
return request.GetAuditIDTruncated(lazy.req.Context())
|
||||
return audit.GetAuditIDTruncated(lazy.req.Context())
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// lazyVerb implements String() string and it will
|
||||
// lazily get normalized Verb
|
||||
type lazyVerb struct {
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (lazy *lazyVerb) String() string {
|
||||
if lazy.req == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return metrics.NormalizedVerb(lazy.req)
|
||||
}
|
||||
|
||||
// lazyResource implements String() string and it will
|
||||
// lazily get Resource from request info
|
||||
type lazyResource struct {
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (lazy *lazyResource) String() string {
|
||||
if lazy.req != nil {
|
||||
ctx := lazy.req.Context()
|
||||
requestInfo, ok := apirequest.RequestInfoFrom(ctx)
|
||||
if ok {
|
||||
return requestInfo.Resource
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// lazyScope implements String() string and it will
|
||||
// lazily get Scope from request info
|
||||
type lazyScope struct {
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (lazy *lazyScope) String() string {
|
||||
if lazy.req != nil {
|
||||
ctx := lazy.req.Context()
|
||||
requestInfo, ok := apirequest.RequestInfoFrom(ctx)
|
||||
if ok {
|
||||
return metrics.CleanScope(requestInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
|
||||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS
generated
vendored
Normal file
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/metrics/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- logicalhan
|
||||
51
vendor/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go
generated
vendored
Normal file
51
vendor/k8s.io/apiserver/pkg/endpoints/handlers/metrics/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"k8s.io/component-base/metrics"
|
||||
)
|
||||
|
||||
type RequestBodyVerb string
|
||||
|
||||
const (
|
||||
Patch RequestBodyVerb = "patch"
|
||||
Delete RequestBodyVerb = "delete"
|
||||
Update RequestBodyVerb = "update"
|
||||
Create RequestBodyVerb = "create"
|
||||
DeleteCollection RequestBodyVerb = "delete_collection"
|
||||
)
|
||||
|
||||
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"},
|
||||
)
|
||||
)
|
||||
|
||||
func RecordRequestBodySize(ctx context.Context, resource string, verb RequestBodyVerb, size int) {
|
||||
RequestBodySizes.WithContext(ctx).WithLabelValues(resource, string(verb)).Observe(float64(size))
|
||||
}
|
||||
43
vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
generated
vendored
43
vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
generated
vendored
@@ -23,9 +23,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
kjson "sigs.k8s.io/json"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
@@ -45,13 +46,12 @@ import (
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
||||
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"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"
|
||||
"k8s.io/component-base/tracing"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -62,14 +62,10 @@ const (
|
||||
// PatchResource returns a function that will handle a resource patch.
|
||||
func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
// For performance tracking purposes.
|
||||
trace := utiltrace.New("Patch", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
ctx, span := tracing.Start(ctx, "Patch", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
// Do this first, otherwise name extraction can fail for unrecognized content types
|
||||
// TODO: handle this in negotiation
|
||||
@@ -94,7 +90,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
||||
|
||||
// enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided
|
||||
// timeout inside the parent context is lower than requestTimeoutUpperBound.
|
||||
ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound)
|
||||
ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound)
|
||||
defer cancel()
|
||||
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
@@ -105,12 +101,13 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
||||
return
|
||||
}
|
||||
|
||||
patchBytes, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
|
||||
trace.Step("limitedReadBody done", utiltrace.Field{"len", len(patchBytes)}, utiltrace.Field{"err", err})
|
||||
patchBytes, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.Patch)
|
||||
if err != nil {
|
||||
span.AddEvent("limitedReadBody failed", attribute.Int("len", len(patchBytes)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(patchBytes)))
|
||||
|
||||
options := &metav1.PatchOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, options); err != nil {
|
||||
@@ -128,7 +125,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
||||
admit = admission.WithAudit(admit)
|
||||
|
||||
audit.LogRequestPatch(req.Context(), patchBytes)
|
||||
trace.Step("Recorded the audit event")
|
||||
span.AddEvent("Recorded the audit event")
|
||||
|
||||
baseContentType := runtime.ContentTypeJSON
|
||||
if patchType == types.ApplyPatchType {
|
||||
@@ -225,8 +222,6 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
||||
patchType: patchType,
|
||||
patchBytes: patchBytes,
|
||||
userAgent: req.UserAgent(),
|
||||
|
||||
trace: trace,
|
||||
}
|
||||
|
||||
result, wasCreated, err := p.patchResource(ctx, scope)
|
||||
@@ -234,16 +229,16 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Object stored in database")
|
||||
span.AddEvent("Object stored in database")
|
||||
|
||||
status := http.StatusOK
|
||||
if wasCreated {
|
||||
status = http.StatusCreated
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +282,6 @@ type patcher struct {
|
||||
patchBytes []byte
|
||||
userAgent string
|
||||
|
||||
trace *utiltrace.Trace
|
||||
|
||||
// Set at invocation-time (by applyPatch) and immutable thereafter
|
||||
namespace string
|
||||
updatedObjectInfo rest.UpdatedObjectInfo
|
||||
@@ -550,7 +543,7 @@ func strategicPatchObject(
|
||||
// TODO: rename this function because the name implies it is related to applyPatcher
|
||||
func (p *patcher) applyPatch(ctx context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
|
||||
// Make sure we actually have a persisted currentObject
|
||||
p.trace.Step("About to apply patch")
|
||||
tracing.SpanFromContext(ctx).AddEvent("About to apply patch")
|
||||
currentObjectHasUID, err := hasUID(currentObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -599,7 +592,7 @@ func (p *patcher) admissionAttributes(ctx context.Context, updatedObject runtime
|
||||
// and is given the currently persisted object and the patched object as input.
|
||||
// TODO: rename this function because the name implies it is related to applyPatcher
|
||||
func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) {
|
||||
p.trace.Step("About to check admission control")
|
||||
tracing.SpanFromContext(ctx).AddEvent("About to check admission control")
|
||||
var operation admission.Operation
|
||||
var options runtime.Object
|
||||
if hasUID, err := hasUID(currentObject); err != nil {
|
||||
|
||||
5
vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
generated
vendored
5
vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
generated
vendored
@@ -32,7 +32,6 @@ import (
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
endpointsrequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
)
|
||||
|
||||
// transformObject takes the object as returned by storage and ensures it is in
|
||||
@@ -129,7 +128,7 @@ func targetEncodingForTransform(scope *RequestScope, mediaType negotiation.Media
|
||||
|
||||
// transformResponseObject takes an object loaded from storage and performs any necessary transformations.
|
||||
// Will write the complete response object.
|
||||
func transformResponseObject(ctx context.Context, scope *RequestScope, trace *utiltrace.Trace, req *http.Request, w http.ResponseWriter, statusCode int, mediaType negotiation.MediaTypeOptions, result runtime.Object) {
|
||||
func transformResponseObject(ctx context.Context, scope *RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, mediaType negotiation.MediaTypeOptions, result runtime.Object) {
|
||||
options, err := optionsForTransform(mediaType, req)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
@@ -147,7 +146,7 @@ func transformResponseObject(ctx context.Context, scope *RequestScope, trace *ut
|
||||
return
|
||||
}
|
||||
kind, serializer, _ := targetEncodingForTransform(scope, mediaType, req)
|
||||
responsewriters.WriteObjectNegotiated(serializer, scope, kind.GroupVersion(), w, req, statusCode, obj)
|
||||
responsewriters.WriteObjectNegotiated(serializer, scope, kind.GroupVersion(), w, req, statusCode, obj, false)
|
||||
}
|
||||
|
||||
// errNotAcceptable indicates Accept negotiation has failed
|
||||
|
||||
85
vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go
generated
vendored
85
vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go
generated
vendored
@@ -18,6 +18,7 @@ package responsewriters
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -27,6 +28,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -40,7 +43,7 @@ import (
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/apiserver/pkg/util/flushwriter"
|
||||
"k8s.io/apiserver/pkg/util/wsstream"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
"k8s.io/component-base/tracing"
|
||||
)
|
||||
|
||||
// StreamObject performs input stream negotiation from a ResourceStreamer and writes that to the response.
|
||||
@@ -87,21 +90,22 @@ func StreamObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSe
|
||||
// The context is optional and can be nil. This method will perform optional content compression if requested by
|
||||
// a client and the feature gate for APIResponseCompression is enabled.
|
||||
func SerializeObject(mediaType string, encoder runtime.Encoder, hw http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
|
||||
trace := utiltrace.New("SerializeObject",
|
||||
utiltrace.Field{"audit-id", request.GetAuditIDTruncated(req.Context())},
|
||||
utiltrace.Field{"method", req.Method},
|
||||
utiltrace.Field{"url", req.URL.Path},
|
||||
utiltrace.Field{"protocol", req.Proto},
|
||||
utiltrace.Field{"mediaType", mediaType},
|
||||
utiltrace.Field{"encoder", encoder.Identifier()})
|
||||
defer trace.LogIfLong(5 * time.Second)
|
||||
ctx := req.Context()
|
||||
ctx, span := tracing.Start(ctx, "SerializeObject",
|
||||
attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)),
|
||||
attribute.String("method", req.Method),
|
||||
attribute.String("url", req.URL.Path),
|
||||
attribute.String("protocol", req.Proto),
|
||||
attribute.String("mediaType", mediaType),
|
||||
attribute.String("encoder", string(encoder.Identifier())))
|
||||
defer span.End(5 * time.Second)
|
||||
|
||||
w := &deferredResponseWriter{
|
||||
mediaType: mediaType,
|
||||
statusCode: statusCode,
|
||||
contentEncoding: negotiateContentEncoding(req),
|
||||
hw: hw,
|
||||
trace: trace,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
err := encoder.Encode(object, w)
|
||||
@@ -191,23 +195,30 @@ type deferredResponseWriter struct {
|
||||
hw http.ResponseWriter
|
||||
w io.Writer
|
||||
|
||||
trace *utiltrace.Trace
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (w *deferredResponseWriter) Write(p []byte) (n int, err error) {
|
||||
if w.trace != nil {
|
||||
// This Step usually wraps in-memory object serialization.
|
||||
w.trace.Step("About to start writing response", utiltrace.Field{"size", len(p)})
|
||||
ctx := w.ctx
|
||||
span := tracing.SpanFromContext(ctx)
|
||||
// This Step usually wraps in-memory object serialization.
|
||||
span.AddEvent("About to start writing response", attribute.Int("size", len(p)))
|
||||
|
||||
firstWrite := !w.hasWritten
|
||||
defer func() {
|
||||
w.trace.Step("Write call finished",
|
||||
utiltrace.Field{"writer", fmt.Sprintf("%T", w.w)},
|
||||
utiltrace.Field{"size", len(p)},
|
||||
utiltrace.Field{"firstWrite", firstWrite},
|
||||
utiltrace.Field{"err", err})
|
||||
}()
|
||||
}
|
||||
firstWrite := !w.hasWritten
|
||||
defer func() {
|
||||
if err != nil {
|
||||
span.AddEvent("Write call failed",
|
||||
attribute.String("writer", fmt.Sprintf("%T", w.w)),
|
||||
attribute.Int("size", len(p)),
|
||||
attribute.Bool("firstWrite", firstWrite),
|
||||
attribute.String("err", err.Error()))
|
||||
} else {
|
||||
span.AddEvent("Write call succeeded",
|
||||
attribute.String("writer", fmt.Sprintf("%T", w.w)),
|
||||
attribute.Int("size", len(p)),
|
||||
attribute.Bool("firstWrite", firstWrite))
|
||||
}
|
||||
}()
|
||||
if w.hasWritten {
|
||||
return w.w.Write(p)
|
||||
}
|
||||
@@ -248,7 +259,7 @@ func (w *deferredResponseWriter) Close() error {
|
||||
}
|
||||
|
||||
// WriteObjectNegotiated renders an object in the content type negotiated by the client.
|
||||
func WriteObjectNegotiated(s runtime.NegotiatedSerializer, restrictions negotiation.EndpointRestrictions, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
|
||||
func WriteObjectNegotiated(s runtime.NegotiatedSerializer, restrictions negotiation.EndpointRestrictions, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object, listGVKInContentType bool) {
|
||||
stream, ok := object.(rest.ResourceStreamer)
|
||||
if ok {
|
||||
requestInfo, _ := request.RequestInfoFrom(req.Context())
|
||||
@@ -258,7 +269,7 @@ func WriteObjectNegotiated(s runtime.NegotiatedSerializer, restrictions negotiat
|
||||
return
|
||||
}
|
||||
|
||||
_, serializer, err := negotiation.NegotiateOutputMediaType(req, s, restrictions)
|
||||
mediaType, serializer, err := negotiation.NegotiateOutputMediaType(req, s, restrictions)
|
||||
if err != nil {
|
||||
// if original statusCode was not successful we need to return the original error
|
||||
// we cannot hide it behind negotiation problems
|
||||
@@ -275,10 +286,30 @@ func WriteObjectNegotiated(s runtime.NegotiatedSerializer, restrictions negotiat
|
||||
|
||||
encoder := s.EncoderForVersion(serializer.Serializer, gv)
|
||||
request.TrackSerializeResponseObjectLatency(req.Context(), func() {
|
||||
SerializeObject(serializer.MediaType, encoder, w, req, statusCode, object)
|
||||
if listGVKInContentType {
|
||||
SerializeObject(generateMediaTypeWithGVK(serializer.MediaType, mediaType.Convert), encoder, w, req, statusCode, object)
|
||||
} else {
|
||||
SerializeObject(serializer.MediaType, encoder, w, req, statusCode, object)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func generateMediaTypeWithGVK(mediaType string, gvk *schema.GroupVersionKind) string {
|
||||
if gvk == nil {
|
||||
return mediaType
|
||||
}
|
||||
if gvk.Group != "" {
|
||||
mediaType += ";g=" + gvk.Group
|
||||
}
|
||||
if gvk.Version != "" {
|
||||
mediaType += ";v=" + gvk.Version
|
||||
}
|
||||
if gvk.Kind != "" {
|
||||
mediaType += ";as=" + gvk.Kind
|
||||
}
|
||||
return mediaType
|
||||
}
|
||||
|
||||
// ErrorNegotiated renders an error to the response. Returns the HTTP status code of the error.
|
||||
// The context is optional and may be nil.
|
||||
func ErrorNegotiated(err error, s runtime.NegotiatedSerializer, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request) int {
|
||||
@@ -295,7 +326,7 @@ func ErrorNegotiated(err error, s runtime.NegotiatedSerializer, gv schema.GroupV
|
||||
return code
|
||||
}
|
||||
|
||||
WriteObjectNegotiated(s, negotiation.DefaultEndpointRestrictions, gv, w, req, code, status)
|
||||
WriteObjectNegotiated(s, negotiation.DefaultEndpointRestrictions, gv, w, req, code, status, false)
|
||||
return code
|
||||
}
|
||||
|
||||
|
||||
12
vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
generated
vendored
12
vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
generated
vendored
@@ -41,6 +41,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
@@ -236,7 +237,7 @@ type responder struct {
|
||||
}
|
||||
|
||||
func (r *responder) Object(statusCode int, obj runtime.Object) {
|
||||
responsewriters.WriteObjectNegotiated(r.scope.Serializer, r.scope, r.scope.Kind.GroupVersion(), r.w, r.req, statusCode, obj)
|
||||
responsewriters.WriteObjectNegotiated(r.scope.Serializer, r.scope, r.scope.Kind.GroupVersion(), r.w, r.req, statusCode, obj, false)
|
||||
}
|
||||
|
||||
func (r *responder) Error(err error) {
|
||||
@@ -390,6 +391,15 @@ func limitedReadBody(req *http.Request, limit int64) ([]byte, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func limitedReadBodyWithRecordMetric(ctx context.Context, req *http.Request, limit int64, resourceGroup string, verb requestmetrics.RequestBodyVerb) ([]byte, error) {
|
||||
readBody, err := limitedReadBody(req, limit)
|
||||
if err == nil {
|
||||
// only record if we've read successfully
|
||||
requestmetrics.RecordRequestBodySize(ctx, resourceGroup, verb, len(readBody))
|
||||
}
|
||||
return readBody, err
|
||||
}
|
||||
|
||||
func isDryRun(url *url.URL) bool {
|
||||
return len(url.Query()["dryRun"]) != 0
|
||||
}
|
||||
|
||||
22
vendor/k8s.io/apiserver/pkg/endpoints/handlers/trace_util.go
generated
vendored
22
vendor/k8s.io/apiserver/pkg/endpoints/handlers/trace_util.go
generated
vendored
@@ -19,15 +19,19 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
func traceFields(req *http.Request) []utiltrace.Field {
|
||||
return []utiltrace.Field{
|
||||
{Key: "url", Value: req.URL.Path},
|
||||
{Key: "user-agent", Value: &lazyTruncatedUserAgent{req: req}},
|
||||
{Key: "audit-id", Value: &lazyAuditID{req: req}},
|
||||
{Key: "client", Value: &lazyClientIP{req: req}},
|
||||
{Key: "accept", Value: &lazyAccept{req: req}},
|
||||
{Key: "protocol", Value: req.Proto}}
|
||||
func traceFields(req *http.Request) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
attribute.Stringer("accept", &lazyAccept{req: req}),
|
||||
attribute.Stringer("audit-id", &lazyAuditID{req: req}),
|
||||
attribute.Stringer("client", &lazyClientIP{req: req}),
|
||||
attribute.String("protocol", req.Proto),
|
||||
attribute.Stringer("resource", &lazyResource{req: req}),
|
||||
attribute.Stringer("scope", &lazyScope{req: req}),
|
||||
attribute.String("url", req.URL.Path),
|
||||
attribute.Stringer("user-agent", &lazyTruncatedUserAgent{req: req}),
|
||||
attribute.Stringer("verb", &lazyVerb{req: req}),
|
||||
}
|
||||
}
|
||||
|
||||
39
vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
generated
vendored
39
vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
generated
vendored
@@ -23,6 +23,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
@@ -35,27 +37,22 @@ import (
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
||||
requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||
"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"
|
||||
"k8s.io/component-base/tracing"
|
||||
"k8s.io/klog/v2"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
)
|
||||
|
||||
// UpdateResource returns a function that will handle a resource update
|
||||
func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interface) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
// For performance tracking purposes.
|
||||
trace := utiltrace.New("Update", traceFields(req)...)
|
||||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
ctx, span := tracing.Start(ctx, "Update", traceFields(req)...)
|
||||
defer span.End(500 * time.Millisecond)
|
||||
|
||||
namespace, name, err := scope.Namer.Name(req)
|
||||
if err != nil {
|
||||
@@ -65,7 +62,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
|
||||
// enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided
|
||||
// timeout inside the parent context is lower than requestTimeoutUpperBound.
|
||||
ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound)
|
||||
ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound)
|
||||
defer cancel()
|
||||
|
||||
ctx = request.WithNamespace(ctx, namespace)
|
||||
@@ -76,12 +73,13 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
return
|
||||
}
|
||||
|
||||
body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
|
||||
trace.Step("limitedReadBody done", utiltrace.Field{"len", len(body)}, utiltrace.Field{"err", err})
|
||||
body, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.Update)
|
||||
if err != nil {
|
||||
span.AddEvent("limitedReadBody failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(body)))
|
||||
|
||||
options := &metav1.UpdateOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, options); err != nil {
|
||||
@@ -111,7 +109,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
}
|
||||
|
||||
decoder := scope.Serializer.DecoderToVersion(decodeSerializer, scope.HubGroupVersion)
|
||||
trace.Step("About to convert to expected version")
|
||||
span.AddEvent("About to convert to expected version")
|
||||
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
|
||||
if err != nil {
|
||||
strictError, isStrictError := runtime.AsStrictDecodingError(err)
|
||||
@@ -134,7 +132,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
trace.Step("Conversion done")
|
||||
span.AddEvent("Conversion done")
|
||||
|
||||
audit.LogRequestObject(req.Context(), obj, objGV, scope.Resource, scope.Subresource, scope.Serializer)
|
||||
admit = admission.WithAudit(admit)
|
||||
@@ -213,7 +211,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
Name: name,
|
||||
}
|
||||
|
||||
trace.Step("About to store object in database")
|
||||
span.AddEvent("About to store object in database")
|
||||
wasCreated := false
|
||||
requestFunc := func() (runtime.Object, error) {
|
||||
obj, created, err := r.Update(
|
||||
@@ -248,20 +246,21 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
||||
}
|
||||
return result, err
|
||||
})
|
||||
trace.Step("Write to database call finished", utiltrace.Field{"len", len(body)}, utiltrace.Field{"err", err})
|
||||
if err != nil {
|
||||
span.AddEvent("Write to database call failed", attribute.Int("len", len(body)), attribute.String("err", err.Error()))
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
span.AddEvent("Write to database call succeeded", attribute.Int("len", len(body)))
|
||||
|
||||
status := http.StatusOK
|
||||
if wasCreated {
|
||||
status = http.StatusCreated
|
||||
}
|
||||
|
||||
trace.Step("About to write a response")
|
||||
defer trace.Step("Writing http response done")
|
||||
transformResponseObject(ctx, scope, trace, req, w, status, outputMediaType, result)
|
||||
span.AddEvent("About to write a response")
|
||||
defer span.AddEvent("Writing http response done")
|
||||
transformResponseObject(ctx, scope, req, w, status, outputMediaType, result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user