rebase: update kubernetes to 1.26.1

update kubernetes and its dependencies
to v1.26.1

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2023-02-01 18:06:36 +01:00
committed by mergify[bot]
parent e9e33fb851
commit 9c8de9471e
937 changed files with 75539 additions and 33050 deletions

View File

@ -36,11 +36,11 @@ type mutatingWebhookConfigurationManager struct {
configuration *atomic.Value
lister admissionregistrationlisters.MutatingWebhookConfigurationLister
hasSynced func() bool
// initialConfigurationSynced stores a boolean value, which tracks if
// initialConfigurationSynced tracks if
// the existing webhook configs have been synced (honored) by the
// manager at startup-- the informer has synced and either has no items
// or has finished executing updateConfiguration() once.
initialConfigurationSynced *atomic.Value
initialConfigurationSynced *atomic.Bool
}
var _ generic.Source = &mutatingWebhookConfigurationManager{}
@ -51,7 +51,7 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g
configuration: &atomic.Value{},
lister: informer.Lister(),
hasSynced: informer.Informer().HasSynced,
initialConfigurationSynced: &atomic.Value{},
initialConfigurationSynced: &atomic.Bool{},
}
// Start with an empty list
@ -80,7 +80,7 @@ func (m *mutatingWebhookConfigurationManager) HasSynced() bool {
if !m.hasSynced() {
return false
}
if m.initialConfigurationSynced.Load().(bool) {
if m.initialConfigurationSynced.Load() {
// the informer has synced and configuration has been updated
return true
}

View File

@ -36,11 +36,11 @@ type validatingWebhookConfigurationManager struct {
configuration *atomic.Value
lister admissionregistrationlisters.ValidatingWebhookConfigurationLister
hasSynced func() bool
// initialConfigurationSynced stores a boolean value, which tracks if
// initialConfigurationSynced tracks if
// the existing webhook configs have been synced (honored) by the
// manager at startup-- the informer has synced and either has no items
// or has finished executing updateConfiguration() once.
initialConfigurationSynced *atomic.Value
initialConfigurationSynced *atomic.Bool
}
var _ generic.Source = &validatingWebhookConfigurationManager{}
@ -51,7 +51,7 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory)
configuration: &atomic.Value{},
lister: informer.Lister(),
hasSynced: informer.Informer().HasSynced,
initialConfigurationSynced: &atomic.Value{},
initialConfigurationSynced: &atomic.Bool{},
}
// Start with an empty list
@ -80,7 +80,7 @@ func (v *validatingWebhookConfigurationManager) HasSynced() bool {
if !v.hasSynced() {
return false
}
if v.initialConfigurationSynced.Load().(bool) {
if v.initialConfigurationSynced.Load() {
// the informer has synced and configuration has been updated
return true
}

View File

@ -19,6 +19,7 @@ package initializer
import (
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
@ -26,6 +27,7 @@ import (
type pluginInitializer struct {
externalClient kubernetes.Interface
dynamicClient dynamic.Interface
externalInformers informers.SharedInformerFactory
authorizer authorizer.Authorizer
featureGates featuregate.FeatureGate
@ -37,6 +39,7 @@ type pluginInitializer struct {
// during compilation when they update a level.
func New(
extClientset kubernetes.Interface,
dynamicClient dynamic.Interface,
extInformers informers.SharedInformerFactory,
authz authorizer.Authorizer,
featureGates featuregate.FeatureGate,
@ -44,6 +47,7 @@ func New(
) pluginInitializer {
return pluginInitializer{
externalClient: extClientset,
dynamicClient: dynamicClient,
externalInformers: extInformers,
authorizer: authz,
featureGates: featureGates,
@ -68,6 +72,10 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
wants.SetExternalKubeClientSet(i.externalClient)
}
if wants, ok := plugin.(WantsDynamicClient); ok {
wants.SetDynamicClient(i.dynamicClient)
}
if wants, ok := plugin.(WantsExternalKubeInformerFactory); ok {
wants.SetExternalKubeInformerFactory(i.externalInformers)
}

View File

@ -17,9 +17,11 @@ limitations under the License.
package initializer
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
quota "k8s.io/apiserver/pkg/quota/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
@ -68,3 +70,14 @@ type WantsFeatures interface {
InspectFeatureGates(featuregate.FeatureGate)
admission.InitializationValidator
}
type WantsDynamicClient interface {
SetDynamicClient(dynamic.Interface)
admission.InitializationValidator
}
// WantsRESTMapper defines a function which sets RESTMapper for admission plugins that need it.
type WantsRESTMapper interface {
SetRESTMapper(meta.RESTMapper)
admission.InitializationValidator
}

View File

@ -22,12 +22,19 @@ import (
"k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/rest"
)
// WebhookAccessor provides a common interface to both mutating and validating webhook types.
type WebhookAccessor interface {
// This accessor provides the methods needed to support matching against webhook
// predicates
namespace.NamespaceSelectorProvider
object.ObjectSelectorProvider
// GetUID gets a string that uniquely identifies the webhook.
GetUID() string
@ -36,10 +43,6 @@ type WebhookAccessor interface {
// GetRESTClient gets the webhook client
GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error)
// GetParsedNamespaceSelector gets the webhook NamespaceSelector field.
GetParsedNamespaceSelector() (labels.Selector, error)
// GetParsedObjectSelector gets the webhook ObjectSelector field.
GetParsedObjectSelector() (labels.Selector, error)
// GetName gets the webhook Name field. Note that the name is scoped to the webhook
// configuration and does not provide a globally unique identity, if a unique identity is

View File

@ -30,9 +30,9 @@ import (
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/admission/plugin/webhook"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
"k8s.io/apiserver/pkg/admission/plugin/webhook/object"
"k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/rules"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
@ -219,7 +219,7 @@ func (a *attrWithResourceOverride) GetResource() schema.GroupVersionResource { r
// Dispatch is called by the downstream Validate or Admit methods.
func (a *Webhook) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
if rules.IsWebhookConfigurationResource(attr) {
if rules.IsExemptAdmissionConfigurationResource(attr) {
return nil
}
if !a.WaitForReady() {

View File

@ -24,6 +24,7 @@ import (
"time"
jsonpatch "github.com/evanphx/json-patch"
"go.opentelemetry.io/otel/attribute"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/klog/v2"
@ -46,7 +47,7 @@ import (
endpointsrequest "k8s.io/apiserver/pkg/endpoints/request"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/pkg/warning"
utiltrace "k8s.io/utils/trace"
"k8s.io/component-base/tracing"
)
const (
@ -233,14 +234,14 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
if err != nil {
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("could not get REST client: %w", err), Status: apierrors.NewBadRequest("error getting REST client")}
}
trace := utiltrace.New("Call mutating webhook",
utiltrace.Field{"configuration", configurationName},
utiltrace.Field{"webhook", h.Name},
utiltrace.Field{"resource", attr.GetResource()},
utiltrace.Field{"subresource", attr.GetSubresource()},
utiltrace.Field{"operation", attr.GetOperation()},
utiltrace.Field{"UID", uid})
defer trace.LogIfLong(500 * time.Millisecond)
ctx, span := tracing.Start(ctx, "Call mutating webhook",
attribute.String("configuration", configurationName),
attribute.String("webhook", h.Name),
attribute.Stringer("resource", attr.GetResource()),
attribute.String("subresource", attr.GetSubresource()),
attribute.String("operation", string(attr.GetOperation())),
attribute.String("UID", string(uid)))
defer span.End(500 * time.Millisecond)
// if the webhook has a specific timeout, wrap the context to apply it
if h.TimeoutSeconds != nil {
@ -279,7 +280,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
}
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("failed to call webhook: %w", err), Status: status}
}
trace.Step("Request completed")
span.AddEvent("Request completed")
result, err := webhookrequest.VerifyAdmissionResponse(uid, true, response)
if err != nil {
@ -353,7 +354,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
}
changed = !apiequality.Semantic.DeepEqual(attr.VersionedObject, newVersionedObject)
trace.Step("Patch applied")
span.AddEvent("Patch applied")
annotator.addPatchAnnotation(patchObj, result.PatchType)
attr.Dirty = true
attr.VersionedObject = newVersionedObject

View File

@ -17,4 +17,4 @@ limitations under the License.
// Package namespace defines the utilities that are used by the webhook
// plugin to decide if a webhook should be applied to an object based on its
// namespace.
package namespace // import "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
package namespace // import "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"

View File

@ -26,11 +26,15 @@ import (
"k8s.io/apimachinery/pkg/labels"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/webhook"
clientset "k8s.io/client-go/kubernetes"
corelisters "k8s.io/client-go/listers/core/v1"
)
type NamespaceSelectorProvider interface {
// GetNamespaceSelector gets the webhook NamespaceSelector field.
GetParsedNamespaceSelector() (labels.Selector, error)
}
// Matcher decides if a request is exempted by the NamespaceSelector of a
// webhook configuration.
type Matcher struct {
@ -87,7 +91,7 @@ func (m *Matcher) GetNamespaceLabels(attr admission.Attributes) (map[string]stri
// MatchNamespaceSelector decideds whether the request matches the
// namespaceSelctor of the webhook. Only when they match, the webhook is called.
func (m *Matcher) MatchNamespaceSelector(h webhook.WebhookAccessor, attr admission.Attributes) (bool, *apierrors.StatusError) {
func (m *Matcher) MatchNamespaceSelector(p NamespaceSelectorProvider, attr admission.Attributes) (bool, *apierrors.StatusError) {
namespaceName := attr.GetNamespace()
if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" {
// If the request is about a cluster scoped resource, and it is not a
@ -96,7 +100,7 @@ func (m *Matcher) MatchNamespaceSelector(h webhook.WebhookAccessor, attr admissi
// Also update the comment in types.go
return true, nil
}
selector, err := h.GetParsedNamespaceSelector()
selector, err := p.GetParsedNamespaceSelector()
if err != nil {
return false, apierrors.NewInternalError(err)
}

View File

@ -17,4 +17,4 @@ limitations under the License.
// Package object defines the utilities that are used by the webhook plugin to
// decide if a webhook should run, as long as either the old object or the new
// object has labels matching the webhook config's objectSelector.
package object // import "k8s.io/apiserver/pkg/admission/plugin/webhook/object"
package object // import "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"

View File

@ -22,10 +22,14 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/webhook"
"k8s.io/klog/v2"
)
type ObjectSelectorProvider interface {
// GetObjectSelector gets the webhook ObjectSelector field.
GetParsedObjectSelector() (labels.Selector, error)
}
// Matcher decides if a request selected by the ObjectSelector.
type Matcher struct {
}
@ -45,8 +49,8 @@ func matchObject(obj runtime.Object, selector labels.Selector) bool {
// MatchObjectSelector decideds whether the request matches the ObjectSelector
// of the webhook. Only when they match, the webhook is called.
func (m *Matcher) MatchObjectSelector(h webhook.WebhookAccessor, attr admission.Attributes) (bool, *apierrors.StatusError) {
selector, err := h.GetParsedObjectSelector()
func (m *Matcher) MatchObjectSelector(p ObjectSelectorProvider, attr admission.Attributes) (bool, *apierrors.StatusError) {
selector, err := p.GetParsedObjectSelector()
if err != nil {
return false, apierrors.NewInternalError(err)
}

View File

@ -116,12 +116,12 @@ func (r *Matcher) resource() bool {
return false
}
// IsWebhookConfigurationResource determines if an admission.Attributes object is describing
// the admission of a ValidatingWebhookConfiguration or a MutatingWebhookConfiguration
func IsWebhookConfigurationResource(attr admission.Attributes) bool {
// IsExemptAdmissionConfigurationResource determines if an admission.Attributes object is describing
// the admission of a ValidatingWebhookConfiguration or a MutatingWebhookConfiguration or a ValidatingAdmissionPolicy or a ValidatingAdmissionPolicyBinding
func IsExemptAdmissionConfigurationResource(attr admission.Attributes) bool {
gvk := attr.GetKind()
if gvk.Group == "admissionregistration.k8s.io" {
if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" {
if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" || gvk.Kind == "ValidatingAdmissionPolicy" || gvk.Kind == "ValidatingAdmissionPolicyBinding" {
return true
}
}

View File

@ -85,7 +85,7 @@ func (ps *Plugins) Register(name string, plugin Factory) {
ps.registry[name] = plugin
}
// getPlugin creates an instance of the named plugin. It returns `false` if the
// getPlugin creates an instance of the named plugin. It returns `false` if
// the name is not known. The error is returned only when the named provider was
// known but failed to initialize. The config parameter specifies the io.Reader
// handler of the configuration file for the cloud provider, or nil for no configuration.

View File

@ -20,6 +20,7 @@ import (
"context"
"sync"
"k8s.io/apimachinery/pkg/types"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/klog/v2"
@ -28,38 +29,31 @@ import (
// The key type is unexported to prevent collisions
type key int
const (
// auditAnnotationsKey is the context key for the audit annotations.
// TODO: consolidate all audit info under the AuditContext, rather than storing 3 separate keys.
auditAnnotationsKey key = iota
// auditKey is the context key for storing the audit context that is being
// captured and the evaluated policy that applies to the given request.
const auditKey key = iota
// auditKey is the context key for storing the audit event that is being
// captured and the evaluated policy that applies to the given request.
auditKey
// AuditContext holds the information for constructing the audit events for the current request.
type AuditContext struct {
// RequestAuditConfig is the audit configuration that applies to the request
RequestAuditConfig RequestAuditConfig
// auditAnnotationsMutexKey is the context key for the audit annotations mutex.
auditAnnotationsMutexKey
)
// Event is the audit Event object that is being captured to be written in
// the API audit log. It is set to nil when the request is not being audited.
Event *auditinternal.Event
// annotations = *[]annotation instead of a map to preserve order of insertions
type annotation struct {
key, value string
// annotations holds audit annotations that are recorded before the event has been initialized.
// This is represented as a slice rather than a map to preserve order.
annotations []annotation
// annotationMutex guards annotations AND event.Annotations
annotationMutex sync.Mutex
// auditID is the Audit ID associated with this request.
auditID types.UID
}
// WithAuditAnnotations returns a new context that can store audit annotations
// via the AddAuditAnnotation function. This function is meant to be called from
// an early request handler to allow all later layers to set audit annotations.
// This is required to support flows where handlers that come before WithAudit
// (such as WithAuthentication) wish to set audit annotations.
func WithAuditAnnotations(parent context.Context) context.Context {
// this should never really happen, but prevent double registration of this slice
if _, ok := parent.Value(auditAnnotationsKey).(*[]annotation); ok {
return parent
}
parent = withAuditAnnotationsMutex(parent)
var annotations []annotation // avoid allocations until we actually need it
return genericapirequest.WithValue(parent, auditAnnotationsKey, &annotations)
type annotation struct {
key, value string
}
// AddAuditAnnotation sets the audit annotation for the given key, value pair.
@ -70,102 +64,79 @@ func WithAuditAnnotations(parent context.Context) context.Context {
// Handlers that are unaware of their position in the overall request flow should
// prefer AddAuditAnnotation over LogAnnotation to avoid dropping annotations.
func AddAuditAnnotation(ctx context.Context, key, value string) {
mutex, ok := auditAnnotationsMutex(ctx)
if !ok {
ac := AuditContextFrom(ctx)
if ac == nil {
// auditing is not enabled
return
}
mutex.Lock()
defer mutex.Unlock()
ac.annotationMutex.Lock()
defer ac.annotationMutex.Unlock()
ae := AuditEventFrom(ctx)
var ctxAnnotations *[]annotation
if ae == nil {
ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
}
addAuditAnnotationLocked(ae, ctxAnnotations, key, value)
addAuditAnnotationLocked(ac, key, value)
}
// AddAuditAnnotations is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
// keysAndValues are the key-value pairs to add, and must have an even number of items.
func AddAuditAnnotations(ctx context.Context, keysAndValues ...string) {
mutex, ok := auditAnnotationsMutex(ctx)
if !ok {
ac := AuditContextFrom(ctx)
if ac == nil {
// auditing is not enabled
return
}
mutex.Lock()
defer mutex.Unlock()
ae := AuditEventFrom(ctx)
var ctxAnnotations *[]annotation
if ae == nil {
ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
}
ac.annotationMutex.Lock()
defer ac.annotationMutex.Unlock()
if len(keysAndValues)%2 != 0 {
klog.Errorf("Dropping mismatched audit annotation %q", keysAndValues[len(keysAndValues)-1])
}
for i := 0; i < len(keysAndValues); i += 2 {
addAuditAnnotationLocked(ae, ctxAnnotations, keysAndValues[i], keysAndValues[i+1])
addAuditAnnotationLocked(ac, keysAndValues[i], keysAndValues[i+1])
}
}
// AddAuditAnnotationsMap is a bulk version of AddAuditAnnotation. Refer to AddAuditAnnotation for
// restrictions on when this can be called.
func AddAuditAnnotationsMap(ctx context.Context, annotations map[string]string) {
mutex, ok := auditAnnotationsMutex(ctx)
if !ok {
ac := AuditContextFrom(ctx)
if ac == nil {
// auditing is not enabled
return
}
mutex.Lock()
defer mutex.Unlock()
ae := AuditEventFrom(ctx)
var ctxAnnotations *[]annotation
if ae == nil {
ctxAnnotations, _ = ctx.Value(auditAnnotationsKey).(*[]annotation)
}
ac.annotationMutex.Lock()
defer ac.annotationMutex.Unlock()
for k, v := range annotations {
addAuditAnnotationLocked(ae, ctxAnnotations, k, v)
addAuditAnnotationLocked(ac, k, v)
}
}
// addAuditAnnotationLocked is the shared code for recording an audit annotation. This method should
// only be called while the auditAnnotationsMutex is locked.
func addAuditAnnotationLocked(ae *auditinternal.Event, annotations *[]annotation, key, value string) {
if ae != nil {
logAnnotation(ae, key, value)
} else if annotations != nil {
*annotations = append(*annotations, annotation{key: key, value: value})
func addAuditAnnotationLocked(ac *AuditContext, key, value string) {
if ac.Event != nil {
logAnnotation(ac.Event, key, value)
} else {
ac.annotations = append(ac.annotations, annotation{key: key, value: value})
}
}
// This is private to prevent reads/write to the slice from outside of this package.
// The audit event should be directly read to get access to the annotations.
func addAuditAnnotationsFrom(ctx context.Context, ev *auditinternal.Event) {
mutex, ok := auditAnnotationsMutex(ctx)
if !ok {
ac := AuditContextFrom(ctx)
if ac == nil {
// auditing is not enabled
return
}
mutex.Lock()
defer mutex.Unlock()
ac.annotationMutex.Lock()
defer ac.annotationMutex.Unlock()
annotations, ok := ctx.Value(auditAnnotationsKey).(*[]annotation)
if !ok {
return // no annotations to copy
}
for _, kv := range *annotations {
for _, kv := range ac.annotations {
logAnnotation(ev, kv.key, kv.value)
}
}
@ -185,12 +156,13 @@ func logAnnotation(ae *auditinternal.Event, key, value string) {
ae.Annotations[key] = value
}
// WithAuditContext returns a new context that stores the pair of the audit
// configuration object that applies to the given request and
// the audit event that is going to be written to the API audit log.
func WithAuditContext(parent context.Context, ev *AuditContext) context.Context {
parent = withAuditAnnotationsMutex(parent)
return genericapirequest.WithValue(parent, auditKey, ev)
// WithAuditContext returns a new context that stores the AuditContext.
func WithAuditContext(parent context.Context) context.Context {
if AuditContextFrom(parent) != nil {
return parent // Avoid double registering.
}
return genericapirequest.WithValue(parent, auditKey, &AuditContext{})
}
// AuditEventFrom returns the audit event struct on the ctx
@ -209,17 +181,46 @@ func AuditContextFrom(ctx context.Context) *AuditContext {
return ev
}
// WithAuditAnnotationMutex adds a mutex for guarding context.AddAuditAnnotation.
func withAuditAnnotationsMutex(parent context.Context) context.Context {
if _, ok := parent.Value(auditAnnotationsMutexKey).(*sync.Mutex); ok {
return parent
// WithAuditID sets the AuditID on the AuditContext. The AuditContext must already be present in the
// request context. If the specified auditID is empty, no value is set.
func WithAuditID(ctx context.Context, auditID types.UID) {
if auditID == "" {
return
}
ac := AuditContextFrom(ctx)
if ac == nil {
return
}
ac.auditID = auditID
if ac.Event != nil {
ac.Event.AuditID = auditID
}
var mutex sync.Mutex
return genericapirequest.WithValue(parent, auditAnnotationsMutexKey, &mutex)
}
// AuditAnnotationsMutex returns the audit annotations mutex from the context.
func auditAnnotationsMutex(ctx context.Context) (*sync.Mutex, bool) {
mutex, ok := ctx.Value(auditAnnotationsMutexKey).(*sync.Mutex)
return mutex, ok
// AuditIDFrom returns the value of the audit ID from the request context.
func AuditIDFrom(ctx context.Context) (types.UID, bool) {
if ac := AuditContextFrom(ctx); ac != nil {
return ac.auditID, ac.auditID != ""
}
return "", false
}
// GetAuditIDTruncated returns the audit ID (truncated) from the request context.
// If the length of the Audit-ID value exceeds the limit, we truncate it to keep
// the first N (maxAuditIDLength) characters.
// This is intended to be used in logging only.
func GetAuditIDTruncated(ctx context.Context) string {
auditID, ok := AuditIDFrom(ctx)
if !ok {
return ""
}
// if the user has specified a very long audit ID then we will use the first N characters
// Note: assuming Audit-ID header is in ASCII
const maxAuditIDLength = 64
if len(auditID) > maxAuditIDLength {
auditID = auditID[:maxAuditIDLength]
}
return string(auditID)
}

View File

@ -21,18 +21,6 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
)
// AuditContext is a pair of the audit configuration object that applies to
// a given request and the audit Event object that is being captured.
// It's a convenient placeholder to store both these objects in the request context.
type AuditContext struct {
// RequestAuditConfig is the audit configuration that applies to the request
RequestAuditConfig RequestAuditConfig
// Event is the audit Event object that is being captured to be written in
// the API audit log. It is set to nil when the request is not being audited.
Event *audit.Event
}
// RequestAuditConfig is the evaluated audit configuration that is applicable to
// a given request. PolicyRuleEvaluator evaluates the audit policy against the
// authorizer attributes and returns a RequestAuditConfig that applies to the request.

View File

@ -21,7 +21,6 @@ import (
"context"
"fmt"
"net/http"
"reflect"
"time"
authnv1 "k8s.io/api/authentication/v1"
@ -34,7 +33,6 @@ import (
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/klog/v2"
"github.com/google/uuid"
@ -54,7 +52,7 @@ func NewEventFromRequest(req *http.Request, requestReceivedTimestamp time.Time,
Level: level,
}
auditID, found := request.AuditIDFrom(req.Context())
auditID, found := AuditIDFrom(req.Context())
if !found {
auditID = types.UID(uuid.New().String())
}
@ -154,7 +152,7 @@ func LogRequestObject(ctx context.Context, obj runtime.Object, objGV schema.Grou
if shouldOmitManagedFields(ctx) {
copy, ok, err := copyWithoutManagedFields(obj)
if err != nil {
klog.Warningf("error while dropping managed fields from the request for %q error: %v", reflect.TypeOf(obj).Name(), err)
klog.ErrorS(err, "Error while dropping managed fields from the request", "auditID", ae.AuditID)
}
if ok {
obj = copy
@ -166,7 +164,7 @@ func LogRequestObject(ctx context.Context, obj runtime.Object, objGV schema.Grou
ae.RequestObject, err = encodeObject(obj, objGV, s)
if err != nil {
// TODO(audit): add error slice to audit event struct
klog.Warningf("Auditing failed of %v request: %v", reflect.TypeOf(obj).Name(), err)
klog.ErrorS(err, "Encoding failed of request object", "auditID", ae.AuditID, "gvr", gvr.String(), "obj", obj)
return
}
}
@ -209,7 +207,7 @@ func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupV
if shouldOmitManagedFields(ctx) {
copy, ok, err := copyWithoutManagedFields(obj)
if err != nil {
klog.Warningf("error while dropping managed fields from the response for %q error: %v", reflect.TypeOf(obj).Name(), err)
klog.ErrorS(err, "Error while dropping managed fields from the response", "auditID", ae.AuditID)
}
if ok {
obj = copy
@ -220,7 +218,7 @@ func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupV
var err error
ae.ResponseObject, err = encodeObject(obj, gv, s)
if err != nil {
klog.Warningf("Audit failed for %q response: %v", reflect.TypeOf(obj).Name(), err)
klog.ErrorS(err, "Encoding failed of response object", "auditID", ae.AuditID, "obj", obj)
}
}

View File

@ -1,65 +0,0 @@
/*
Copyright 2021 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 request
import (
"context"
"k8s.io/apimachinery/pkg/types"
)
type auditIDKeyType int
// auditIDKey is the key to associate the Audit-ID value of a request.
const auditIDKey auditIDKeyType = iota
// WithAuditID returns a copy of the parent context into which the Audit-ID
// associated with the request is set.
//
// If the specified auditID is empty, no value is set and the parent context is returned as is.
func WithAuditID(parent context.Context, auditID types.UID) context.Context {
if auditID == "" {
return parent
}
return WithValue(parent, auditIDKey, auditID)
}
// AuditIDFrom returns the value of the audit ID from the request context.
func AuditIDFrom(ctx context.Context) (types.UID, bool) {
auditID, ok := ctx.Value(auditIDKey).(types.UID)
return auditID, ok
}
// GetAuditIDTruncated returns the audit ID (truncated) from the request context.
// If the length of the Audit-ID value exceeds the limit, we truncate it to keep
// the first N (maxAuditIDLength) characters.
// This is intended to be used in logging only.
func GetAuditIDTruncated(ctx context.Context) string {
auditID, ok := AuditIDFrom(ctx)
if !ok {
return ""
}
// if the user has specified a very long audit ID then we will use the first N characters
// Note: assuming Audit-ID header is in ASCII
const maxAuditIDLength = 64
if len(auditID) > maxAuditIDLength {
auditID = auditID[0:maxAuditIDLength]
}
return string(auditID)
}

View File

@ -35,6 +35,13 @@ const (
// of code conflicts because changes are more likely to be scattered
// across the file.
// owner: @jefftree @alexzielenski
// alpha: v1.26
//
// Enables an single HTTP endpoint /discovery/<version> which supports native HTTP
// caching with ETags containing all APIResources known to the apiserver.
AggregatedDiscoveryEndpoint featuregate.Feature = "AggregatedDiscoveryEndpoint"
// owner: @smarterclayton
// alpha: v1.8
// beta: v1.9
@ -81,8 +88,15 @@ const (
// audited.
AdvancedAuditing featuregate.Feature = "AdvancedAuditing"
// owner: @cici37 @jpbetz
// kep: http://kep.k8s.io/3488
// alpha: v1.26
//
// Enables expression validation in Admission Control
ValidatingAdmissionPolicy featuregate.Feature = "ValidatingAdmissionPolicy"
// owner: @cici37
// kep: http://kep.k8s.io/2876
// kep: https://kep.k8s.io/2876
// alpha: v1.23
// beta: v1.25
//
@ -108,14 +122,14 @@ const (
EfficientWatchResumption featuregate.Feature = "EfficientWatchResumption"
// owner: @aramase
// kep: http://kep.k8s.io/3299
// kep: https://kep.k8s.io/3299
// alpha: v1.25
//
// Enables KMS v2 API for encryption at rest.
KMSv2 featuregate.Feature = "KMSv2"
// owner: @jiahuif
// kep: http://kep.k8s.io/2887
// kep: https://kep.k8s.io/2887
// alpha: v1.23
// beta: v1.24
//
@ -124,7 +138,7 @@ const (
OpenAPIEnums featuregate.Feature = "OpenAPIEnums"
// owner: @jefftree
// kep: http://kep.k8s.io/2896
// kep: https://kep.k8s.io/2896
// alpha: v1.23
// beta: v1.24
//
@ -156,7 +170,7 @@ const (
ServerSideApply featuregate.Feature = "ServerSideApply"
// owner: @kevindelgado
// kep: http://kep.k8s.io/2885
// kep: https://kep.k8s.io/2885
// alpha: v1.23
// beta: v1.24
//
@ -194,21 +208,25 @@ func init() {
// To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
AggregatedDiscoveryEndpoint: {Default: false, PreRelease: featuregate.Alpha},
APIListChunking: {Default: true, PreRelease: featuregate.Beta},
APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},
APIResponseCompression: {Default: true, PreRelease: featuregate.Beta},
APIServerIdentity: {Default: false, PreRelease: featuregate.Alpha},
APIServerIdentity: {Default: true, PreRelease: featuregate.Beta},
APIServerTracing: {Default: false, PreRelease: featuregate.Alpha},
AdvancedAuditing: {Default: true, PreRelease: featuregate.GA},
ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Alpha},
CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},
DryRun: {Default: true, PreRelease: featuregate.GA},
DryRun: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.28
EfficientWatchResumption: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
@ -222,7 +240,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
RemoveSelfLink: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
ServerSideApply: {Default: true, PreRelease: featuregate.GA},
ServerSideApply: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},

View File

@ -29,19 +29,25 @@ import (
"strings"
"time"
"go.opentelemetry.io/otel/attribute"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/apis/apiserver"
egressmetrics "k8s.io/apiserver/pkg/server/egressselector/metrics"
compbasemetrics "k8s.io/component-base/metrics"
"k8s.io/component-base/tracing"
"k8s.io/klog/v2"
utiltrace "k8s.io/utils/trace"
client "sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client"
)
var directDialer utilnet.DialFunc = http.DefaultTransport.(*http.Transport).DialContext
func init() {
client.Metrics.RegisterMetrics(compbasemetrics.NewKubeRegistry().Registerer())
}
// EgressSelector is the map of network context type to context dialer, for network egress.
type EgressSelector struct {
egressToDialer map[EgressType]utilnet.DialFunc
@ -216,6 +222,9 @@ func (u *udsGRPCConnector) connect(_ context.Context) (proxier, error) {
// See https://github.com/kubernetes-sigs/apiserver-network-proxy/issues/357.
tunnelCtx := context.TODO()
tunnel, err := client.CreateSingleUseGrpcTunnel(tunnelCtx, udsName, dialOption,
grpc.WithBlock(),
grpc.WithReturnConnectionError(),
grpc.WithTimeout(30*time.Second), // matches http.DefaultTransport dial timeout
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
@ -239,9 +248,10 @@ func (d *dialerCreator) createDialer() utilnet.DialFunc {
return directDialer
}
return func(ctx context.Context, network, addr string) (net.Conn, error) {
trace := utiltrace.New(fmt.Sprintf("Proxy via %s protocol over %s", d.options.protocol, d.options.transport), utiltrace.Field{Key: "address", Value: addr})
defer trace.LogIfLong(500 * time.Millisecond)
ctx, span := tracing.Start(ctx, fmt.Sprintf("Proxy via %s protocol over %s", d.options.protocol, d.options.transport), attribute.String("address", addr))
defer span.End(500 * time.Millisecond)
start := egressmetrics.Metrics.Clock().Now()
egressmetrics.Metrics.ObserveDialStart(d.options.protocol, d.options.transport)
proxier, err := d.connector.connect(ctx)
if err != nil {
egressmetrics.Metrics.ObserveDialFailure(d.options.protocol, d.options.transport, egressmetrics.StageConnect)

View File

@ -53,12 +53,24 @@ var (
// DialMetrics instruments dials to proxy server with prometheus metrics.
type DialMetrics struct {
clock clock.Clock
starts *metrics.CounterVec
latencies *metrics.HistogramVec
failures *metrics.CounterVec
}
// newDialMetrics create a new DialMetrics, configured with default metric names.
func newDialMetrics() *DialMetrics {
starts := metrics.NewCounterVec(
&metrics.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "dial_start_total",
Help: "Dial starts, labeled by the protocol (http-connect or grpc) and transport (tcp or uds).",
StabilityLevel: metrics.ALPHA,
},
[]string{"protocol", "transport"},
)
latencies := metrics.NewHistogramVec(
&metrics.HistogramOpts{
Namespace: namespace,
@ -82,9 +94,10 @@ func newDialMetrics() *DialMetrics {
[]string{"protocol", "transport", "stage"},
)
legacyregistry.MustRegister(starts)
legacyregistry.MustRegister(latencies)
legacyregistry.MustRegister(failures)
return &DialMetrics{latencies: latencies, failures: failures, clock: clock.RealClock{}}
return &DialMetrics{starts: starts, latencies: latencies, failures: failures, clock: clock.RealClock{}}
}
// Clock returns the clock.
@ -99,10 +112,16 @@ func (m *DialMetrics) SetClock(c clock.Clock) {
// Reset resets the metrics.
func (m *DialMetrics) Reset() {
m.starts.Reset()
m.latencies.Reset()
m.failures.Reset()
}
// ObserveDialStart records the start of a dial attempt, labeled by protocol, transport.
func (m *DialMetrics) ObserveDialStart(protocol, transport string) {
m.starts.WithLabelValues(protocol, transport).Inc()
}
// ObserveDialLatency records the latency of a dial, labeled by protocol, transport.
func (m *DialMetrics) ObserveDialLatency(elapsed time.Duration, protocol, transport string) {
m.latencies.WithLabelValues(protocol, transport).Observe(elapsed.Seconds())

View File

@ -25,7 +25,7 @@ import (
// NameGenerator generates names for objects. Some backends may have more information
// available to guide selection of new names and this interface hides those details.
type NameGenerator interface {
// GenerateName generates a valid name from the base name, adding a random suffix to the
// GenerateName generates a valid name from the base name, adding a random suffix to
// the base. If base is valid, the returned name must also be valid. The generator is
// responsible for knowing the maximum valid name length.
GenerateName(base string) string

View File

@ -141,7 +141,7 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
// Use http/1.1 instead of http/2.
// This is a workaround for http/2-enabled clients not load-balancing concurrent requests to multiple backends.
// See http://issue.k8s.io/75791 for details.
// See https://issue.k8s.io/75791 for details.
cfg.NextProtos = []string{"http/1.1"}
cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer

View File

@ -36,7 +36,7 @@ func ValidateWebhookURL(fldPath *field.Path, URL string, forceHttps bool) field.
allErrors = append(allErrors, field.Invalid(fldPath, u.Scheme, "'https' is the only allowed URL scheme"+form))
}
if len(u.Host) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath, u.Host, "host must be provided"+form))
allErrors = append(allErrors, field.Invalid(fldPath, u.Host, "host must be specified"+form))
}
if u.User != nil {
allErrors = append(allErrors, field.Invalid(fldPath, u.User.String(), "user information is not permitted in the URL"))

View File

@ -24,7 +24,7 @@ import (
type key int
const (
// auditAnnotationsKey is the context key for the audit annotations.
// warningRecorderKey is the context key for the warning recorder.
warningRecorderKey key = iota
)
@ -41,6 +41,7 @@ type Recorder interface {
func WithWarningRecorder(ctx context.Context, recorder Recorder) context.Context {
return context.WithValue(ctx, warningRecorderKey, recorder)
}
func warningRecorderFrom(ctx context.Context) (Recorder, bool) {
recorder, ok := ctx.Value(warningRecorderKey).(Recorder)
return recorder, ok