mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 22:25:03 +00:00
vendor: Update vendor logic
This commit is contained in:
parent
c6ac5cbc87
commit
4ca64b85f0
1540 changed files with 265304 additions and 91616 deletions
16
vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go
generated
vendored
16
vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go
generated
vendored
|
|
@ -17,13 +17,14 @@ limitations under the License.
|
|||
package lifecycle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
|
@ -73,7 +74,7 @@ var _ = initializer.WantsExternalKubeInformerFactory(&Lifecycle{})
|
|||
var _ = initializer.WantsExternalKubeClientSet(&Lifecycle{})
|
||||
|
||||
// Admit makes an admission decision based on the request attributes
|
||||
func (l *Lifecycle) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
func (l *Lifecycle) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
// prevent deletion of immortal namespaces
|
||||
if a.GetOperation() == admission.Delete && a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() && l.immortalNamespaces.Has(a.GetName()) {
|
||||
return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted"))
|
||||
|
|
@ -169,8 +170,15 @@ func (l *Lifecycle) Admit(a admission.Attributes, o admission.ObjectInterfaces)
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO: This should probably not be a 403
|
||||
return admission.NewForbidden(a, fmt.Errorf("unable to create new content in namespace %s because it is being terminated", a.GetNamespace()))
|
||||
err := admission.NewForbidden(a, fmt.Errorf("unable to create new content in namespace %s because it is being terminated", a.GetNamespace()))
|
||||
if apierr, ok := err.(*errors.StatusError); ok {
|
||||
apierr.ErrStatus.Details.Causes = append(apierr.ErrStatus.Details.Causes, metav1.StatusCause{
|
||||
Type: v1.NamespaceTerminatingCause,
|
||||
Message: fmt.Sprintf("namespace %s is being terminated", a.GetNamespace()),
|
||||
Field: "metadata.namespace",
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
221
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go
generated
vendored
221
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go
generated
vendored
|
|
@ -17,8 +17,13 @@ limitations under the License.
|
|||
package webhook
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"sync"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// WebhookAccessor provides a common interface to both mutating and validating webhook types.
|
||||
|
|
@ -26,135 +31,267 @@ type WebhookAccessor interface {
|
|||
// GetUID gets a string that uniquely identifies the webhook.
|
||||
GetUID() string
|
||||
|
||||
// GetConfigurationName gets the name of the webhook configuration that owns this webhook.
|
||||
GetConfigurationName() string
|
||||
|
||||
// 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
|
||||
// needed, use GetUID.
|
||||
GetName() string
|
||||
// GetClientConfig gets the webhook ClientConfig field.
|
||||
GetClientConfig() v1beta1.WebhookClientConfig
|
||||
GetClientConfig() v1.WebhookClientConfig
|
||||
// GetRules gets the webhook Rules field.
|
||||
GetRules() []v1beta1.RuleWithOperations
|
||||
GetRules() []v1.RuleWithOperations
|
||||
// GetFailurePolicy gets the webhook FailurePolicy field.
|
||||
GetFailurePolicy() *v1beta1.FailurePolicyType
|
||||
GetFailurePolicy() *v1.FailurePolicyType
|
||||
// GetMatchPolicy gets the webhook MatchPolicy field.
|
||||
GetMatchPolicy() *v1beta1.MatchPolicyType
|
||||
GetMatchPolicy() *v1.MatchPolicyType
|
||||
// GetNamespaceSelector gets the webhook NamespaceSelector field.
|
||||
GetNamespaceSelector() *metav1.LabelSelector
|
||||
// GetObjectSelector gets the webhook ObjectSelector field.
|
||||
GetObjectSelector() *metav1.LabelSelector
|
||||
// GetSideEffects gets the webhook SideEffects field.
|
||||
GetSideEffects() *v1beta1.SideEffectClass
|
||||
GetSideEffects() *v1.SideEffectClass
|
||||
// GetTimeoutSeconds gets the webhook TimeoutSeconds field.
|
||||
GetTimeoutSeconds() *int32
|
||||
// GetAdmissionReviewVersions gets the webhook AdmissionReviewVersions field.
|
||||
GetAdmissionReviewVersions() []string
|
||||
|
||||
// GetMutatingWebhook if the accessor contains a MutatingWebhook, returns it and true, else returns false.
|
||||
GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool)
|
||||
GetMutatingWebhook() (*v1.MutatingWebhook, bool)
|
||||
// GetValidatingWebhook if the accessor contains a ValidatingWebhook, returns it and true, else returns false.
|
||||
GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool)
|
||||
GetValidatingWebhook() (*v1.ValidatingWebhook, bool)
|
||||
}
|
||||
|
||||
// NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook.
|
||||
func NewMutatingWebhookAccessor(uid string, h *v1beta1.MutatingWebhook) WebhookAccessor {
|
||||
return mutatingWebhookAccessor{uid: uid, MutatingWebhook: h}
|
||||
func NewMutatingWebhookAccessor(uid, configurationName string, h *v1.MutatingWebhook) WebhookAccessor {
|
||||
return &mutatingWebhookAccessor{uid: uid, configurationName: configurationName, MutatingWebhook: h}
|
||||
}
|
||||
|
||||
type mutatingWebhookAccessor struct {
|
||||
*v1beta1.MutatingWebhook
|
||||
uid string
|
||||
*v1.MutatingWebhook
|
||||
uid string
|
||||
configurationName string
|
||||
|
||||
initObjectSelector sync.Once
|
||||
objectSelector labels.Selector
|
||||
objectSelectorErr error
|
||||
|
||||
initNamespaceSelector sync.Once
|
||||
namespaceSelector labels.Selector
|
||||
namespaceSelectorErr error
|
||||
|
||||
initClient sync.Once
|
||||
client *rest.RESTClient
|
||||
clientErr error
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetUID() string {
|
||||
func (m *mutatingWebhookAccessor) GetUID() string {
|
||||
return m.uid
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetName() string {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetConfigurationName() string {
|
||||
return m.configurationName
|
||||
}
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) {
|
||||
m.initClient.Do(func() {
|
||||
m.client, m.clientErr = clientManager.HookClient(hookClientConfigForWebhook(m))
|
||||
})
|
||||
return m.client, m.clientErr
|
||||
}
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) {
|
||||
m.initNamespaceSelector.Do(func() {
|
||||
m.namespaceSelector, m.namespaceSelectorErr = metav1.LabelSelectorAsSelector(m.NamespaceSelector)
|
||||
})
|
||||
return m.namespaceSelector, m.namespaceSelectorErr
|
||||
}
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) {
|
||||
m.initObjectSelector.Do(func() {
|
||||
m.objectSelector, m.objectSelectorErr = metav1.LabelSelectorAsSelector(m.ObjectSelector)
|
||||
})
|
||||
return m.objectSelector, m.objectSelectorErr
|
||||
}
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetName() string {
|
||||
return m.Name
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig {
|
||||
return m.ClientConfig
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetRules() []v1.RuleWithOperations {
|
||||
return m.Rules
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType {
|
||||
return m.FailurePolicy
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType {
|
||||
return m.MatchPolicy
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
return m.NamespaceSelector
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
return m.ObjectSelector
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass {
|
||||
return m.SideEffects
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
return m.TimeoutSeconds
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
|
||||
func (m *mutatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
return m.AdmissionReviewVersions
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) {
|
||||
func (m *mutatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) {
|
||||
return m.MutatingWebhook, true
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) {
|
||||
func (m *mutatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// NewValidatingWebhookAccessor creates an accessor for a ValidatingWebhook.
|
||||
func NewValidatingWebhookAccessor(uid string, h *v1beta1.ValidatingWebhook) WebhookAccessor {
|
||||
return validatingWebhookAccessor{uid: uid, ValidatingWebhook: h}
|
||||
func NewValidatingWebhookAccessor(uid, configurationName string, h *v1.ValidatingWebhook) WebhookAccessor {
|
||||
return &validatingWebhookAccessor{uid: uid, configurationName: configurationName, ValidatingWebhook: h}
|
||||
}
|
||||
|
||||
type validatingWebhookAccessor struct {
|
||||
*v1beta1.ValidatingWebhook
|
||||
uid string
|
||||
*v1.ValidatingWebhook
|
||||
uid string
|
||||
configurationName string
|
||||
|
||||
initObjectSelector sync.Once
|
||||
objectSelector labels.Selector
|
||||
objectSelectorErr error
|
||||
|
||||
initNamespaceSelector sync.Once
|
||||
namespaceSelector labels.Selector
|
||||
namespaceSelectorErr error
|
||||
|
||||
initClient sync.Once
|
||||
client *rest.RESTClient
|
||||
clientErr error
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetUID() string {
|
||||
func (v *validatingWebhookAccessor) GetUID() string {
|
||||
return v.uid
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetName() string {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetConfigurationName() string {
|
||||
return v.configurationName
|
||||
}
|
||||
|
||||
func (v *validatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error) {
|
||||
v.initClient.Do(func() {
|
||||
v.client, v.clientErr = clientManager.HookClient(hookClientConfigForWebhook(v))
|
||||
})
|
||||
return v.client, v.clientErr
|
||||
}
|
||||
|
||||
func (v *validatingWebhookAccessor) GetParsedNamespaceSelector() (labels.Selector, error) {
|
||||
v.initNamespaceSelector.Do(func() {
|
||||
v.namespaceSelector, v.namespaceSelectorErr = metav1.LabelSelectorAsSelector(v.NamespaceSelector)
|
||||
})
|
||||
return v.namespaceSelector, v.namespaceSelectorErr
|
||||
}
|
||||
|
||||
func (v *validatingWebhookAccessor) GetParsedObjectSelector() (labels.Selector, error) {
|
||||
v.initObjectSelector.Do(func() {
|
||||
v.objectSelector, v.objectSelectorErr = metav1.LabelSelectorAsSelector(v.ObjectSelector)
|
||||
})
|
||||
return v.objectSelector, v.objectSelectorErr
|
||||
}
|
||||
|
||||
func (v *validatingWebhookAccessor) GetName() string {
|
||||
return v.Name
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetClientConfig() v1.WebhookClientConfig {
|
||||
return v.ClientConfig
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetRules() []v1.RuleWithOperations {
|
||||
return v.Rules
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetFailurePolicy() *v1.FailurePolicyType {
|
||||
return v.FailurePolicy
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetMatchPolicy() *v1.MatchPolicyType {
|
||||
return v.MatchPolicy
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
return v.NamespaceSelector
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
return v.ObjectSelector
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetSideEffects() *v1.SideEffectClass {
|
||||
return v.SideEffects
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
return v.TimeoutSeconds
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
|
||||
func (v *validatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
return v.AdmissionReviewVersions
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) {
|
||||
func (v *validatingWebhookAccessor) GetMutatingWebhook() (*v1.MutatingWebhook, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) {
|
||||
func (v *validatingWebhookAccessor) GetValidatingWebhook() (*v1.ValidatingWebhook, bool) {
|
||||
return v.ValidatingWebhook, true
|
||||
}
|
||||
|
||||
// hookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access
|
||||
// v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects. webhookutil.ClientConfig is used
|
||||
// to create a HookClient and the purpose of the config struct is to share that with other packages
|
||||
// that need to create a HookClient.
|
||||
func hookClientConfigForWebhook(w WebhookAccessor) webhookutil.ClientConfig {
|
||||
ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle}
|
||||
if w.GetClientConfig().URL != nil {
|
||||
ret.URL = *w.GetClientConfig().URL
|
||||
}
|
||||
if w.GetClientConfig().Service != nil {
|
||||
ret.Service = &webhookutil.ClientConfigService{
|
||||
Name: w.GetClientConfig().Service.Name,
|
||||
Namespace: w.GetClientConfig().Service.Namespace,
|
||||
}
|
||||
if w.GetClientConfig().Service.Port != nil {
|
||||
ret.Service.Port = *w.GetClientConfig().Service.Port
|
||||
} else {
|
||||
ret.Service.Port = 443
|
||||
}
|
||||
if w.GetClientConfig().Service.Path != nil {
|
||||
ret.Service.Path = *w.GetClientConfig().Service.Path
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,9 +43,11 @@ func Resource(resource string) schema.GroupResource {
|
|||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
// TODO this will get cleaned up with the scheme types are fixed
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&WebhookAdmission{},
|
||||
)
|
||||
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("WebhookAdmissionConfiguration"),
|
||||
&WebhookAdmission{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
23
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/doc.go
generated
vendored
Normal file
23
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Copyright 2019 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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:conversion-gen=k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=apiserver.config.k8s.io
|
||||
|
||||
// Package v1 is the v1 version of the API.
|
||||
package v1
|
||||
50
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/register.go
generated
vendored
Normal file
50
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/register.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2019 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 v1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = "apiserver.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
|
||||
|
||||
var (
|
||||
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
|
||||
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("WebhookAdmissionConfiguration"),
|
||||
&WebhookAdmission{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
29
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/types.go
generated
vendored
Normal file
29
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright 2019 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 v1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// WebhookAdmission provides configuration for the webhook admission controller.
|
||||
type WebhookAdmission struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// KubeConfigFile is the path to the kubeconfig file.
|
||||
KubeConfigFile string `json:"kubeConfigFile"`
|
||||
}
|
||||
67
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.conversion.go
generated
vendored
Normal file
67
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.conversion.go
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
webhookadmission "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission"
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*WebhookAdmission)(nil), (*webhookadmission.WebhookAdmission)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_WebhookAdmission_To_webhookadmission_WebhookAdmission(a.(*WebhookAdmission), b.(*webhookadmission.WebhookAdmission), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*webhookadmission.WebhookAdmission)(nil), (*WebhookAdmission)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_webhookadmission_WebhookAdmission_To_v1_WebhookAdmission(a.(*webhookadmission.WebhookAdmission), b.(*WebhookAdmission), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in *WebhookAdmission, out *webhookadmission.WebhookAdmission, s conversion.Scope) error {
|
||||
out.KubeConfigFile = in.KubeConfigFile
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_WebhookAdmission_To_webhookadmission_WebhookAdmission is an autogenerated conversion function.
|
||||
func Convert_v1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in *WebhookAdmission, out *webhookadmission.WebhookAdmission, s conversion.Scope) error {
|
||||
return autoConvert_v1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_webhookadmission_WebhookAdmission_To_v1_WebhookAdmission(in *webhookadmission.WebhookAdmission, out *WebhookAdmission, s conversion.Scope) error {
|
||||
out.KubeConfigFile = in.KubeConfigFile
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_webhookadmission_WebhookAdmission_To_v1_WebhookAdmission is an autogenerated conversion function.
|
||||
func Convert_webhookadmission_WebhookAdmission_To_v1_WebhookAdmission(in *webhookadmission.WebhookAdmission, out *WebhookAdmission, s conversion.Scope) error {
|
||||
return autoConvert_webhookadmission_WebhookAdmission_To_v1_WebhookAdmission(in, out, s)
|
||||
}
|
||||
50
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.deepcopy.go
generated
vendored
Normal file
50
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.deepcopy.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *WebhookAdmission) DeepCopyInto(out *WebhookAdmission) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookAdmission.
|
||||
func (in *WebhookAdmission) DeepCopy() *WebhookAdmission {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(WebhookAdmission)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *WebhookAdmission) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.defaults.go
generated
vendored
Normal file
32
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1/zz_generated.defaults.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
||||
2
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/kubeconfig.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/kubeconfig.go
generated
vendored
|
|
@ -27,6 +27,7 @@ import (
|
|||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1"
|
||||
)
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ var (
|
|||
|
||||
func init() {
|
||||
utilruntime.Must(webhookadmission.AddToScheme(scheme))
|
||||
utilruntime.Must(v1.AddToScheme(scheme))
|
||||
utilruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||
}
|
||||
|
||||
|
|
|
|||
19
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
generated
vendored
19
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
generated
vendored
|
|
@ -21,8 +21,9 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
|
|
@ -65,7 +66,14 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cm, err := webhookutil.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
||||
cm, err := webhookutil.NewClientManager(
|
||||
[]schema.GroupVersion{
|
||||
admissionv1beta1.SchemeGroupVersion,
|
||||
admissionv1.SchemeGroupVersion,
|
||||
},
|
||||
admissionv1beta1.AddToScheme,
|
||||
admissionv1.AddToScheme,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -147,7 +155,7 @@ func (a *Webhook) ShouldCallHook(h webhook.WebhookAccessor, attr admission.Attri
|
|||
break
|
||||
}
|
||||
}
|
||||
if invocation == nil && h.GetMatchPolicy() != nil && *h.GetMatchPolicy() == v1beta1.Equivalent {
|
||||
if invocation == nil && h.GetMatchPolicy() != nil && *h.GetMatchPolicy() == v1.Equivalent {
|
||||
attrWithOverride := &attrWithResourceOverride{Attributes: attr}
|
||||
equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource())
|
||||
// honor earlier rules first
|
||||
|
|
@ -203,7 +211,7 @@ type attrWithResourceOverride struct {
|
|||
func (a *attrWithResourceOverride) GetResource() schema.GroupVersionResource { return a.resource }
|
||||
|
||||
// Dispatch is called by the downstream Validate or Admit methods.
|
||||
func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
func (a *Webhook) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
if rules.IsWebhookConfigurationResource(attr) {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -211,8 +219,5 @@ func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfac
|
|||
return admission.NewForbidden(attr, fmt.Errorf("not yet ready to handle request"))
|
||||
}
|
||||
hooks := a.hookSource.Webhooks()
|
||||
// TODO: Figure out if adding one second timeout make sense here.
|
||||
ctx := context.TODO()
|
||||
|
||||
return a.dispatcher.Dispatch(ctx, attr, o, hooks)
|
||||
}
|
||||
|
|
|
|||
260
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
generated
vendored
260
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
generated
vendored
|
|
@ -27,8 +27,8 @@ import (
|
|||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/klog"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -39,11 +39,24 @@ import (
|
|||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
||||
webhookrequest "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
// PatchAuditAnnotationPrefix is a prefix for persisting webhook patch in audit annotation.
|
||||
// Audit handler decides whether annotation with this prefix should be logged based on audit level.
|
||||
// Since mutating webhook patches the request body, audit level must be greater or equal to Request
|
||||
// for the annotation to be logged
|
||||
PatchAuditAnnotationPrefix = "patch.webhook.admission.k8s.io/"
|
||||
// MutationAuditAnnotationPrefix is a prefix for presisting webhook mutation existence in audit annotation.
|
||||
MutationAuditAnnotationPrefix = "mutation.webhook.admission.k8s.io/"
|
||||
)
|
||||
|
||||
var encodingjson = json.CaseSensitiveJsonIterator()
|
||||
|
||||
type mutatingDispatcher struct {
|
||||
cm *webhookutil.ClientManager
|
||||
plugin *Plugin
|
||||
|
|
@ -76,7 +89,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||
webhookReinvokeCtx.SetLastWebhookInvocationOutput(attr.GetObject())
|
||||
}()
|
||||
var versionedAttr *generic.VersionedAttributes
|
||||
for _, hook := range hooks {
|
||||
for i, hook := range hooks {
|
||||
attrForCheck := attr
|
||||
if versionedAttr != nil {
|
||||
attrForCheck = versionedAttr
|
||||
|
|
@ -90,7 +103,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||
}
|
||||
hook, ok := invocation.Webhook.GetMutatingWebhook()
|
||||
if !ok {
|
||||
return fmt.Errorf("mutating webhook dispatch requires v1beta1.MutatingWebhook, but got %T", hook)
|
||||
return fmt.Errorf("mutating webhook dispatch requires v1.MutatingWebhook, but got %T", hook)
|
||||
}
|
||||
// This means that during reinvocation, a webhook will not be
|
||||
// called for the first time. For example, if the webhook is
|
||||
|
|
@ -115,31 +128,61 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||
}
|
||||
|
||||
t := time.Now()
|
||||
|
||||
changed, err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "admit", hook.Name)
|
||||
round := 0
|
||||
if reinvokeCtx.IsReinvoke() {
|
||||
round = 1
|
||||
}
|
||||
changed, err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o, round, i)
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == admissionregistrationv1.Ignore
|
||||
rejected := false
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *webhookutil.ErrCallingWebhook:
|
||||
if !ignoreClientCallFailures {
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionCallingWebhookError, 0)
|
||||
}
|
||||
case *webhookutil.ErrWebhookRejection:
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionNoError, int(err.Status.ErrStatus.Code))
|
||||
default:
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "admit", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionAPIServerInternalError, 0)
|
||||
}
|
||||
}
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), rejected, versionedAttr.Attributes, "admit", hook.Name)
|
||||
if changed {
|
||||
// Patch had changed the object. Prepare to reinvoke all previous webhooks that are eligible for re-invocation.
|
||||
webhookReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
|
||||
reinvokeCtx.SetShouldReinvoke()
|
||||
}
|
||||
if hook.ReinvocationPolicy != nil && *hook.ReinvocationPolicy == v1beta1.IfNeededReinvocationPolicy {
|
||||
if hook.ReinvocationPolicy != nil && *hook.ReinvocationPolicy == admissionregistrationv1.IfNeededReinvocationPolicy {
|
||||
webhookReinvokeCtx.AddReinvocableWebhookToPreviouslyInvoked(invocation.Webhook.GetUID())
|
||||
}
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||
if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok {
|
||||
if ignoreClientCallFailures {
|
||||
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||
utilruntime.HandleError(callErr)
|
||||
continue
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// parent context is canceled or timed out, no point in continuing
|
||||
return apierrors.NewTimeoutError("request did not complete within requested timeout", 0)
|
||||
default:
|
||||
// individual webhook timed out, but parent context did not, continue
|
||||
continue
|
||||
}
|
||||
}
|
||||
klog.Warningf("Failed calling webhook, failing closed %v: %v", hook.Name, err)
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
if rejectionErr, ok := err.(*webhookutil.ErrWebhookRejection); ok {
|
||||
return rejectionErr.Status
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -153,60 +196,89 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||
|
||||
// note that callAttrMutatingHook updates attr
|
||||
|
||||
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) (bool, error) {
|
||||
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admissionregistrationv1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces, round, idx int) (bool, error) {
|
||||
configurationName := invocation.Webhook.GetConfigurationName()
|
||||
annotator := newWebhookAnnotator(attr, round, idx, h.Name, configurationName)
|
||||
changed := false
|
||||
defer func() { annotator.addMutationAnnotation(changed) }()
|
||||
if attr.Attributes.IsDryRun() {
|
||||
if h.SideEffects == nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
}
|
||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||
if !(*h.SideEffects == admissionregistrationv1.SideEffectClassNone || *h.SideEffects == admissionregistrationv1.SideEffectClassNoneOnDryRun) {
|
||||
return false, webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
||||
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
||||
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")}
|
||||
}
|
||||
|
||||
// Make the webhook request
|
||||
request := request.CreateAdmissionReview(attr, invocation)
|
||||
client, err := a.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook))
|
||||
uid, request, response, err := webhookrequest.CreateAdmissionObjects(attr, invocation)
|
||||
if err != nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
response := &admissionv1beta1.AdmissionReview{}
|
||||
r := client.Post().Context(ctx).Body(&request)
|
||||
if h.TimeoutSeconds != nil {
|
||||
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||
// Make the webhook request
|
||||
client, err := invocation.Webhook.GetRESTClient(a.cm)
|
||||
if err != nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
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)
|
||||
|
||||
// if the webhook has a specific timeout, wrap the context to apply it
|
||||
if h.TimeoutSeconds != nil {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(*h.TimeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
r := client.Post().Context(ctx).Body(request)
|
||||
|
||||
// if the context has a deadline, set it as a parameter to inform the backend
|
||||
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||
// compute the timeout
|
||||
if timeout := time.Until(deadline); timeout > 0 {
|
||||
// if it's not an even number of seconds, round up to the nearest second
|
||||
if truncated := timeout.Truncate(time.Second); truncated != timeout {
|
||||
timeout = truncated + time.Second
|
||||
}
|
||||
// set the timeout
|
||||
r.Timeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.Do().Into(response); err != nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
trace.Step("Request completed")
|
||||
|
||||
if response.Response == nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
result, err := webhookrequest.VerifyAdmissionResponse(uid, true, response)
|
||||
if err != nil {
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
|
||||
for k, v := range response.Response.AuditAnnotations {
|
||||
for k, v := range result.AuditAnnotations {
|
||||
key := h.Name + "/" + k
|
||||
if err := attr.Attributes.AddAnnotation(key, v); err != nil {
|
||||
klog.Warningf("Failed to set admission audit annotation %s to %s for mutating webhook %s: %v", key, v, h.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !response.Response.Allowed {
|
||||
return false, webhookerrors.ToStatusErr(h.Name, response.Response.Result)
|
||||
if !result.Allowed {
|
||||
return false, &webhookutil.ErrWebhookRejection{Status: webhookerrors.ToStatusErr(h.Name, result.Result)}
|
||||
}
|
||||
|
||||
patchJS := response.Response.Patch
|
||||
if len(patchJS) == 0 {
|
||||
if len(result.Patch) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
patchObj, err := jsonpatch.DecodePatch(patchJS)
|
||||
patchObj, err := jsonpatch.DecodePatch(result.Patch)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
||||
if len(patchObj) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -216,14 +288,21 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
|
|||
return false, apierrors.NewInternalError(fmt.Errorf("admission webhook %q attempted to modify the object, which is not supported for this operation", h.Name))
|
||||
}
|
||||
|
||||
var patchedJS []byte
|
||||
jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), false)
|
||||
objJS, err := runtime.Encode(jsonSerializer, attr.VersionedObject)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
patchedJS, err := patchObj.Apply(objJS)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
switch result.PatchType {
|
||||
// VerifyAdmissionResponse normalizes to v1 patch types, regardless of the AdmissionReview version used
|
||||
case admissionv1.PatchTypeJSONPatch:
|
||||
objJS, err := runtime.Encode(jsonSerializer, attr.VersionedObject)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
patchedJS, err = patchObj.Apply(objJS)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
default:
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("unsupported patch type %q", result.PatchType)}
|
||||
}
|
||||
|
||||
var newVersionedObject runtime.Object
|
||||
|
|
@ -244,10 +323,103 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
|
|||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
||||
changed := !apiequality.Semantic.DeepEqual(attr.VersionedObject, newVersionedObject)
|
||||
|
||||
changed = !apiequality.Semantic.DeepEqual(attr.VersionedObject, newVersionedObject)
|
||||
trace.Step("Patch applied")
|
||||
annotator.addPatchAnnotation(patchObj, result.PatchType)
|
||||
attr.Dirty = true
|
||||
attr.VersionedObject = newVersionedObject
|
||||
o.GetObjectDefaulter().Default(attr.VersionedObject)
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
type webhookAnnotator struct {
|
||||
attr *generic.VersionedAttributes
|
||||
patchAnnotationKey string
|
||||
mutationAnnotationKey string
|
||||
webhook string
|
||||
configuration string
|
||||
}
|
||||
|
||||
func newWebhookAnnotator(attr *generic.VersionedAttributes, round, idx int, webhook, configuration string) *webhookAnnotator {
|
||||
return &webhookAnnotator{
|
||||
attr: attr,
|
||||
patchAnnotationKey: fmt.Sprintf("%sround_%d_index_%d", PatchAuditAnnotationPrefix, round, idx),
|
||||
mutationAnnotationKey: fmt.Sprintf("%sround_%d_index_%d", MutationAuditAnnotationPrefix, round, idx),
|
||||
webhook: webhook,
|
||||
configuration: configuration,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *webhookAnnotator) addMutationAnnotation(mutated bool) {
|
||||
if w.attr == nil || w.attr.Attributes == nil {
|
||||
return
|
||||
}
|
||||
value, err := mutationAnnotationValue(w.configuration, w.webhook, mutated)
|
||||
if err != nil {
|
||||
klog.Warningf("unexpected error composing mutating webhook annotation: %v", err)
|
||||
return
|
||||
}
|
||||
if err := w.attr.Attributes.AddAnnotation(w.mutationAnnotationKey, value); err != nil {
|
||||
klog.Warningf("failed to set mutation annotation for mutating webhook key %s to %s: %v", w.mutationAnnotationKey, value, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *webhookAnnotator) addPatchAnnotation(patch interface{}, patchType admissionv1.PatchType) {
|
||||
if w.attr == nil || w.attr.Attributes == nil {
|
||||
return
|
||||
}
|
||||
var value string
|
||||
var err error
|
||||
switch patchType {
|
||||
case admissionv1.PatchTypeJSONPatch:
|
||||
value, err = jsonPatchAnnotationValue(w.configuration, w.webhook, patch)
|
||||
if err != nil {
|
||||
klog.Warningf("unexpected error composing mutating webhook JSON patch annotation: %v", err)
|
||||
return
|
||||
}
|
||||
default:
|
||||
klog.Warningf("unsupported patch type for mutating webhook annotation: %v", patchType)
|
||||
return
|
||||
}
|
||||
if err := w.attr.Attributes.AddAnnotationWithLevel(w.patchAnnotationKey, value, auditinternal.LevelRequest); err != nil {
|
||||
// NOTE: we don't log actual patch in kube-apiserver log to avoid potentially
|
||||
// leaking information
|
||||
klog.Warningf("failed to set patch annotation for mutating webhook key %s; confugiration name: %s, webhook name: %s", w.patchAnnotationKey, w.configuration, w.webhook)
|
||||
}
|
||||
}
|
||||
|
||||
// MutationAuditAnnotation logs if a webhook invocation mutated the request object
|
||||
type MutationAuditAnnotation struct {
|
||||
Configuration string `json:"configuration"`
|
||||
Webhook string `json:"webhook"`
|
||||
Mutated bool `json:"mutated"`
|
||||
}
|
||||
|
||||
// PatchAuditAnnotation logs a patch from a mutating webhook
|
||||
type PatchAuditAnnotation struct {
|
||||
Configuration string `json:"configuration"`
|
||||
Webhook string `json:"webhook"`
|
||||
Patch interface{} `json:"patch,omitempty"`
|
||||
PatchType string `json:"patchType,omitempty"`
|
||||
}
|
||||
|
||||
func mutationAnnotationValue(configuration, webhook string, mutated bool) (string, error) {
|
||||
m := MutationAuditAnnotation{
|
||||
Configuration: configuration,
|
||||
Webhook: webhook,
|
||||
Mutated: mutated,
|
||||
}
|
||||
bytes, err := encodingjson.Marshal(m)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
func jsonPatchAnnotationValue(configuration, webhook string, patch interface{}) (string, error) {
|
||||
p := PatchAuditAnnotation{
|
||||
Configuration: configuration,
|
||||
Webhook: webhook,
|
||||
Patch: patch,
|
||||
PatchType: string(admissionv1.PatchTypeJSONPatch),
|
||||
}
|
||||
bytes, err := encodingjson.Marshal(p)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
|
|
|||
5
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin.go
generated
vendored
5
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin.go
generated
vendored
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package mutating
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
|
|
@ -70,6 +71,6 @@ func (a *Plugin) ValidateInitialization() error {
|
|||
}
|
||||
|
||||
// Admit makes an admission decision based on the request attributes.
|
||||
func (a *Plugin) Admit(attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return a.Webhook.Dispatch(attr, o)
|
||||
func (a *Plugin) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return a.Webhook.Dispatch(ctx, attr, o)
|
||||
}
|
||||
|
|
|
|||
3
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
generated
vendored
3
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
generated
vendored
|
|
@ -95,8 +95,7 @@ func (m *Matcher) MatchNamespaceSelector(h webhook.WebhookAccessor, attr admissi
|
|||
// Also update the comment in types.go
|
||||
return true, nil
|
||||
}
|
||||
// TODO: adding an LRU cache to cache the translation
|
||||
selector, err := metav1.LabelSelectorAsSelector(h.GetNamespaceSelector())
|
||||
selector, err := h.GetParsedNamespaceSelector()
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
|
|
|||
4
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go
generated
vendored
4
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go
generated
vendored
|
|
@ -19,7 +19,6 @@ package object
|
|||
import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
|
|
@ -47,8 +46,7 @@ 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) {
|
||||
// TODO: adding an LRU cache to cache the translation
|
||||
selector, err := metav1.LabelSelectorAsSelector(h.GetObjectSelector())
|
||||
selector, err := h.GetParsedObjectSelector()
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
|
|
|||
196
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
generated
vendored
196
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
generated
vendored
|
|
@ -17,16 +17,138 @@ limitations under the License.
|
|||
package request
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||
)
|
||||
|
||||
// CreateAdmissionReview creates an AdmissionReview for the provided admission.Attributes
|
||||
func CreateAdmissionReview(versionedAttributes *generic.VersionedAttributes, invocation *generic.WebhookInvocation) admissionv1beta1.AdmissionReview {
|
||||
// AdmissionResponse contains the fields extracted from an AdmissionReview response
|
||||
type AdmissionResponse struct {
|
||||
AuditAnnotations map[string]string
|
||||
Allowed bool
|
||||
Patch []byte
|
||||
PatchType admissionv1.PatchType
|
||||
Result *metav1.Status
|
||||
}
|
||||
|
||||
// VerifyAdmissionResponse checks the validity of the provided admission review object, and returns the
|
||||
// audit annotations, whether the response allowed the request, any provided patch/patchType/status,
|
||||
// or an error if the provided admission review was not valid.
|
||||
func VerifyAdmissionResponse(uid types.UID, mutating bool, review runtime.Object) (*AdmissionResponse, error) {
|
||||
switch r := review.(type) {
|
||||
case *admissionv1.AdmissionReview:
|
||||
if r.Response == nil {
|
||||
return nil, fmt.Errorf("webhook response was absent")
|
||||
}
|
||||
|
||||
// Verify UID matches
|
||||
if r.Response.UID != uid {
|
||||
return nil, fmt.Errorf("expected response.uid=%q, got %q", uid, r.Response.UID)
|
||||
}
|
||||
|
||||
// Verify GVK
|
||||
v1GVK := admissionv1.SchemeGroupVersion.WithKind("AdmissionReview")
|
||||
if r.GroupVersionKind() != v1GVK {
|
||||
return nil, fmt.Errorf("expected webhook response of %v, got %v", v1GVK.String(), r.GroupVersionKind().String())
|
||||
}
|
||||
|
||||
patch := []byte(nil)
|
||||
patchType := admissionv1.PatchType("")
|
||||
|
||||
if mutating {
|
||||
// Ensure a mutating webhook provides both patch and patchType together
|
||||
if len(r.Response.Patch) > 0 && r.Response.PatchType == nil {
|
||||
return nil, fmt.Errorf("webhook returned response.patch but not response.patchType")
|
||||
}
|
||||
if len(r.Response.Patch) == 0 && r.Response.PatchType != nil {
|
||||
return nil, fmt.Errorf("webhook returned response.patchType but not response.patch")
|
||||
}
|
||||
patch = r.Response.Patch
|
||||
if r.Response.PatchType != nil {
|
||||
patchType = *r.Response.PatchType
|
||||
if len(patchType) == 0 {
|
||||
return nil, fmt.Errorf("webhook returned invalid response.patchType of %q", patchType)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ensure a validating webhook doesn't return patch or patchType
|
||||
if len(r.Response.Patch) > 0 {
|
||||
return nil, fmt.Errorf("validating webhook may not return response.patch")
|
||||
}
|
||||
if r.Response.PatchType != nil {
|
||||
return nil, fmt.Errorf("validating webhook may not return response.patchType")
|
||||
}
|
||||
}
|
||||
|
||||
return &AdmissionResponse{
|
||||
AuditAnnotations: r.Response.AuditAnnotations,
|
||||
Allowed: r.Response.Allowed,
|
||||
Patch: patch,
|
||||
PatchType: patchType,
|
||||
Result: r.Response.Result,
|
||||
}, nil
|
||||
|
||||
case *admissionv1beta1.AdmissionReview:
|
||||
if r.Response == nil {
|
||||
return nil, fmt.Errorf("webhook response was absent")
|
||||
}
|
||||
|
||||
// Response GVK and response.uid were not verified in v1beta1 handling, allow any
|
||||
|
||||
patch := []byte(nil)
|
||||
patchType := admissionv1.PatchType("")
|
||||
if mutating {
|
||||
patch = r.Response.Patch
|
||||
if len(r.Response.Patch) > 0 {
|
||||
// patch type was not verified in v1beta1 admissionreview handling. pin to only supported version if a patch is provided.
|
||||
patchType = admissionv1.PatchTypeJSONPatch
|
||||
}
|
||||
}
|
||||
|
||||
return &AdmissionResponse{
|
||||
AuditAnnotations: r.Response.AuditAnnotations,
|
||||
Allowed: r.Response.Allowed,
|
||||
Patch: patch,
|
||||
PatchType: patchType,
|
||||
Result: r.Response.Result,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected response type %T", review)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAdmissionObjects returns the unique request uid, the AdmissionReview object to send the webhook and to decode the response into,
|
||||
// or an error if the webhook does not support receiving any of the admission review versions we know to send
|
||||
func CreateAdmissionObjects(versionedAttributes *generic.VersionedAttributes, invocation *generic.WebhookInvocation) (uid types.UID, request, response runtime.Object, err error) {
|
||||
for _, version := range invocation.Webhook.GetAdmissionReviewVersions() {
|
||||
switch version {
|
||||
case admissionv1.SchemeGroupVersion.Version:
|
||||
uid := types.UID(uuid.NewUUID())
|
||||
request := CreateV1AdmissionReview(uid, versionedAttributes, invocation)
|
||||
response := &admissionv1.AdmissionReview{}
|
||||
return uid, request, response, nil
|
||||
|
||||
case admissionv1beta1.SchemeGroupVersion.Version:
|
||||
uid := types.UID(uuid.NewUUID())
|
||||
request := CreateV1beta1AdmissionReview(uid, versionedAttributes, invocation)
|
||||
response := &admissionv1beta1.AdmissionReview{}
|
||||
return uid, request, response, nil
|
||||
|
||||
}
|
||||
}
|
||||
return "", nil, nil, fmt.Errorf("webhook does not accept known AdmissionReview versions (v1, v1beta1)")
|
||||
}
|
||||
|
||||
// CreateV1AdmissionReview creates an AdmissionReview for the provided admission.Attributes
|
||||
func CreateV1AdmissionReview(uid types.UID, versionedAttributes *generic.VersionedAttributes, invocation *generic.WebhookInvocation) *admissionv1.AdmissionReview {
|
||||
attr := versionedAttributes.Attributes
|
||||
gvk := invocation.Kind
|
||||
gvr := invocation.Resource
|
||||
|
|
@ -48,9 +170,75 @@ func CreateAdmissionReview(versionedAttributes *generic.VersionedAttributes, inv
|
|||
userInfo.Extra[key] = authenticationv1.ExtraValue(val)
|
||||
}
|
||||
|
||||
return admissionv1beta1.AdmissionReview{
|
||||
return &admissionv1.AdmissionReview{
|
||||
Request: &admissionv1.AdmissionRequest{
|
||||
UID: uid,
|
||||
Kind: metav1.GroupVersionKind{
|
||||
Group: gvk.Group,
|
||||
Kind: gvk.Kind,
|
||||
Version: gvk.Version,
|
||||
},
|
||||
Resource: metav1.GroupVersionResource{
|
||||
Group: gvr.Group,
|
||||
Resource: gvr.Resource,
|
||||
Version: gvr.Version,
|
||||
},
|
||||
SubResource: subresource,
|
||||
RequestKind: &metav1.GroupVersionKind{
|
||||
Group: requestGVK.Group,
|
||||
Kind: requestGVK.Kind,
|
||||
Version: requestGVK.Version,
|
||||
},
|
||||
RequestResource: &metav1.GroupVersionResource{
|
||||
Group: requestGVR.Group,
|
||||
Resource: requestGVR.Resource,
|
||||
Version: requestGVR.Version,
|
||||
},
|
||||
RequestSubResource: requestSubResource,
|
||||
Name: attr.GetName(),
|
||||
Namespace: attr.GetNamespace(),
|
||||
Operation: admissionv1.Operation(attr.GetOperation()),
|
||||
UserInfo: userInfo,
|
||||
Object: runtime.RawExtension{
|
||||
Object: versionedAttributes.VersionedObject,
|
||||
},
|
||||
OldObject: runtime.RawExtension{
|
||||
Object: versionedAttributes.VersionedOldObject,
|
||||
},
|
||||
DryRun: &dryRun,
|
||||
Options: runtime.RawExtension{
|
||||
Object: attr.GetOperationOptions(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateV1beta1AdmissionReview creates an AdmissionReview for the provided admission.Attributes
|
||||
func CreateV1beta1AdmissionReview(uid types.UID, versionedAttributes *generic.VersionedAttributes, invocation *generic.WebhookInvocation) *admissionv1beta1.AdmissionReview {
|
||||
attr := versionedAttributes.Attributes
|
||||
gvk := invocation.Kind
|
||||
gvr := invocation.Resource
|
||||
subresource := invocation.Subresource
|
||||
requestGVK := attr.GetKind()
|
||||
requestGVR := attr.GetResource()
|
||||
requestSubResource := attr.GetSubresource()
|
||||
aUserInfo := attr.GetUserInfo()
|
||||
userInfo := authenticationv1.UserInfo{
|
||||
Extra: make(map[string]authenticationv1.ExtraValue),
|
||||
Groups: aUserInfo.GetGroups(),
|
||||
UID: aUserInfo.GetUID(),
|
||||
Username: aUserInfo.GetName(),
|
||||
}
|
||||
dryRun := attr.IsDryRun()
|
||||
|
||||
// Convert the extra information in the user object
|
||||
for key, val := range aUserInfo.GetExtra() {
|
||||
userInfo.Extra[key] = authenticationv1.ExtraValue(val)
|
||||
}
|
||||
|
||||
return &admissionv1beta1.AdmissionReview{
|
||||
Request: &admissionv1beta1.AdmissionRequest{
|
||||
UID: uuid.NewUUID(),
|
||||
UID: uid,
|
||||
Kind: metav1.GroupVersionKind{
|
||||
Group: gvk.Group,
|
||||
Kind: gvk.Kind,
|
||||
|
|
|
|||
14
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules/rules.go
generated
vendored
14
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules/rules.go
generated
vendored
|
|
@ -19,7 +19,7 @@ package rules
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
// Matcher determines if the Attr matches the Rule.
|
||||
type Matcher struct {
|
||||
Rule v1beta1.RuleWithOperations
|
||||
Rule v1.RuleWithOperations
|
||||
Attr admission.Attributes
|
||||
}
|
||||
|
||||
|
|
@ -56,15 +56,15 @@ func exactOrWildcard(items []string, requested string) bool {
|
|||
var namespaceResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
|
||||
|
||||
func (r *Matcher) scope() bool {
|
||||
if r.Rule.Scope == nil || *r.Rule.Scope == v1beta1.AllScopes {
|
||||
if r.Rule.Scope == nil || *r.Rule.Scope == v1.AllScopes {
|
||||
return true
|
||||
}
|
||||
// attr.GetNamespace() is set to the name of the namespace for requests of the namespace object itself.
|
||||
switch *r.Rule.Scope {
|
||||
case v1beta1.NamespacedScope:
|
||||
case v1.NamespacedScope:
|
||||
// first make sure that we are not requesting a namespace object (namespace objects are cluster-scoped)
|
||||
return r.Attr.GetResource() != namespaceResource && r.Attr.GetNamespace() != metav1.NamespaceNone
|
||||
case v1beta1.ClusterScope:
|
||||
case v1.ClusterScope:
|
||||
// also return true if the request is for a namespace object (namespace objects are cluster-scoped)
|
||||
return r.Attr.GetResource() == namespaceResource || r.Attr.GetNamespace() == metav1.NamespaceNone
|
||||
default:
|
||||
|
|
@ -83,12 +83,12 @@ func (r *Matcher) version() bool {
|
|||
func (r *Matcher) operation() bool {
|
||||
attrOp := r.Attr.GetOperation()
|
||||
for _, op := range r.Rule.Operations {
|
||||
if op == v1beta1.OperationAll {
|
||||
if op == v1.OperationAll {
|
||||
return true
|
||||
}
|
||||
// The constants are the same such that this is a valid cast (and this
|
||||
// is tested).
|
||||
if op == v1beta1.OperationType(attrOp) {
|
||||
if op == v1.OperationType(attrOp) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go
generated
vendored
58
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go
generated
vendored
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 util
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
)
|
||||
|
||||
// HookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access
|
||||
// v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects. webhookutil.ClientConfig is used
|
||||
// to create a HookClient and the purpose of the config struct is to share that with other packages
|
||||
// that need to create a HookClient.
|
||||
func HookClientConfigForWebhook(w webhook.WebhookAccessor) webhookutil.ClientConfig {
|
||||
ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle}
|
||||
if w.GetClientConfig().URL != nil {
|
||||
ret.URL = *w.GetClientConfig().URL
|
||||
}
|
||||
if w.GetClientConfig().Service != nil {
|
||||
ret.Service = &webhookutil.ClientConfigService{
|
||||
Name: w.GetClientConfig().Service.Name,
|
||||
Namespace: w.GetClientConfig().Service.Namespace,
|
||||
}
|
||||
if w.GetClientConfig().Service.Port != nil {
|
||||
ret.Service.Port = *w.GetClientConfig().Service.Port
|
||||
} else {
|
||||
ret.Service.Port = 443
|
||||
}
|
||||
if w.GetClientConfig().Service.Path != nil {
|
||||
ret.Service.Path = *w.GetClientConfig().Service.Path
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// HasAdmissionReviewVersion check whether a version is accepted by a given webhook.
|
||||
func HasAdmissionReviewVersion(a string, w webhook.WebhookAccessor) bool {
|
||||
for _, b := range w.GetAdmissionReviewVersions() {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
106
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
generated
vendored
106
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
generated
vendored
|
|
@ -22,8 +22,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
|
@ -32,10 +31,10 @@ import (
|
|||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
||||
webhookrequest "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/klog"
|
||||
utiltrace "k8s.io/utils/trace"
|
||||
)
|
||||
|
||||
type validatingDispatcher struct {
|
||||
|
|
@ -80,6 +79,14 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||
return nil
|
||||
}
|
||||
|
||||
// Check if the request has already timed out before spawning remote calls
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// parent context is canceled or timed out, no point in continuing
|
||||
return apierrors.NewTimeoutError("request did not complete within requested timeout", 0)
|
||||
default:
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
errCh := make(chan error, len(relevantHooks))
|
||||
wg.Add(len(relevantHooks))
|
||||
|
|
@ -88,18 +95,34 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||
defer wg.Done()
|
||||
hook, ok := invocation.Webhook.GetValidatingWebhook()
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("validating webhook dispatch requires v1beta1.ValidatingWebhook, but got %T", hook))
|
||||
utilruntime.HandleError(fmt.Errorf("validating webhook dispatch requires v1.ValidatingWebhook, but got %T", hook))
|
||||
return
|
||||
}
|
||||
versionedAttr := versionedAttrs[invocation.Kind]
|
||||
t := time.Now()
|
||||
err := d.callHook(ctx, hook, invocation, versionedAttr)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "validating", hook.Name)
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1.Ignore
|
||||
rejected := false
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *webhookutil.ErrCallingWebhook:
|
||||
if !ignoreClientCallFailures {
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionCallingWebhookError, 0)
|
||||
}
|
||||
case *webhookutil.ErrWebhookRejection:
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionNoError, int(err.Status.ErrStatus.Code))
|
||||
default:
|
||||
rejected = true
|
||||
admissionmetrics.Metrics.ObserveWebhookRejection(hook.Name, "validating", string(versionedAttr.Attributes.GetOperation()), admissionmetrics.WebhookRejectionAPIServerInternalError, 0)
|
||||
}
|
||||
}
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), rejected, versionedAttr.Attributes, "validating", hook.Name)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||
if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok {
|
||||
if ignoreClientCallFailures {
|
||||
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||
|
|
@ -112,6 +135,9 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||
return
|
||||
}
|
||||
|
||||
if rejectionErr, ok := err.(*webhookutil.ErrWebhookRejection); ok {
|
||||
err = rejectionErr.Status
|
||||
}
|
||||
klog.Warningf("rejected by webhook %q: %#v", hook.Name, err)
|
||||
errCh <- err
|
||||
}(relevantHooks[i])
|
||||
|
|
@ -135,48 +161,74 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||
return errs[0]
|
||||
}
|
||||
|
||||
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.ValidatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error {
|
||||
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1.ValidatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error {
|
||||
if attr.Attributes.IsDryRun() {
|
||||
if h.SideEffects == nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
}
|
||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||
if !(*h.SideEffects == v1.SideEffectClassNone || *h.SideEffects == v1.SideEffectClassNoneOnDryRun) {
|
||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
||||
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
||||
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")}
|
||||
}
|
||||
|
||||
// Make the webhook request
|
||||
request := request.CreateAdmissionReview(attr, invocation)
|
||||
client, err := d.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook))
|
||||
uid, request, response, err := webhookrequest.CreateAdmissionObjects(attr, invocation)
|
||||
if err != nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
response := &admissionv1beta1.AdmissionReview{}
|
||||
r := client.Post().Context(ctx).Body(&request)
|
||||
if h.TimeoutSeconds != nil {
|
||||
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||
// Make the webhook request
|
||||
client, err := invocation.Webhook.GetRESTClient(d.cm)
|
||||
if err != nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
trace := utiltrace.New("Call validating webhook",
|
||||
utiltrace.Field{"configuration", invocation.Webhook.GetConfigurationName()},
|
||||
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)
|
||||
|
||||
// if the webhook has a specific timeout, wrap the context to apply it
|
||||
if h.TimeoutSeconds != nil {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(*h.TimeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
r := client.Post().Context(ctx).Body(request)
|
||||
|
||||
// if the context has a deadline, set it as a parameter to inform the backend
|
||||
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||
// compute the timeout
|
||||
if timeout := time.Until(deadline); timeout > 0 {
|
||||
// if it's not an even number of seconds, round up to the nearest second
|
||||
if truncated := timeout.Truncate(time.Second); truncated != timeout {
|
||||
timeout = truncated + time.Second
|
||||
}
|
||||
// set the timeout
|
||||
r.Timeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.Do().Into(response); err != nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
trace.Step("Request completed")
|
||||
|
||||
if response.Response == nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
result, err := webhookrequest.VerifyAdmissionResponse(uid, false, response)
|
||||
if err != nil {
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
for k, v := range response.Response.AuditAnnotations {
|
||||
|
||||
for k, v := range result.AuditAnnotations {
|
||||
key := h.Name + "/" + k
|
||||
if err := attr.Attributes.AddAnnotation(key, v); err != nil {
|
||||
klog.Warningf("Failed to set admission audit annotation %s to %s for validating webhook %s: %v", key, v, h.Name, err)
|
||||
}
|
||||
}
|
||||
if response.Response.Allowed {
|
||||
if result.Allowed {
|
||||
return nil
|
||||
}
|
||||
return webhookerrors.ToStatusErr(h.Name, response.Response.Result)
|
||||
return &webhookutil.ErrWebhookRejection{Status: webhookerrors.ToStatusErr(h.Name, result.Result)}
|
||||
}
|
||||
|
|
|
|||
5
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
generated
vendored
5
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
generated
vendored
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package validating
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
|
|
@ -61,6 +62,6 @@ func NewValidatingAdmissionWebhook(configFile io.Reader) (*Plugin, error) {
|
|||
}
|
||||
|
||||
// Validate makes an admission decision based on the request attributes.
|
||||
func (a *Plugin) Validate(attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return a.Webhook.Dispatch(attr, o)
|
||||
func (a *Plugin) Validate(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return a.Webhook.Dispatch(ctx, attr, o)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue