mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 10:17:51 +00:00
Update custom-metrics-apiserver and metrics-server
This commit is contained in:
parent
4c673534f2
commit
b480e45a67
915 changed files with 63694 additions and 106514 deletions
95
vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go
generated
vendored
95
vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go
generated
vendored
|
|
@ -18,8 +18,8 @@ package filters
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
|
@ -28,61 +28,9 @@ import (
|
|||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
/*
|
||||
* By default, all the following metrics are defined as falling under
|
||||
* ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/20190404-kubernetes-control-plane-metrics-stability.md#stability-classes)
|
||||
*
|
||||
* Promoting the stability level of the metric is a responsibility of the component owner, since it
|
||||
* involves explicitly acknowledging support for the metric across multiple releases, in accordance with
|
||||
* the metric stability policy.
|
||||
*/
|
||||
const (
|
||||
successLabel = "success"
|
||||
failureLabel = "failure"
|
||||
errorLabel = "error"
|
||||
)
|
||||
|
||||
var (
|
||||
authenticatedUserCounter = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Name: "authenticated_user_requests",
|
||||
Help: "Counter of authenticated requests broken out by username.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"username"},
|
||||
)
|
||||
|
||||
authenticatedAttemptsCounter = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Name: "authentication_attempts",
|
||||
Help: "Counter of authenticated attempts.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"result"},
|
||||
)
|
||||
|
||||
authenticationLatency = metrics.NewHistogramVec(
|
||||
&metrics.HistogramOpts{
|
||||
Name: "authentication_duration_seconds",
|
||||
Help: "Authentication duration in seconds broken out by result.",
|
||||
Buckets: metrics.ExponentialBuckets(0.001, 2, 15),
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"result"},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
legacyregistry.MustRegister(authenticatedUserCounter)
|
||||
legacyregistry.MustRegister(authenticatedAttemptsCounter)
|
||||
legacyregistry.MustRegister(authenticationLatency)
|
||||
}
|
||||
|
||||
// WithAuthentication creates an http handler that tries to authenticate the given request as a user, and then
|
||||
// stores any such user found onto the provided context for the request. If authentication fails or returns an error
|
||||
// the failed handler is used. On success, "Authorization" header is removed from the request and handler
|
||||
|
|
@ -99,22 +47,18 @@ func WithAuthentication(handler http.Handler, auth authenticator.Request, failed
|
|||
req = req.WithContext(authenticator.WithAudiences(req.Context(), apiAuds))
|
||||
}
|
||||
resp, ok, err := auth.AuthenticateRequest(req)
|
||||
defer recordAuthMetrics(resp, ok, err, apiAuds, authenticationStart)
|
||||
if err != nil || !ok {
|
||||
if err != nil {
|
||||
klog.Errorf("Unable to authenticate the request due to an error: %v", err)
|
||||
authenticatedAttemptsCounter.WithLabelValues(errorLabel).Inc()
|
||||
authenticationLatency.WithLabelValues(errorLabel).Observe(time.Since(authenticationStart).Seconds())
|
||||
} else if !ok {
|
||||
authenticatedAttemptsCounter.WithLabelValues(failureLabel).Inc()
|
||||
authenticationLatency.WithLabelValues(failureLabel).Observe(time.Since(authenticationStart).Seconds())
|
||||
}
|
||||
|
||||
failed.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
if len(apiAuds) > 0 && len(resp.Audiences) > 0 && len(authenticator.Audiences(apiAuds).Intersect(resp.Audiences)) == 0 {
|
||||
klog.Errorf("Unable to match the audience: %v , accepted: %v", resp.Audiences, apiAuds)
|
||||
if !audiencesAreAcceptable(apiAuds, resp.Audiences) {
|
||||
err = fmt.Errorf("unable to match the audience: %v , accepted: %v", resp.Audiences, apiAuds)
|
||||
klog.Error(err)
|
||||
failed.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
|
@ -123,11 +67,6 @@ func WithAuthentication(handler http.Handler, auth authenticator.Request, failed
|
|||
req.Header.Del("Authorization")
|
||||
|
||||
req = req.WithContext(genericapirequest.WithUser(req.Context(), resp.User))
|
||||
|
||||
authenticatedUserCounter.WithLabelValues(compressUsername(resp.User.GetName())).Inc()
|
||||
authenticatedAttemptsCounter.WithLabelValues(successLabel).Inc()
|
||||
authenticationLatency.WithLabelValues(successLabel).Observe(time.Since(authenticationStart).Seconds())
|
||||
|
||||
handler.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
|
|
@ -149,24 +88,10 @@ func Unauthorized(s runtime.NegotiatedSerializer, supportsBasicAuth bool) http.H
|
|||
})
|
||||
}
|
||||
|
||||
// compressUsername maps all possible usernames onto a small set of categories
|
||||
// of usernames. This is done both to limit the cardinality of the
|
||||
// authorized_user_requests metric, and to avoid pushing actual usernames in the
|
||||
// metric.
|
||||
func compressUsername(username string) string {
|
||||
switch {
|
||||
// Known internal identities.
|
||||
case username == "admin" ||
|
||||
username == "client" ||
|
||||
username == "kube_proxy" ||
|
||||
username == "kubelet" ||
|
||||
username == "system:serviceaccount:kube-system:default":
|
||||
return username
|
||||
// Probably an email address.
|
||||
case strings.Contains(username, "@"):
|
||||
return "email_id"
|
||||
// Anything else (custom service accounts, custom external identities, etc.)
|
||||
default:
|
||||
return "other"
|
||||
func audiencesAreAcceptable(apiAuds, responseAudiences authenticator.Audiences) bool {
|
||||
if len(apiAuds) == 0 || len(responseAudiences) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return len(apiAuds.Intersect(responseAudiences)) > 0
|
||||
}
|
||||
|
|
|
|||
115
vendor/k8s.io/apiserver/pkg/endpoints/filters/metrics.go
generated
vendored
Normal file
115
vendor/k8s.io/apiserver/pkg/endpoints/filters/metrics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
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 (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
||||
/*
|
||||
* By default, all the following metrics are defined as falling under
|
||||
* ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/20190404-kubernetes-control-plane-metrics-stability.md#stability-classes)
|
||||
*
|
||||
* Promoting the stability level of the metric is a responsibility of the component owner, since it
|
||||
* involves explicitly acknowledging support for the metric across multiple releases, in accordance with
|
||||
* the metric stability policy.
|
||||
*/
|
||||
const (
|
||||
successLabel = "success"
|
||||
failureLabel = "failure"
|
||||
errorLabel = "error"
|
||||
)
|
||||
|
||||
var (
|
||||
authenticatedUserCounter = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Name: "authenticated_user_requests",
|
||||
Help: "Counter of authenticated requests broken out by username.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"username"},
|
||||
)
|
||||
|
||||
authenticatedAttemptsCounter = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Name: "authentication_attempts",
|
||||
Help: "Counter of authenticated attempts.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"result"},
|
||||
)
|
||||
|
||||
authenticationLatency = metrics.NewHistogramVec(
|
||||
&metrics.HistogramOpts{
|
||||
Name: "authentication_duration_seconds",
|
||||
Help: "Authentication duration in seconds broken out by result.",
|
||||
Buckets: metrics.ExponentialBuckets(0.001, 2, 15),
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"result"},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
legacyregistry.MustRegister(authenticatedUserCounter)
|
||||
legacyregistry.MustRegister(authenticatedAttemptsCounter)
|
||||
legacyregistry.MustRegister(authenticationLatency)
|
||||
}
|
||||
|
||||
func recordAuthMetrics(resp *authenticator.Response, ok bool, err error, apiAudiences authenticator.Audiences, authStart time.Time) {
|
||||
var resultLabel string
|
||||
|
||||
switch {
|
||||
case err != nil || (resp != nil && !audiencesAreAcceptable(apiAudiences, resp.Audiences)):
|
||||
resultLabel = errorLabel
|
||||
case !ok:
|
||||
resultLabel = failureLabel
|
||||
default:
|
||||
resultLabel = successLabel
|
||||
authenticatedUserCounter.WithLabelValues(compressUsername(resp.User.GetName())).Inc()
|
||||
}
|
||||
|
||||
authenticatedAttemptsCounter.WithLabelValues(resultLabel).Inc()
|
||||
authenticationLatency.WithLabelValues(resultLabel).Observe(time.Since(authStart).Seconds())
|
||||
}
|
||||
|
||||
// compressUsername maps all possible usernames onto a small set of categories
|
||||
// of usernames. This is done both to limit the cardinality of the
|
||||
// authorized_user_requests metric, and to avoid pushing actual usernames in the
|
||||
// metric.
|
||||
func compressUsername(username string) string {
|
||||
switch {
|
||||
// Known internal identities.
|
||||
case username == "admin" ||
|
||||
username == "client" ||
|
||||
username == "kube_proxy" ||
|
||||
username == "kubelet" ||
|
||||
username == "system:serviceaccount:kube-system:default":
|
||||
return username
|
||||
// Probably an email address.
|
||||
case strings.Contains(username, "@"):
|
||||
return "email_id"
|
||||
// Anything else (custom service accounts, custom external identities, etc.)
|
||||
default:
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
52
vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go
generated
vendored
52
vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go
generated
vendored
|
|
@ -27,6 +27,7 @@ import (
|
|||
"unicode/utf8"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
|
|
@ -50,7 +51,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun alpha feature is disabled"), w, req)
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -137,31 +138,10 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||
if len(name) == 0 {
|
||||
_, name, _ = scope.Namer.ObjectName(obj)
|
||||
}
|
||||
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
|
||||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
|
||||
err = mutatingAdmission.Admit(ctx, admissionAttributes, scope)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if scope.FieldManager != nil {
|
||||
liveObj, err := scope.Creater.New(scope.Kind)
|
||||
if err != nil {
|
||||
scope.err(fmt.Errorf("failed to create new object (Create for %v): %v", scope.Kind, err), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
obj, err = scope.FieldManager.Update(liveObj, obj, managerOrUserAgent(options.FieldManager, req.UserAgent()))
|
||||
if err != nil {
|
||||
scope.err(fmt.Errorf("failed to update object (Create for %v) managed fields: %v", scope.Kind, err), w, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
trace.Step("About to store object in database")
|
||||
result, err := finishRequest(timeout, func() (runtime.Object, error) {
|
||||
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(
|
||||
ctx,
|
||||
name,
|
||||
|
|
@ -169,6 +149,30 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope),
|
||||
options,
|
||||
)
|
||||
}
|
||||
result, err := finishRequest(timeout, func() (runtime.Object, error) {
|
||||
if scope.FieldManager != nil {
|
||||
liveObj, err := scope.Creater.New(scope.Kind)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new object (Create for %v): %v", scope.Kind, err)
|
||||
}
|
||||
obj = scope.FieldManager.UpdateNoErrors(liveObj, obj, managerOrUserAgent(options.FieldManager, req.UserAgent()))
|
||||
}
|
||||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
|
||||
if err := mutatingAdmission.Admit(ctx, admissionAttributes, scope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
result, err := requestFunc()
|
||||
// If the object wasn't committed to storage because it's serialized size was too large,
|
||||
// it is safe to remove managedFields (which can be large) and try again.
|
||||
if isTooLargeError(err) {
|
||||
if accessor, accessorErr := meta.Accessor(obj); accessorErr == nil {
|
||||
accessor.SetManagedFields(nil)
|
||||
result, err = requestFunc()
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
})
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
|
|
|
|||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
generated
vendored
|
|
@ -49,7 +49,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope *RequestSc
|
|||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun alpha feature is disabled"), w, req)
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
|
|||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun alpha feature is disabled"), w, req)
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/buildmanagerinfo.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/buildmanagerinfo.go
generated
vendored
|
|
@ -51,12 +51,12 @@ func (f *buildManagerInfoManager) Update(liveObj, newObj runtime.Object, managed
|
|||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *buildManagerInfoManager) Apply(liveObj runtime.Object, patch []byte, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
func (f *buildManagerInfoManager) Apply(liveObj, appliedObj runtime.Object, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
manager, err := f.buildManagerInfo(manager, metav1.ManagedFieldsOperationApply)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to build manager identifier: %v", err)
|
||||
}
|
||||
return f.fieldManager.Apply(liveObj, patch, managed, manager, force)
|
||||
return f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force)
|
||||
}
|
||||
|
||||
func (f *buildManagerInfoManager) buildManagerInfo(prefix string, operation metav1.ManagedFieldsOperationType) (string, error) {
|
||||
|
|
|
|||
21
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers.go
generated
vendored
21
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers.go
generated
vendored
|
|
@ -19,12 +19,11 @@ package fieldmanager
|
|||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
type capManagersManager struct {
|
||||
|
|
@ -58,8 +57,8 @@ func (f *capManagersManager) Update(liveObj, newObj runtime.Object, managed Mana
|
|||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *capManagersManager) Apply(liveObj runtime.Object, patch []byte, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
return f.fieldManager.Apply(liveObj, patch, managed, fieldManager, force)
|
||||
func (f *capManagersManager) Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
return f.fieldManager.Apply(liveObj, appliedObj, managed, fieldManager, force)
|
||||
}
|
||||
|
||||
// capUpdateManagers merges a number of the oldest update entries into versioned buckets,
|
||||
|
|
@ -78,15 +77,15 @@ func (f *capManagersManager) capUpdateManagers(managed Managed) (newManaged Mana
|
|||
|
||||
// If we have more than the maximum, sort the update entries by time, oldest first.
|
||||
sort.Slice(updaters, func(i, j int) bool {
|
||||
iTime, jTime, nTime := managed.Times()[updaters[i]], managed.Times()[updaters[j]], &metav1.Time{Time: time.Time{}}
|
||||
if iTime == nil {
|
||||
iTime = nTime
|
||||
iTime, jTime, iSeconds, jSeconds := managed.Times()[updaters[i]], managed.Times()[updaters[j]], int64(0), int64(0)
|
||||
if iTime != nil {
|
||||
iSeconds = iTime.Unix()
|
||||
}
|
||||
if jTime == nil {
|
||||
jTime = nTime
|
||||
if jTime != nil {
|
||||
jSeconds = jTime.Unix()
|
||||
}
|
||||
if !iTime.Equal(jTime) {
|
||||
return iTime.Before(jTime)
|
||||
if iSeconds != jSeconds {
|
||||
return iSeconds < jSeconds
|
||||
}
|
||||
return updaters[i] < updaters[j]
|
||||
})
|
||||
|
|
|
|||
77
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go
generated
vendored
77
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go
generated
vendored
|
|
@ -18,14 +18,17 @@ package fieldmanager
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
"k8s.io/klog"
|
||||
openapiproto "k8s.io/kube-openapi/pkg/util/proto"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
|
||||
|
|
@ -33,6 +36,12 @@ import (
|
|||
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
|
||||
const DefaultMaxUpdateManagers int = 10
|
||||
|
||||
// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
|
||||
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
|
||||
const DefaultTrackOnCreateProbability float32 = 1
|
||||
|
||||
var atMostEverySecond = internal.NewAtMostEvery(time.Second)
|
||||
|
||||
// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
|
||||
type Managed interface {
|
||||
// Fields gets the fieldpath.ManagedFields.
|
||||
|
|
@ -51,7 +60,7 @@ type Manager interface {
|
|||
|
||||
// Apply is used when server-side apply is called, as it merges the
|
||||
// object and updates the managed fields.
|
||||
Apply(liveObj runtime.Object, patch []byte, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
|
||||
Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
|
||||
}
|
||||
|
||||
// FieldManager updates the managed fields and merge applied
|
||||
|
|
@ -90,9 +99,10 @@ func NewDefaultCRDFieldManager(models openapiproto.Models, objectConverter runti
|
|||
// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
|
||||
func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind) *FieldManager {
|
||||
f = NewStripMetaManager(f)
|
||||
f = NewManagedFieldsUpdater(f)
|
||||
f = NewBuildManagerInfoManager(f, kind.GroupVersion())
|
||||
f = NewCapManagersManager(f, DefaultMaxUpdateManagers)
|
||||
f = NewSkipNonAppliedManager(f, objectCreater, kind)
|
||||
f = NewProbabilisticSkipNonAppliedManager(f, objectCreater, kind, DefaultTrackOnCreateProbability)
|
||||
return NewFieldManager(f)
|
||||
}
|
||||
|
||||
|
|
@ -102,20 +112,26 @@ func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind
|
|||
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
|
||||
// If the object doesn't have metadata, we should just return without trying to
|
||||
// set the managedFields at all, so creates/updates/patches will work normally.
|
||||
if _, err = meta.Accessor(newObj); err != nil {
|
||||
newAccessor, err := meta.Accessor(newObj)
|
||||
if err != nil {
|
||||
return newObj, nil
|
||||
}
|
||||
|
||||
// First try to decode the managed fields provided in the update,
|
||||
// This is necessary to allow directly updating managed fields.
|
||||
var managed Managed
|
||||
if managed, err = internal.DecodeObjectManagedFields(newObj); err != nil || len(managed.Fields()) == 0 {
|
||||
if isResetManagedFields(newAccessor.GetManagedFields()) {
|
||||
managed = internal.NewEmptyManaged()
|
||||
} else if managed, err = internal.DecodeObjectManagedFields(newAccessor.GetManagedFields()); err != nil || len(managed.Fields()) == 0 {
|
||||
liveAccessor, err := meta.Accessor(liveObj)
|
||||
if err != nil {
|
||||
return newObj, nil
|
||||
}
|
||||
// If the managed field is empty or we failed to decode it,
|
||||
// let's try the live object. This is to prevent clients who
|
||||
// don't understand managedFields from deleting it accidentally.
|
||||
managed, err = internal.DecodeObjectManagedFields(liveObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
|
||||
if managed, err = internal.DecodeObjectManagedFields(liveAccessor.GetManagedFields()); err != nil {
|
||||
managed = internal.NewEmptyManaged()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,23 +149,58 @@ func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (o
|
|||
return object, nil
|
||||
}
|
||||
|
||||
// UpdateNoErrors is the same as Update, but it will not return
|
||||
// errors. If an error happens, the object is returned with
|
||||
// managedFields cleared.
|
||||
func (f *FieldManager) UpdateNoErrors(liveObj, newObj runtime.Object, manager string) runtime.Object {
|
||||
obj, err := f.Update(liveObj, newObj, manager)
|
||||
if err != nil {
|
||||
atMostEverySecond.Do(func() {
|
||||
klog.Errorf("[SHOULD NOT HAPPEN] failed to update managedFields for %v: %v",
|
||||
newObj.GetObjectKind().GroupVersionKind(),
|
||||
err)
|
||||
})
|
||||
// Explicitly remove managedFields on failure, so that
|
||||
// we can't have garbage in it.
|
||||
internal.RemoveObjectManagedFields(newObj)
|
||||
return newObj
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// Returns true if the managedFields indicate that the user is trying to
|
||||
// reset the managedFields, i.e. if the list is non-nil but empty, or if
|
||||
// the list has one empty item.
|
||||
func isResetManagedFields(managedFields []metav1.ManagedFieldsEntry) bool {
|
||||
if len(managedFields) == 0 {
|
||||
return managedFields != nil
|
||||
}
|
||||
|
||||
if len(managedFields) == 1 {
|
||||
return reflect.DeepEqual(managedFields[0], metav1.ManagedFieldsEntry{})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Apply is used when server-side apply is called, as it merges the
|
||||
// object and updates the managed fields.
|
||||
func (f *FieldManager) Apply(liveObj runtime.Object, patch []byte, manager string, force bool) (object runtime.Object, err error) {
|
||||
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
|
||||
// If the object doesn't have metadata, apply isn't allowed.
|
||||
if _, err = meta.Accessor(liveObj); err != nil {
|
||||
accessor, err := meta.Accessor(liveObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
|
||||
// Decode the managed fields in the live object, since it isn't allowed in the patch.
|
||||
var managed Managed
|
||||
if managed, err = internal.DecodeObjectManagedFields(liveObj); err != nil {
|
||||
managed, err := internal.DecodeObjectManagedFields(accessor.GetManagedFields())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
|
||||
}
|
||||
|
||||
internal.RemoveObjectManagedFields(liveObj)
|
||||
|
||||
if object, managed, err = f.fieldManager.Apply(liveObj, patch, managed, manager, force); err != nil {
|
||||
if object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
60
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/atmostevery.go
generated
vendored
Normal file
60
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/atmostevery.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
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 internal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AtMostEvery will never run the method more than once every specified
|
||||
// duration.
|
||||
type AtMostEvery struct {
|
||||
delay time.Duration
|
||||
lastCall time.Time
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewAtMostEvery creates a new AtMostEvery, that will run the method at
|
||||
// most every given duration.
|
||||
func NewAtMostEvery(delay time.Duration) *AtMostEvery {
|
||||
return &AtMostEvery{
|
||||
delay: delay,
|
||||
}
|
||||
}
|
||||
|
||||
// updateLastCall returns true if the lastCall time has been updated,
|
||||
// false if it was too early.
|
||||
func (s *AtMostEvery) updateLastCall() bool {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
if time.Since(s.lastCall) < s.delay {
|
||||
return false
|
||||
}
|
||||
s.lastCall = time.Now()
|
||||
return true
|
||||
}
|
||||
|
||||
// Do will run the method if enough time has passed, and return true.
|
||||
// Otherwise, it does nothing and returns false.
|
||||
func (s *AtMostEvery) Do(fn func()) bool {
|
||||
if !s.updateLastCall() {
|
||||
return false
|
||||
}
|
||||
fn()
|
||||
return true
|
||||
}
|
||||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/conflict.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/conflict.go
generated
vendored
|
|
@ -25,8 +25,8 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/merge"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/merge"
|
||||
)
|
||||
|
||||
// NewConflictError returns an error including details on the requests apply conflicts
|
||||
|
|
|
|||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go
generated
vendored
|
|
@ -21,12 +21,12 @@ import (
|
|||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
// EmptyFields represents a set with no paths
|
||||
// It looks like metav1.Fields{Raw: []byte("{}")}
|
||||
var EmptyFields metav1.FieldsV1 = func() metav1.FieldsV1 {
|
||||
var EmptyFields = func() metav1.FieldsV1 {
|
||||
f, err := SetToFields(*fieldpath.NewSet())
|
||||
if err != nil {
|
||||
panic("should never happen")
|
||||
|
|
|
|||
2
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/gvkparser.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/gvkparser.go
generated
vendored
|
|
@ -22,7 +22,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/schemaconv"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"sigs.k8s.io/structured-merge-diff/typed"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/typed"
|
||||
)
|
||||
|
||||
// groupVersionKindExtensionKey is the key used to lookup the
|
||||
|
|
|
|||
45
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go
generated
vendored
45
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go
generated
vendored
|
|
@ -20,12 +20,11 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
// ManagedInterface groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
|
||||
|
|
@ -54,6 +53,11 @@ func (m *managedStruct) Times() map[string]*metav1.Time {
|
|||
return m.times
|
||||
}
|
||||
|
||||
// NewEmptyManaged creates an empty ManagedInterface.
|
||||
func NewEmptyManaged() ManagedInterface {
|
||||
return NewManaged(fieldpath.ManagedFields{}, map[string]*metav1.Time{})
|
||||
}
|
||||
|
||||
// NewManaged creates a ManagedInterface from a fieldpath.ManagedFields and the timestamps associated with each operation.
|
||||
func NewManaged(f fieldpath.ManagedFields, t map[string]*metav1.Time) ManagedInterface {
|
||||
return &managedStruct{
|
||||
|
|
@ -74,16 +78,8 @@ func RemoveObjectManagedFields(obj runtime.Object) {
|
|||
}
|
||||
|
||||
// DecodeObjectManagedFields extracts and converts the objects ManagedFields into a fieldpath.ManagedFields.
|
||||
func DecodeObjectManagedFields(from runtime.Object) (ManagedInterface, error) {
|
||||
if from == nil {
|
||||
return &managedStruct{}, nil
|
||||
}
|
||||
accessor, err := meta.Accessor(from)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't get accessor: %v", err))
|
||||
}
|
||||
|
||||
managed, err := decodeManagedFields(accessor.GetManagedFields())
|
||||
func DecodeObjectManagedFields(from []metav1.ManagedFieldsEntry) (ManagedInterface, error) {
|
||||
managed, err := decodeManagedFields(from)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert managed fields from API: %v", err)
|
||||
}
|
||||
|
|
@ -111,7 +107,16 @@ func EncodeObjectManagedFields(obj runtime.Object, managed ManagedInterface) err
|
|||
func decodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (managed managedStruct, err error) {
|
||||
managed.fields = make(fieldpath.ManagedFields, len(encodedManagedFields))
|
||||
managed.times = make(map[string]*metav1.Time, len(encodedManagedFields))
|
||||
for _, encodedVersionedSet := range encodedManagedFields {
|
||||
|
||||
for i, encodedVersionedSet := range encodedManagedFields {
|
||||
switch encodedVersionedSet.FieldsType {
|
||||
case "FieldsV1":
|
||||
// Valid case.
|
||||
case "":
|
||||
return managedStruct{}, fmt.Errorf("missing fieldsType in managed fields entry %d", i)
|
||||
default:
|
||||
return managedStruct{}, fmt.Errorf("invalid fieldsType %q in managed fields entry %d", encodedVersionedSet.FieldsType, i)
|
||||
}
|
||||
manager, err := BuildManagerIdentifier(&encodedVersionedSet)
|
||||
if err != nil {
|
||||
return managedStruct{}, fmt.Errorf("error decoding manager from %v: %v", encodedVersionedSet, err)
|
||||
|
|
@ -194,15 +199,15 @@ func sortEncodedManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry)
|
|||
return p.Operation < q.Operation
|
||||
}
|
||||
|
||||
ntime := &metav1.Time{Time: time.Time{}}
|
||||
if p.Time == nil {
|
||||
p.Time = ntime
|
||||
pSeconds, qSeconds := int64(0), int64(0)
|
||||
if p.Time != nil {
|
||||
pSeconds = p.Time.Unix()
|
||||
}
|
||||
if q.Time == nil {
|
||||
q.Time = ntime
|
||||
if q.Time != nil {
|
||||
qSeconds = q.Time.Unix()
|
||||
}
|
||||
if !p.Time.Equal(q.Time) {
|
||||
return p.Time.Before(q.Time)
|
||||
if pSeconds != qSeconds {
|
||||
return pSeconds < qSeconds
|
||||
}
|
||||
|
||||
if p.Manager != q.Manager {
|
||||
|
|
|
|||
8
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/pathelement.go
generated
vendored
8
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/pathelement.go
generated
vendored
|
|
@ -23,8 +23,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -110,7 +110,7 @@ func PathElementString(pe fieldpath.PathElement) (string, error) {
|
|||
case pe.Key != nil:
|
||||
kv := map[string]json.RawMessage{}
|
||||
for _, k := range *pe.Key {
|
||||
b, err := k.Value.ToJSON()
|
||||
b, err := value.ToJSON(k.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ func PathElementString(pe fieldpath.PathElement) (string, error) {
|
|||
}
|
||||
return Key + ":" + string(b), nil
|
||||
case pe.Value != nil:
|
||||
b, err := pe.Value.ToJSON()
|
||||
b, err := value.ToJSON(*pe.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
37
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/typeconverter.go
generated
vendored
37
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/typeconverter.go
generated
vendored
|
|
@ -23,8 +23,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"sigs.k8s.io/structured-merge-diff/typed"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/typed"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/value"
|
||||
)
|
||||
|
||||
// TypeConverter allows you to convert from runtime.Object to
|
||||
|
|
@ -49,11 +49,12 @@ var _ TypeConverter = DeducedTypeConverter{}
|
|||
|
||||
// ObjectToTyped converts an object into a TypedValue with a "deduced type".
|
||||
func (DeducedTypeConverter) ObjectToTyped(obj runtime.Object) (*typed.TypedValue, error) {
|
||||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch o := obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return typed.DeducedParseableType.FromUnstructured(o.UnstructuredContent())
|
||||
default:
|
||||
return typed.DeducedParseableType.FromStructured(obj)
|
||||
}
|
||||
return typed.DeducedParseableType.FromUnstructured(u)
|
||||
}
|
||||
|
||||
// TypedToObject transforms the typed value into a runtime.Object. That
|
||||
|
|
@ -80,29 +81,31 @@ func NewTypeConverter(models proto.Models, preserveUnknownFields bool) (TypeConv
|
|||
}
|
||||
|
||||
func (c *typeConverter) ObjectToTyped(obj runtime.Object) (*typed.TypedValue, error) {
|
||||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
t := c.parser.Type(gvk)
|
||||
if t == nil {
|
||||
return nil, newNoCorrespondingTypeError(gvk)
|
||||
}
|
||||
return t.FromUnstructured(u)
|
||||
switch o := obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return t.FromUnstructured(o.UnstructuredContent())
|
||||
default:
|
||||
return t.FromStructured(obj)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *typeConverter) TypedToObject(value *typed.TypedValue) (runtime.Object, error) {
|
||||
return valueToObject(value.AsValue())
|
||||
}
|
||||
|
||||
func valueToObject(value *value.Value) (runtime.Object, error) {
|
||||
vu := value.ToUnstructured(false)
|
||||
u, ok := vu.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert typed to unstructured: want map, got %T", vu)
|
||||
func valueToObject(val value.Value) (runtime.Object, error) {
|
||||
vu := val.Unstructured()
|
||||
switch o := vu.(type) {
|
||||
case map[string]interface{}:
|
||||
return &unstructured.Unstructured{Object: o}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("failed to convert value to unstructured for type %T", vu)
|
||||
}
|
||||
return &unstructured.Unstructured{Object: u}, nil
|
||||
}
|
||||
|
||||
type noCorrespondingTypeErr struct {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package internal
|
|||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/merge"
|
||||
"sigs.k8s.io/structured-merge-diff/typed"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/merge"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/typed"
|
||||
)
|
||||
|
||||
// versionConverter is an implementation of
|
||||
|
|
|
|||
82
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater.go
generated
vendored
Normal file
82
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/managedfieldsupdater.go
generated
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
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 fieldmanager
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
type managedFieldsUpdater struct {
|
||||
fieldManager Manager
|
||||
}
|
||||
|
||||
var _ Manager = &managedFieldsUpdater{}
|
||||
|
||||
// NewManagedFieldsUpdater is responsible for updating the managedfields
|
||||
// in the object, updating the time of the operation as necessary. For
|
||||
// updates, it uses a hard-coded manager to detect if things have
|
||||
// changed, and swaps back the correct manager after the operation is
|
||||
// done.
|
||||
func NewManagedFieldsUpdater(fieldManager Manager) Manager {
|
||||
return &managedFieldsUpdater{
|
||||
fieldManager: fieldManager,
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements Manager.
|
||||
func (f *managedFieldsUpdater) Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error) {
|
||||
self := "current-operation"
|
||||
object, managed, err := f.fieldManager.Update(liveObj, newObj, managed, self)
|
||||
if err != nil {
|
||||
return object, managed, err
|
||||
}
|
||||
|
||||
// If the current operation took any fields from anything, it means the object changed,
|
||||
// so update the timestamp of the managedFieldsEntry and merge with any previous updates from the same manager
|
||||
if vs, ok := managed.Fields()[self]; ok {
|
||||
delete(managed.Fields(), self)
|
||||
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if previous, ok := managed.Fields()[manager]; ok {
|
||||
managed.Fields()[manager] = fieldpath.NewVersionedSet(vs.Set().Union(previous.Set()), vs.APIVersion(), vs.Applied())
|
||||
} else {
|
||||
managed.Fields()[manager] = vs
|
||||
}
|
||||
}
|
||||
|
||||
return object, managed, nil
|
||||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *managedFieldsUpdater) Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
formerManaged := managed.Fields().Copy()
|
||||
object, managed, err := f.fieldManager.Apply(liveObj, appliedObj, managed, fieldManager, force)
|
||||
if err != nil {
|
||||
return object, managed, err
|
||||
}
|
||||
if object != nil || !managed.Fields().Equals(formerManaged) {
|
||||
managed.Times()[fieldManager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
}
|
||||
if object == nil {
|
||||
object = liveObj
|
||||
}
|
||||
return object, managed, nil
|
||||
}
|
||||
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/node.yaml
generated
vendored
4
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/node.yaml
generated
vendored
|
|
@ -7,10 +7,10 @@ metadata:
|
|||
volumes.kubernetes.io/controller-managed-attach-detach: "true"
|
||||
creationTimestamp: "2019-07-09T16:17:29Z"
|
||||
labels:
|
||||
beta.kubernetes.io/arch: amd64
|
||||
kubernetes.io/arch: amd64
|
||||
beta.kubernetes.io/fluentd-ds-ready: "true"
|
||||
beta.kubernetes.io/instance-type: n1-standard-4
|
||||
beta.kubernetes.io/os: linux
|
||||
kubernetes.io/os: linux
|
||||
cloud.google.com/gke-nodepool: default-pool
|
||||
cloud.google.com/gke-os-distribution: cos
|
||||
failure-domain.beta.kubernetes.io/region: us-central1
|
||||
|
|
|
|||
31
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go
generated
vendored
31
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go
generated
vendored
|
|
@ -18,7 +18,9 @@ package fieldmanager
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
|
@ -28,30 +30,53 @@ type skipNonAppliedManager struct {
|
|||
objectCreater runtime.ObjectCreater
|
||||
gvk schema.GroupVersionKind
|
||||
beforeApplyManagerName string
|
||||
probability float32
|
||||
}
|
||||
|
||||
var _ Manager = &skipNonAppliedManager{}
|
||||
|
||||
// NewSkipNonAppliedManager creates a new wrapped FieldManager that only starts tracking managers after the first apply.
|
||||
func NewSkipNonAppliedManager(fieldManager Manager, objectCreater runtime.ObjectCreater, gvk schema.GroupVersionKind) Manager {
|
||||
return NewProbabilisticSkipNonAppliedManager(fieldManager, objectCreater, gvk, 0.0)
|
||||
}
|
||||
|
||||
// NewProbabilisticSkipNonAppliedManager creates a new wrapped FieldManager that starts tracking managers after the first apply,
|
||||
// or starts tracking on create with p probability.
|
||||
func NewProbabilisticSkipNonAppliedManager(fieldManager Manager, objectCreater runtime.ObjectCreater, gvk schema.GroupVersionKind, p float32) Manager {
|
||||
return &skipNonAppliedManager{
|
||||
fieldManager: fieldManager,
|
||||
objectCreater: objectCreater,
|
||||
gvk: gvk,
|
||||
beforeApplyManagerName: "before-first-apply",
|
||||
probability: p,
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements Manager.
|
||||
func (f *skipNonAppliedManager) Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error) {
|
||||
if len(managed.Fields()) == 0 {
|
||||
accessor, err := meta.Accessor(liveObj)
|
||||
if err != nil {
|
||||
return newObj, managed, nil
|
||||
}
|
||||
|
||||
// If managed fields is empty, we need to determine whether to skip tracking managed fields.
|
||||
if len(managed.Fields()) == 0 {
|
||||
// Check if the operation is a create, by checking whether lastObj's UID is empty.
|
||||
// If the operation is create, P(tracking managed fields) = f.probability
|
||||
// If the operation is update, skip tracking managed fields, since we already know managed fields is empty.
|
||||
if len(accessor.GetUID()) == 0 {
|
||||
if f.probability <= rand.Float32() {
|
||||
return newObj, managed, nil
|
||||
}
|
||||
} else {
|
||||
return newObj, managed, nil
|
||||
}
|
||||
}
|
||||
return f.fieldManager.Update(liveObj, newObj, managed, manager)
|
||||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *skipNonAppliedManager) Apply(liveObj runtime.Object, patch []byte, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
func (f *skipNonAppliedManager) Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
if len(managed.Fields()) == 0 {
|
||||
emptyObj, err := f.objectCreater.New(f.gvk)
|
||||
if err != nil {
|
||||
|
|
@ -62,5 +87,5 @@ func (f *skipNonAppliedManager) Apply(liveObj runtime.Object, patch []byte, mana
|
|||
return nil, nil, fmt.Errorf("failed to create manager for existing fields: %v", err)
|
||||
}
|
||||
}
|
||||
return f.fieldManager.Apply(liveObj, patch, managed, fieldManager, force)
|
||||
return f.fieldManager.Apply(liveObj, appliedObj, managed, fieldManager, force)
|
||||
}
|
||||
|
|
|
|||
6
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/stripmeta.go
generated
vendored
6
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/stripmeta.go
generated
vendored
|
|
@ -20,7 +20,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
type stripMetaManager struct {
|
||||
|
|
@ -64,8 +64,8 @@ func (f *stripMetaManager) Update(liveObj, newObj runtime.Object, managed Manage
|
|||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *stripMetaManager) Apply(liveObj runtime.Object, patch []byte, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
newObj, managed, err := f.fieldManager.Apply(liveObj, patch, managed, manager, force)
|
||||
func (f *stripMetaManager) Apply(liveObj, appliedObj runtime.Object, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
newObj, managed, err := f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
56
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/structuredmerge.go
generated
vendored
56
vendor/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/structuredmerge.go
generated
vendored
|
|
@ -18,19 +18,15 @@ package fieldmanager
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
"k8s.io/klog"
|
||||
openapiproto "k8s.io/kube-openapi/pkg/util/proto"
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/merge"
|
||||
"sigs.k8s.io/yaml"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/merge"
|
||||
)
|
||||
|
||||
type structuredMergeManager struct {
|
||||
|
|
@ -99,59 +95,40 @@ func (f *structuredMergeManager) Update(liveObj, newObj runtime.Object, managed
|
|||
}
|
||||
newObjTyped, err := f.typeConverter.ObjectToTyped(newObjVersioned)
|
||||
if err != nil {
|
||||
// Return newObj and just by-pass fields update. This really shouldn't happen.
|
||||
klog.Errorf("[SHOULD NOT HAPPEN] failed to create typed new object: %v", err)
|
||||
return newObj, managed, nil
|
||||
return nil, nil, fmt.Errorf("failed to convert new object (%v) to smd typed: %v", newObjVersioned.GetObjectKind().GroupVersionKind(), err)
|
||||
}
|
||||
liveObjTyped, err := f.typeConverter.ObjectToTyped(liveObjVersioned)
|
||||
if err != nil {
|
||||
// Return newObj and just by-pass fields update. This really shouldn't happen.
|
||||
klog.Errorf("[SHOULD NOT HAPPEN] failed to create typed live object: %v", err)
|
||||
return newObj, managed, nil
|
||||
return nil, nil, fmt.Errorf("failed to convert live object (%v) to smd typed: %v", liveObjVersioned.GetObjectKind().GroupVersionKind(), err)
|
||||
}
|
||||
apiVersion := fieldpath.APIVersion(f.groupVersion.String())
|
||||
|
||||
// TODO(apelisse) use the first return value when unions are implemented
|
||||
self := "current-operation"
|
||||
_, managedFields, err := f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed.Fields(), self)
|
||||
_, managedFields, err := f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed.Fields(), manager)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to update ManagedFields: %v", err)
|
||||
}
|
||||
managed = internal.NewManaged(managedFields, managed.Times())
|
||||
|
||||
// If the current operation took any fields from anything, it means the object changed,
|
||||
// so update the timestamp of the managedFieldsEntry and merge with any previous updates from the same manager
|
||||
if vs, ok := managed.Fields()[self]; ok {
|
||||
delete(managed.Fields(), self)
|
||||
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if previous, ok := managed.Fields()[manager]; ok {
|
||||
managed.Fields()[manager] = fieldpath.NewVersionedSet(vs.Set().Union(previous.Set()), vs.APIVersion(), vs.Applied())
|
||||
} else {
|
||||
managed.Fields()[manager] = vs
|
||||
}
|
||||
}
|
||||
|
||||
return newObj, managed, nil
|
||||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *structuredMergeManager) Apply(liveObj runtime.Object, patch []byte, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
patchObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal(patch, &patchObj.Object); err != nil {
|
||||
return nil, nil, errors.NewBadRequest(fmt.Sprintf("error decoding YAML: %v", err))
|
||||
}
|
||||
|
||||
func (f *structuredMergeManager) Apply(liveObj, patchObj runtime.Object, managed Managed, manager string, force bool) (runtime.Object, Managed, error) {
|
||||
// Check that the patch object has the same version as the live object
|
||||
if patchObj.GetAPIVersion() != f.groupVersion.String() {
|
||||
if patchVersion := patchObj.GetObjectKind().GroupVersionKind().GroupVersion(); patchVersion != f.groupVersion {
|
||||
return nil, nil,
|
||||
errors.NewBadRequest(
|
||||
fmt.Sprintf("Incorrect version specified in apply patch. "+
|
||||
"Specified patch version: %s, expected: %s",
|
||||
patchObj.GetAPIVersion(), f.groupVersion.String()))
|
||||
patchVersion, f.groupVersion))
|
||||
}
|
||||
|
||||
if patchObj.GetManagedFields() != nil {
|
||||
patchObjMeta, err := meta.Accessor(patchObj)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("couldn't get accessor: %v", err)
|
||||
}
|
||||
if patchObjMeta.GetManagedFields() != nil {
|
||||
return nil, nil, errors.NewBadRequest(fmt.Sprintf("metadata.managedFields must be nil"))
|
||||
}
|
||||
|
||||
|
|
@ -179,8 +156,9 @@ func (f *structuredMergeManager) Apply(liveObj runtime.Object, patch []byte, man
|
|||
}
|
||||
managed = internal.NewManaged(managedFields, managed.Times())
|
||||
|
||||
// Update the time in the managedFieldsEntry for this operation
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if newObjTyped == nil {
|
||||
return nil, managed, nil
|
||||
}
|
||||
|
||||
newObj, err := f.typeConverter.TypedToObject(newObjTyped)
|
||||
if err != nil {
|
||||
|
|
|
|||
38
vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
generated
vendored
38
vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
generated
vendored
|
|
@ -28,6 +28,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
|
@ -48,6 +49,7 @@ import (
|
|||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -63,7 +65,7 @@ func PatchResource(r rest.Patcher, scope *RequestScope, admit admission.Interfac
|
|||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun alpha feature is disabled"), w, req)
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -321,9 +323,7 @@ func (p *jsonPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (r
|
|||
}
|
||||
|
||||
if p.fieldManager != nil {
|
||||
if objToUpdate, err = p.fieldManager.Update(currentObject, objToUpdate, managerOrUserAgent(p.options.FieldManager, p.userAgent)); err != nil {
|
||||
return nil, fmt.Errorf("failed to update object (json PATCH for %v) managed fields: %v", p.kind, err)
|
||||
}
|
||||
objToUpdate = p.fieldManager.UpdateNoErrors(currentObject, objToUpdate, managerOrUserAgent(p.options.FieldManager, p.userAgent))
|
||||
}
|
||||
return objToUpdate, nil
|
||||
}
|
||||
|
|
@ -406,9 +406,7 @@ func (p *smpPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (ru
|
|||
}
|
||||
|
||||
if p.fieldManager != nil {
|
||||
if newObj, err = p.fieldManager.Update(currentObject, newObj, managerOrUserAgent(p.options.FieldManager, p.userAgent)); err != nil {
|
||||
return nil, fmt.Errorf("failed to update object (smp PATCH for %v) managed fields: %v", p.kind, err)
|
||||
}
|
||||
newObj = p.fieldManager.UpdateNoErrors(currentObject, newObj, managerOrUserAgent(p.options.FieldManager, p.userAgent))
|
||||
}
|
||||
return newObj, nil
|
||||
}
|
||||
|
|
@ -433,7 +431,13 @@ func (p *applyPatcher) applyPatchToCurrentObject(obj runtime.Object) (runtime.Ob
|
|||
if p.fieldManager == nil {
|
||||
panic("FieldManager must be installed to run apply")
|
||||
}
|
||||
return p.fieldManager.Apply(obj, p.patch, p.options.FieldManager, force)
|
||||
|
||||
patchObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal(p.patch, &patchObj.Object); err != nil {
|
||||
return nil, errors.NewBadRequest(fmt.Sprintf("error decoding YAML: %v", err))
|
||||
}
|
||||
|
||||
return p.fieldManager.Apply(obj, patchObj, p.options.FieldManager, force)
|
||||
}
|
||||
|
||||
func (p *applyPatcher) createNewObject() (runtime.Object, error) {
|
||||
|
|
@ -573,12 +577,28 @@ func (p *patcher) patchResource(ctx context.Context, scope *RequestScope) (runti
|
|||
|
||||
wasCreated := false
|
||||
p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission)
|
||||
result, err := finishRequest(p.timeout, func() (runtime.Object, error) {
|
||||
requestFunc := func() (runtime.Object, error) {
|
||||
// Pass in UpdateOptions to override UpdateStrategy.AllowUpdateOnCreate
|
||||
options := patchToUpdateOptions(p.options)
|
||||
updateObject, created, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation, p.forceAllowCreate, options)
|
||||
wasCreated = created
|
||||
return updateObject, updateErr
|
||||
}
|
||||
result, err := finishRequest(p.timeout, func() (runtime.Object, error) {
|
||||
result, err := requestFunc()
|
||||
// If the object wasn't committed to storage because it's serialized size was too large,
|
||||
// it is safe to remove managedFields (which can be large) and try again.
|
||||
if isTooLargeError(err) && p.patchType != types.ApplyPatchType {
|
||||
if _, accessorErr := meta.Accessor(p.restPatcher.New()); accessorErr == nil {
|
||||
p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission, func(_ context.Context, obj, _ runtime.Object) (runtime.Object, error) {
|
||||
accessor, _ := meta.Accessor(obj)
|
||||
accessor.SetManagedFields(nil)
|
||||
return obj, nil
|
||||
})
|
||||
result, err = requestFunc()
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
})
|
||||
return result, wasCreated, err
|
||||
}
|
||||
|
|
|
|||
8
vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
generated
vendored
8
vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
generated
vendored
|
|
@ -74,7 +74,7 @@ func doTransformObject(ctx context.Context, obj runtime.Object, opts interface{}
|
|||
return asPartialObjectMetadataList(obj, target.GroupVersion())
|
||||
|
||||
case target.Kind == "Table":
|
||||
options, ok := opts.(*metav1beta1.TableOptions)
|
||||
options, ok := opts.(*metav1.TableOptions)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected TableOptions, got %T", opts)
|
||||
}
|
||||
|
|
@ -93,8 +93,8 @@ func optionsForTransform(mediaType negotiation.MediaTypeOptions, req *http.Reque
|
|||
switch target := mediaType.Convert; {
|
||||
case target == nil:
|
||||
case target.Kind == "Table" && (target.GroupVersion() == metav1beta1.SchemeGroupVersion || target.GroupVersion() == metav1.SchemeGroupVersion):
|
||||
opts := &metav1beta1.TableOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), metav1beta1.SchemeGroupVersion, opts); err != nil {
|
||||
opts := &metav1.TableOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), metav1.SchemeGroupVersion, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch errs := validation.ValidateTableOptions(opts); len(errs) {
|
||||
|
|
@ -159,7 +159,7 @@ func (e errNotAcceptable) Status() metav1.Status {
|
|||
}
|
||||
}
|
||||
|
||||
func asTable(ctx context.Context, result runtime.Object, opts *metav1beta1.TableOptions, scope *RequestScope, groupVersion schema.GroupVersion) (runtime.Object, error) {
|
||||
func asTable(ctx context.Context, result runtime.Object, opts *metav1.TableOptions, scope *RequestScope, groupVersion schema.GroupVersion) (runtime.Object, error) {
|
||||
switch groupVersion {
|
||||
case metav1beta1.SchemeGroupVersion, metav1.SchemeGroupVersion:
|
||||
default:
|
||||
|
|
|
|||
29
vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
generated
vendored
29
vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
generated
vendored
|
|
@ -25,8 +25,12 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
goruntime "runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
grpccodes "google.golang.org/grpc/codes"
|
||||
grpcstatus "google.golang.org/grpc/status"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -416,3 +420,28 @@ func parseTimeout(str string) time.Duration {
|
|||
func isDryRun(url *url.URL) bool {
|
||||
return len(url.Query()["dryRun"]) != 0
|
||||
}
|
||||
|
||||
type etcdError interface {
|
||||
Code() grpccodes.Code
|
||||
Error() string
|
||||
}
|
||||
|
||||
type grpcError interface {
|
||||
GRPCStatus() *grpcstatus.Status
|
||||
}
|
||||
|
||||
func isTooLargeError(err error) bool {
|
||||
if err != nil {
|
||||
if etcdErr, ok := err.(etcdError); ok {
|
||||
if etcdErr.Code() == grpccodes.InvalidArgument && etcdErr.Error() == "etcdserver: request is too large" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if grpcErr, ok := err.(grpcError); ok {
|
||||
if grpcErr.GRPCStatus().Code() == grpccodes.ResourceExhausted && strings.Contains(grpcErr.GRPCStatus().Message(), "trying to send message larger than max") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
30
vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
generated
vendored
30
vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
generated
vendored
|
|
@ -24,6 +24,7 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
|
|
@ -49,7 +50,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||
defer trace.LogIfLong(500 * time.Millisecond)
|
||||
|
||||
if isDryRun(req.URL) && !utilfeature.DefaultFeatureGate.Enabled(features.DryRun) {
|
||||
scope.err(errors.NewBadRequest("the dryRun alpha feature is disabled"), w, req)
|
||||
scope.err(errors.NewBadRequest("the dryRun feature is disabled"), w, req)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -124,15 +125,18 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||
|
||||
userInfo, _ := request.UserFrom(ctx)
|
||||
transformers := []rest.TransformFunc{}
|
||||
|
||||
// allows skipping managedFields update if the resulting object is too big
|
||||
shouldUpdateManagedFields := true
|
||||
if scope.FieldManager != nil {
|
||||
transformers = append(transformers, func(_ context.Context, newObj, liveObj runtime.Object) (runtime.Object, error) {
|
||||
obj, err := scope.FieldManager.Update(liveObj, newObj, managerOrUserAgent(options.FieldManager, req.UserAgent()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update object (Update for %v) managed fields: %v", scope.Kind, err)
|
||||
if shouldUpdateManagedFields {
|
||||
return scope.FieldManager.UpdateNoErrors(liveObj, newObj, managerOrUserAgent(options.FieldManager, req.UserAgent())), nil
|
||||
}
|
||||
return obj, nil
|
||||
return newObj, nil
|
||||
})
|
||||
}
|
||||
|
||||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
|
||||
transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) {
|
||||
isNotZeroObject, err := hasUID(oldObj)
|
||||
|
|
@ -149,7 +153,6 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||
}
|
||||
return newObj, nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
createAuthorizerAttributes := authorizer.AttributesRecord{
|
||||
|
|
@ -167,7 +170,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||
|
||||
trace.Step("About to store object in database")
|
||||
wasCreated := false
|
||||
result, err := finishRequest(timeout, func() (runtime.Object, error) {
|
||||
requestFunc := func() (runtime.Object, error) {
|
||||
obj, created, err := r.Update(
|
||||
ctx,
|
||||
name,
|
||||
|
|
@ -184,6 +187,19 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||
)
|
||||
wasCreated = created
|
||||
return obj, err
|
||||
}
|
||||
result, err := finishRequest(timeout, func() (runtime.Object, error) {
|
||||
result, err := requestFunc()
|
||||
// If the object wasn't committed to storage because it's serialized size was too large,
|
||||
// it is safe to remove managedFields (which can be large) and try again.
|
||||
if isTooLargeError(err) && scope.FieldManager != nil {
|
||||
if accessor, accessorErr := meta.Accessor(obj); accessorErr == nil {
|
||||
accessor.SetManagedFields(nil)
|
||||
shouldUpdateManagedFields = false
|
||||
result, err = requestFunc()
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
})
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
|
|
|
|||
19
vendor/k8s.io/apiserver/pkg/endpoints/handlers/watch.go
generated
vendored
19
vendor/k8s.io/apiserver/pkg/endpoints/handlers/watch.go
generated
vendored
|
|
@ -25,7 +25,6 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
|
@ -64,6 +63,8 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
|
|||
// serveWatch will serve a watch response.
|
||||
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
|
||||
func serveWatch(watcher watch.Interface, scope *RequestScope, mediaTypeOptions negotiation.MediaTypeOptions, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
|
||||
defer watcher.Stop()
|
||||
|
||||
options, err := optionsForTransform(mediaTypeOptions, req)
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
|
|
@ -125,7 +126,7 @@ func serveWatch(watcher watch.Interface, scope *RequestScope, mediaTypeOptions n
|
|||
// When we are transformed to a table, use the table options as the state for whether we
|
||||
// should print headers - on watch, we only want to print table headers on the first object
|
||||
// and omit them on subsequent events.
|
||||
if tableOptions, ok := options.(*metav1beta1.TableOptions); ok {
|
||||
if tableOptions, ok := options.(*metav1.TableOptions); ok {
|
||||
tableOptions.NoHeaders = true
|
||||
}
|
||||
return result
|
||||
|
|
@ -173,13 +174,6 @@ func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
cn, ok := w.(http.CloseNotifier)
|
||||
if !ok {
|
||||
err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
|
||||
utilruntime.HandleError(err)
|
||||
s.Scope.err(errors.NewInternalError(err), w, req)
|
||||
return
|
||||
}
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
|
||||
|
|
@ -201,7 +195,6 @@ func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
// ensure the connection times out
|
||||
timeoutCh, cleanup := s.TimeoutFactory.TimeoutCh()
|
||||
defer cleanup()
|
||||
defer s.Watching.Stop()
|
||||
|
||||
// begin the stream
|
||||
w.Header().Set("Content-Type", s.MediaType)
|
||||
|
|
@ -214,9 +207,11 @@ func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
outEvent := &metav1.WatchEvent{}
|
||||
buf := &bytes.Buffer{}
|
||||
ch := s.Watching.ResultChan()
|
||||
done := req.Context().Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-cn.CloseNotify():
|
||||
case <-done:
|
||||
return
|
||||
case <-timeoutCh:
|
||||
return
|
||||
|
|
@ -286,8 +281,6 @@ func (s *WatchServer) HandleWS(ws *websocket.Conn) {
|
|||
streamBuf := &bytes.Buffer{}
|
||||
ch := s.Watching.ResultChan()
|
||||
|
||||
defer s.Watching.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
|
|
|
|||
15
vendor/k8s.io/apiserver/pkg/endpoints/installer.go
generated
vendored
15
vendor/k8s.io/apiserver/pkg/endpoints/installer.go
generated
vendored
|
|
@ -291,12 +291,17 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
|
||||
var versionedDeleteOptions runtime.Object
|
||||
var versionedDeleterObject interface{}
|
||||
deleteReturnsDeletedObject := false
|
||||
if isGracefulDeleter {
|
||||
versionedDeleteOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind("DeleteOptions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionedDeleterObject = indirectArbitraryPointer(versionedDeleteOptions)
|
||||
|
||||
if mayReturnFullObjectDeleter, ok := storage.(rest.MayReturnFullObjectDeleter); ok {
|
||||
deleteReturnsDeletedObject = mayReturnFullObjectDeleter.DeleteReturnsDeletedObject()
|
||||
}
|
||||
}
|
||||
|
||||
versionedStatusPtr, err := a.group.Creater.New(optionsExternalVersion.WithKind("Status"))
|
||||
|
|
@ -769,15 +774,19 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
if isSubresource {
|
||||
doc = "delete " + subresource + " of" + article + kind
|
||||
}
|
||||
deleteReturnType := versionedStatus
|
||||
if deleteReturnsDeletedObject {
|
||||
deleteReturnType = producedObject
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulDeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
|
||||
route := ws.DELETE(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
Operation("delete"+namespaced+kind+strings.Title(subresource)+operationSuffix).
|
||||
Produces(append(storageMeta.ProducesMIMETypes(action.Verb), mediaTypes...)...).
|
||||
Writes(versionedStatus).
|
||||
Returns(http.StatusOK, "OK", versionedStatus).
|
||||
Returns(http.StatusAccepted, "Accepted", versionedStatus)
|
||||
Writes(deleteReturnType).
|
||||
Returns(http.StatusOK, "OK", deleteReturnType).
|
||||
Returns(http.StatusAccepted, "Accepted", deleteReturnType)
|
||||
if isGracefulDeleter {
|
||||
route.Reads(versionedDeleterObject)
|
||||
route.ParameterNamed("body").Required(false)
|
||||
|
|
|
|||
129
vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go
generated
vendored
129
vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go
generated
vendored
|
|
@ -27,8 +27,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
restful "github.com/emicklei/go-restful"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilsets "k8s.io/apimachinery/pkg/util/sets"
|
||||
|
|
@ -48,6 +47,8 @@ type resettableCollector interface {
|
|||
|
||||
const (
|
||||
APIServerComponent string = "apiserver"
|
||||
OtherContentType string = "other"
|
||||
OtherRequestMethod string = "other"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
@ -64,23 +65,14 @@ var (
|
|||
requestCounter = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_request_total",
|
||||
Help: "Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, client, and HTTP response contentType and code.",
|
||||
Help: "Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response contentType and code.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
// The label_name contentType doesn't follow the label_name convention defined here:
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/instrumentation.md
|
||||
// But changing it would break backwards compatibility. Future label_names
|
||||
// should be all lowercase and separated by underscores.
|
||||
[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component", "client", "contentType", "code"},
|
||||
)
|
||||
deprecatedRequestCounter = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_request_count",
|
||||
Help: "Counter of apiserver requests broken out for each verb, group, version, resource, scope, component, client, and HTTP response contentType and code.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
DeprecatedVersion: "1.14.0",
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component", "client", "contentType", "code"},
|
||||
[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component", "contentType", "code"},
|
||||
)
|
||||
longRunningRequestGauge = compbasemetrics.NewGaugeVec(
|
||||
&compbasemetrics.GaugeOpts{
|
||||
|
|
@ -103,29 +95,6 @@ var (
|
|||
},
|
||||
[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
deprecatedRequestLatencies = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_request_latencies",
|
||||
Help: "Response latency distribution in microseconds for each verb, group, version, resource, subresource, scope and component.",
|
||||
// Use buckets ranging from 125 ms to 8 seconds.
|
||||
Buckets: compbasemetrics.ExponentialBuckets(125000, 2.0, 7),
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
DeprecatedVersion: "1.14.0",
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
deprecatedRequestLatenciesSummary = compbasemetrics.NewSummaryVec(
|
||||
&compbasemetrics.SummaryOpts{
|
||||
Name: "apiserver_request_latencies_summary",
|
||||
Help: "Response latency summary in microseconds for each verb, group, version, resource, subresource, scope and component.",
|
||||
// Make the sliding window of 5h.
|
||||
// TODO: The value for this should be based on our SLI definition (medium term).
|
||||
MaxAge: 5 * time.Hour,
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
DeprecatedVersion: "1.14.0",
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
responseSizes = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_response_sizes",
|
||||
|
|
@ -145,15 +114,6 @@ var (
|
|||
},
|
||||
[]string{"requestKind"},
|
||||
)
|
||||
DeprecatedDroppedRequests = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_dropped_requests",
|
||||
Help: "Number of requests dropped with 'Try again later' response",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
DeprecatedVersion: "1.14.0",
|
||||
},
|
||||
[]string{"requestKind"},
|
||||
)
|
||||
// RegisteredWatchers is a number of currently registered watchers splitted by resource.
|
||||
RegisteredWatchers = compbasemetrics.NewGaugeVec(
|
||||
&compbasemetrics.GaugeOpts{
|
||||
|
|
@ -203,20 +163,47 @@ var (
|
|||
|
||||
metrics = []resettableCollector{
|
||||
requestCounter,
|
||||
deprecatedRequestCounter,
|
||||
longRunningRequestGauge,
|
||||
requestLatencies,
|
||||
deprecatedRequestLatencies,
|
||||
deprecatedRequestLatenciesSummary,
|
||||
responseSizes,
|
||||
DroppedRequests,
|
||||
DeprecatedDroppedRequests,
|
||||
RegisteredWatchers,
|
||||
WatchEvents,
|
||||
WatchEventsSizes,
|
||||
currentInflightRequests,
|
||||
requestTerminationsTotal,
|
||||
}
|
||||
|
||||
// these are the known (e.g. whitelisted/known) content types which we will report for
|
||||
// request metrics. Any other RFC compliant content types will be aggregated under 'unknown'
|
||||
knownMetricContentTypes = utilsets.NewString(
|
||||
"application/apply-patch+yaml",
|
||||
"application/json",
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json",
|
||||
"application/vnd.kubernetes.protobuf",
|
||||
"application/vnd.kubernetes.protobuf;stream=watch",
|
||||
"application/yaml",
|
||||
"text/plain",
|
||||
"text/plain;charset=utf-8")
|
||||
// these are the valid request methods which we report in our metrics. Any other request methods
|
||||
// will be aggregated under 'unknown'
|
||||
validRequestMethods = utilsets.NewString(
|
||||
"APPLY",
|
||||
"CONNECT",
|
||||
"CREATE",
|
||||
"DELETE",
|
||||
"DELETECOLLECTION",
|
||||
"GET",
|
||||
"LIST",
|
||||
"PATCH",
|
||||
"POST",
|
||||
"PROXY",
|
||||
"PUT",
|
||||
"UPDATE",
|
||||
"WATCH",
|
||||
"WATCHLIST")
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -264,6 +251,10 @@ func RecordRequestTermination(req *http.Request, requestInfo *request.RequestInf
|
|||
// translated to RequestInfo).
|
||||
// However, we need to tweak it e.g. to differentiate GET from LIST.
|
||||
verb := canonicalVerb(strings.ToUpper(req.Method), scope)
|
||||
// set verbs to a bounded set of known and expected verbs
|
||||
if !validRequestMethods.Has(verb) {
|
||||
verb = OtherRequestMethod
|
||||
}
|
||||
if requestInfo.IsResourceRequest {
|
||||
requestTerminationsTotal.WithLabelValues(cleanVerb(verb, req), requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, component, codeToString(code)).Inc()
|
||||
} else {
|
||||
|
|
@ -300,15 +291,10 @@ func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, comp
|
|||
func MonitorRequest(req *http.Request, verb, group, version, resource, subresource, scope, component, contentType string, httpCode, respSize int, elapsed time.Duration) {
|
||||
reportedVerb := cleanVerb(verb, req)
|
||||
dryRun := cleanDryRun(req.URL)
|
||||
// blank out client string here, in order to avoid cardinality issues
|
||||
client := ""
|
||||
elapsedMicroseconds := float64(elapsed / time.Microsecond)
|
||||
elapsedSeconds := elapsed.Seconds()
|
||||
requestCounter.WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component, client, contentType, codeToString(httpCode)).Inc()
|
||||
deprecatedRequestCounter.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component, client, contentType, codeToString(httpCode)).Inc()
|
||||
cleanContentType := cleanContentType(contentType)
|
||||
requestCounter.WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component, cleanContentType, codeToString(httpCode)).Inc()
|
||||
requestLatencies.WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component).Observe(elapsedSeconds)
|
||||
deprecatedRequestLatencies.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(elapsedMicroseconds)
|
||||
deprecatedRequestLatenciesSummary.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(elapsedMicroseconds)
|
||||
// We are only interested in response sizes of read requests.
|
||||
if verb == "GET" || verb == "LIST" {
|
||||
responseSizes.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(float64(respSize))
|
||||
|
|
@ -362,6 +348,19 @@ func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope, c
|
|||
}
|
||||
}
|
||||
|
||||
// cleanContentType binds the contentType (for metrics related purposes) to a
|
||||
// bounded set of known/expected content-types.
|
||||
func cleanContentType(contentType string) string {
|
||||
normalizedContentType := strings.ToLower(contentType)
|
||||
if strings.HasSuffix(contentType, " stream=watch") || strings.HasSuffix(contentType, " charset=utf-8") {
|
||||
normalizedContentType = strings.ReplaceAll(contentType, " ", "")
|
||||
}
|
||||
if knownMetricContentTypes.Has(normalizedContentType) {
|
||||
return normalizedContentType
|
||||
}
|
||||
return OtherContentType
|
||||
}
|
||||
|
||||
// CleanScope returns the scope of the request.
|
||||
func CleanScope(requestInfo *request.RequestInfo) string {
|
||||
if requestInfo.Namespace != "" {
|
||||
|
|
@ -406,7 +405,10 @@ func cleanVerb(verb string, request *http.Request) string {
|
|||
if verb == "PATCH" && request.Header.Get("Content-Type") == string(types.ApplyPatchType) && utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
|
||||
reportedVerb = "APPLY"
|
||||
}
|
||||
return reportedVerb
|
||||
if validRequestMethods.Has(reportedVerb) {
|
||||
return reportedVerb
|
||||
}
|
||||
return OtherRequestMethod
|
||||
}
|
||||
|
||||
func cleanDryRun(u *url.URL) string {
|
||||
|
|
@ -425,19 +427,6 @@ func cleanDryRun(u *url.URL) string {
|
|||
return strings.Join(utilsets.NewString(dryRun...).List(), ",")
|
||||
}
|
||||
|
||||
func cleanUserAgent(ua string) string {
|
||||
// We collapse all "web browser"-type user agents into one "browser" to reduce metric cardinality.
|
||||
if strings.HasPrefix(ua, "Mozilla/") {
|
||||
return "Browser"
|
||||
}
|
||||
// If an old "kubectl.exe" has passed us its full path, we discard the path portion.
|
||||
if kubectlExeRegexp.MatchString(ua) {
|
||||
// avoid an allocation
|
||||
ua = kubectlExeRegexp.ReplaceAllString(ua, "$1")
|
||||
}
|
||||
return ua
|
||||
}
|
||||
|
||||
// ResponseWriterDelegator interface wraps http.ResponseWriter to additionally record content-length, status-code, etc.
|
||||
type ResponseWriterDelegator struct {
|
||||
http.ResponseWriter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue