mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 22:25:03 +00:00
vendored changes
This commit is contained in:
parent
d091fff18b
commit
128f9a29f5
522 changed files with 29974 additions and 25705 deletions
71
vendor/k8s.io/apiserver/pkg/admission/attributes.go
generated
vendored
71
vendor/k8s.io/apiserver/pkg/admission/attributes.go
generated
vendored
|
|
@ -34,6 +34,7 @@ type attributesRecord struct {
|
|||
resource schema.GroupVersionResource
|
||||
subresource string
|
||||
operation Operation
|
||||
options runtime.Object
|
||||
dryRun bool
|
||||
object runtime.Object
|
||||
oldObject runtime.Object
|
||||
|
|
@ -43,20 +44,24 @@ type attributesRecord struct {
|
|||
// But ValidatingAdmissionWebhook add annotations concurrently.
|
||||
annotations map[string]string
|
||||
annotationsLock sync.RWMutex
|
||||
|
||||
reinvocationContext ReinvocationContext
|
||||
}
|
||||
|
||||
func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, dryRun bool, userInfo user.Info) Attributes {
|
||||
func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, operationOptions runtime.Object, dryRun bool, userInfo user.Info) Attributes {
|
||||
return &attributesRecord{
|
||||
kind: kind,
|
||||
namespace: namespace,
|
||||
name: name,
|
||||
resource: resource,
|
||||
subresource: subresource,
|
||||
operation: operation,
|
||||
dryRun: dryRun,
|
||||
object: object,
|
||||
oldObject: oldObject,
|
||||
userInfo: userInfo,
|
||||
kind: kind,
|
||||
namespace: namespace,
|
||||
name: name,
|
||||
resource: resource,
|
||||
subresource: subresource,
|
||||
operation: operation,
|
||||
options: operationOptions,
|
||||
dryRun: dryRun,
|
||||
object: object,
|
||||
oldObject: oldObject,
|
||||
userInfo: userInfo,
|
||||
reinvocationContext: &reinvocationContext{},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +89,10 @@ func (record *attributesRecord) GetOperation() Operation {
|
|||
return record.operation
|
||||
}
|
||||
|
||||
func (record *attributesRecord) GetOperationOptions() runtime.Object {
|
||||
return record.options
|
||||
}
|
||||
|
||||
func (record *attributesRecord) IsDryRun() bool {
|
||||
return record.dryRun
|
||||
}
|
||||
|
|
@ -134,6 +143,46 @@ func (record *attributesRecord) AddAnnotation(key, value string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (record *attributesRecord) GetReinvocationContext() ReinvocationContext {
|
||||
return record.reinvocationContext
|
||||
}
|
||||
|
||||
type reinvocationContext struct {
|
||||
// isReinvoke is true when admission plugins are being reinvoked
|
||||
isReinvoke bool
|
||||
// reinvokeRequested is true when an admission plugin requested a re-invocation of the chain
|
||||
reinvokeRequested bool
|
||||
// values stores reinvoke context values per plugin.
|
||||
values map[string]interface{}
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) IsReinvoke() bool {
|
||||
return rc.isReinvoke
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) SetIsReinvoke() {
|
||||
rc.isReinvoke = true
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) ShouldReinvoke() bool {
|
||||
return rc.reinvokeRequested
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) SetShouldReinvoke() {
|
||||
rc.reinvokeRequested = true
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) SetValue(plugin string, v interface{}) {
|
||||
if rc.values == nil {
|
||||
rc.values = map[string]interface{}{}
|
||||
}
|
||||
rc.values[plugin] = v
|
||||
}
|
||||
|
||||
func (rc *reinvocationContext) Value(plugin string) interface{} {
|
||||
return rc.values[plugin]
|
||||
}
|
||||
|
||||
func checkKeyFormat(key string) error {
|
||||
parts := strings.Split(key, "/")
|
||||
if len(parts) != 2 {
|
||||
|
|
|
|||
23
vendor/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go
generated
vendored
23
vendor/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go
generated
vendored
|
|
@ -24,6 +24,7 @@ import (
|
|||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||
"k8s.io/client-go/informers"
|
||||
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
||||
|
|
@ -48,7 +49,7 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g
|
|||
}
|
||||
|
||||
// Start with an empty list
|
||||
manager.configuration.Store(&v1beta1.MutatingWebhookConfiguration{})
|
||||
manager.configuration.Store([]webhook.WebhookAccessor{})
|
||||
|
||||
// On any change, rebuild the config
|
||||
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
|
|
@ -61,8 +62,8 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g
|
|||
}
|
||||
|
||||
// Webhooks returns the merged MutatingWebhookConfiguration.
|
||||
func (m *mutatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
|
||||
return m.configuration.Load().(*v1beta1.MutatingWebhookConfiguration).Webhooks
|
||||
func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor {
|
||||
return m.configuration.Load().([]webhook.WebhookAccessor)
|
||||
}
|
||||
|
||||
func (m *mutatingWebhookConfigurationManager) HasSynced() bool {
|
||||
|
|
@ -78,16 +79,24 @@ func (m *mutatingWebhookConfigurationManager) updateConfiguration() {
|
|||
m.configuration.Store(mergeMutatingWebhookConfigurations(configurations))
|
||||
}
|
||||
|
||||
func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) *v1beta1.MutatingWebhookConfiguration {
|
||||
var ret v1beta1.MutatingWebhookConfiguration
|
||||
func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) []webhook.WebhookAccessor {
|
||||
// The internal order of webhooks for each configuration is provided by the user
|
||||
// but configurations themselves can be in any order. As we are going to run these
|
||||
// webhooks in serial, they are sorted here to have a deterministic order.
|
||||
sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName)
|
||||
accessors := []webhook.WebhookAccessor{}
|
||||
for _, c := range configurations {
|
||||
ret.Webhooks = append(ret.Webhooks, c.Webhooks...)
|
||||
// webhook names are not validated for uniqueness, so we check for duplicates and
|
||||
// add a int suffix to distinguish between them
|
||||
names := map[string]int{}
|
||||
for i := range c.Webhooks {
|
||||
n := c.Webhooks[i].Name
|
||||
uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n])
|
||||
names[n]++
|
||||
accessors = append(accessors, webhook.NewMutatingWebhookAccessor(uid, &c.Webhooks[i]))
|
||||
}
|
||||
}
|
||||
return &ret
|
||||
return accessors
|
||||
}
|
||||
|
||||
type MutatingWebhookConfigurationSorter []*v1beta1.MutatingWebhookConfiguration
|
||||
|
|
|
|||
25
vendor/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go
generated
vendored
25
vendor/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go
generated
vendored
|
|
@ -24,6 +24,7 @@ import (
|
|||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||
"k8s.io/client-go/informers"
|
||||
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
||||
|
|
@ -48,7 +49,7 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory)
|
|||
}
|
||||
|
||||
// Start with an empty list
|
||||
manager.configuration.Store(&v1beta1.ValidatingWebhookConfiguration{})
|
||||
manager.configuration.Store([]webhook.WebhookAccessor{})
|
||||
|
||||
// On any change, rebuild the config
|
||||
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
|
|
@ -61,8 +62,8 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory)
|
|||
}
|
||||
|
||||
// Webhooks returns the merged ValidatingWebhookConfiguration.
|
||||
func (v *validatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
|
||||
return v.configuration.Load().(*v1beta1.ValidatingWebhookConfiguration).Webhooks
|
||||
func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor {
|
||||
return v.configuration.Load().([]webhook.WebhookAccessor)
|
||||
}
|
||||
|
||||
// HasSynced returns true if the shared informers have synced.
|
||||
|
|
@ -79,15 +80,21 @@ func (v *validatingWebhookConfigurationManager) updateConfiguration() {
|
|||
v.configuration.Store(mergeValidatingWebhookConfigurations(configurations))
|
||||
}
|
||||
|
||||
func mergeValidatingWebhookConfigurations(
|
||||
configurations []*v1beta1.ValidatingWebhookConfiguration,
|
||||
) *v1beta1.ValidatingWebhookConfiguration {
|
||||
func mergeValidatingWebhookConfigurations(configurations []*v1beta1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor {
|
||||
sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName)
|
||||
var ret v1beta1.ValidatingWebhookConfiguration
|
||||
accessors := []webhook.WebhookAccessor{}
|
||||
for _, c := range configurations {
|
||||
ret.Webhooks = append(ret.Webhooks, c.Webhooks...)
|
||||
// webhook names are not validated for uniqueness, so we check for duplicates and
|
||||
// add a int suffix to distinguish between them
|
||||
names := map[string]int{}
|
||||
for i := range c.Webhooks {
|
||||
n := c.Webhooks[i].Name
|
||||
uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n])
|
||||
names[n]++
|
||||
accessors = append(accessors, webhook.NewValidatingWebhookAccessor(uid, &c.Webhooks[i]))
|
||||
}
|
||||
}
|
||||
return &ret
|
||||
return accessors
|
||||
}
|
||||
|
||||
type ValidatingWebhookConfigurationSorter []*v1beta1.ValidatingWebhookConfiguration
|
||||
|
|
|
|||
23
vendor/k8s.io/apiserver/pkg/admission/interfaces.go
generated
vendored
23
vendor/k8s.io/apiserver/pkg/admission/interfaces.go
generated
vendored
|
|
@ -41,6 +41,8 @@ type Attributes interface {
|
|||
GetSubresource() string
|
||||
// GetOperation is the operation being performed
|
||||
GetOperation() Operation
|
||||
// GetOperationOptions is the options for the operation being performed
|
||||
GetOperationOptions() runtime.Object
|
||||
// IsDryRun indicates that modifications will definitely not be persisted for this request. This is to prevent
|
||||
// admission controllers with side effects and a method of reconciliation from being overwhelmed.
|
||||
// However, a value of false for this does not mean that the modification will be persisted, because it
|
||||
|
|
@ -60,6 +62,9 @@ type Attributes interface {
|
|||
// An error is returned if the format of key is invalid. When trying to overwrite annotation with a new value, an error is returned.
|
||||
// Both ValidationInterface and MutationInterface are allowed to add Annotations.
|
||||
AddAnnotation(key, value string) error
|
||||
|
||||
// GetReinvocationContext tracks the admission request information relevant to the re-invocation policy.
|
||||
GetReinvocationContext() ReinvocationContext
|
||||
}
|
||||
|
||||
// ObjectInterfaces is an interface used by AdmissionController to get object interfaces
|
||||
|
|
@ -74,6 +79,8 @@ type ObjectInterfaces interface {
|
|||
GetObjectDefaulter() runtime.ObjectDefaulter
|
||||
// GetObjectConvertor is the ObjectConvertor appropriate for the requested object.
|
||||
GetObjectConvertor() runtime.ObjectConvertor
|
||||
// GetEquivalentResourceMapper is the EquivalentResourceMapper appropriate for finding equivalent resources and expected kind for the requested object.
|
||||
GetEquivalentResourceMapper() runtime.EquivalentResourceMapper
|
||||
}
|
||||
|
||||
// privateAnnotationsGetter is a private interface which allows users to get annotations from Attributes.
|
||||
|
|
@ -87,6 +94,22 @@ type AnnotationsGetter interface {
|
|||
GetAnnotations() map[string]string
|
||||
}
|
||||
|
||||
// ReinvocationContext provides access to the admission related state required to implement the re-invocation policy.
|
||||
type ReinvocationContext interface {
|
||||
// IsReinvoke returns true if the current admission check is a re-invocation.
|
||||
IsReinvoke() bool
|
||||
// SetIsReinvoke sets the current admission check as a re-invocation.
|
||||
SetIsReinvoke()
|
||||
// ShouldReinvoke returns true if any plugin has requested a re-invocation.
|
||||
ShouldReinvoke() bool
|
||||
// SetShouldReinvoke signals that a re-invocation is desired.
|
||||
SetShouldReinvoke()
|
||||
// AddValue set a value for a plugin name, possibly overriding a previous value.
|
||||
SetValue(plugin string, v interface{})
|
||||
// Value reads a value for a webhook.
|
||||
Value(plugin string) interface{}
|
||||
}
|
||||
|
||||
// Interface is an abstract, pluggable interface for Admission Control decisions.
|
||||
type Interface interface {
|
||||
// Handles returns true if this admission controller can handle the given operation
|
||||
|
|
|
|||
48
vendor/k8s.io/apiserver/pkg/admission/metrics/metrics.go
generated
vendored
48
vendor/k8s.io/apiserver/pkg/admission/metrics/metrics.go
generated
vendored
|
|
@ -32,8 +32,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
// Use buckets ranging from 25 ms to ~2.5 seconds.
|
||||
latencyBuckets = prometheus.ExponentialBuckets(25000, 2.5, 5)
|
||||
// Use buckets ranging from 5 ms to 2.5 seconds (admission webhooks timeout at 30 seconds by default).
|
||||
latencyBuckets = []float64{0.005, 0.025, 0.1, 0.5, 2.5}
|
||||
latencySummaryMaxAge = 5 * time.Hour
|
||||
|
||||
// Metrics provides access to all admission metrics.
|
||||
|
|
@ -153,14 +153,12 @@ func (m *AdmissionMetrics) ObserveWebhook(elapsed time.Duration, rejected bool,
|
|||
}
|
||||
|
||||
type metricSet struct {
|
||||
latencies *prometheus.HistogramVec
|
||||
deprecatedLatencies *prometheus.HistogramVec
|
||||
latenciesSummary *prometheus.SummaryVec
|
||||
deprecatedLatenciesSummary *prometheus.SummaryVec
|
||||
latencies *prometheus.HistogramVec
|
||||
latenciesSummary *prometheus.SummaryVec
|
||||
}
|
||||
|
||||
func newMetricSet(name string, labels []string, helpTemplate string, hasSummary bool) *metricSet {
|
||||
var summary, deprecatedSummary *prometheus.SummaryVec
|
||||
var summary *prometheus.SummaryVec
|
||||
if hasSummary {
|
||||
summary = prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
|
|
@ -172,16 +170,6 @@ func newMetricSet(name string, labels []string, helpTemplate string, hasSummary
|
|||
},
|
||||
labels,
|
||||
)
|
||||
deprecatedSummary = prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: fmt.Sprintf("%s_admission_latencies_milliseconds_summary", name),
|
||||
Help: fmt.Sprintf("(Deprecated) "+helpTemplate, "latency summary in milliseconds"),
|
||||
MaxAge: latencySummaryMaxAge,
|
||||
},
|
||||
labels,
|
||||
)
|
||||
}
|
||||
|
||||
return &metricSet{
|
||||
|
|
@ -195,56 +183,32 @@ func newMetricSet(name string, labels []string, helpTemplate string, hasSummary
|
|||
},
|
||||
labels,
|
||||
),
|
||||
deprecatedLatencies: prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: fmt.Sprintf("%s_admission_latencies_milliseconds", name),
|
||||
Help: fmt.Sprintf("(Deprecated) "+helpTemplate, "latency histogram in milliseconds"),
|
||||
Buckets: latencyBuckets,
|
||||
},
|
||||
labels,
|
||||
),
|
||||
|
||||
latenciesSummary: summary,
|
||||
deprecatedLatenciesSummary: deprecatedSummary,
|
||||
latenciesSummary: summary,
|
||||
}
|
||||
}
|
||||
|
||||
// MustRegister registers all the prometheus metrics in the metricSet.
|
||||
func (m *metricSet) mustRegister() {
|
||||
prometheus.MustRegister(m.latencies)
|
||||
prometheus.MustRegister(m.deprecatedLatencies)
|
||||
if m.latenciesSummary != nil {
|
||||
prometheus.MustRegister(m.latenciesSummary)
|
||||
}
|
||||
if m.deprecatedLatenciesSummary != nil {
|
||||
prometheus.MustRegister(m.deprecatedLatenciesSummary)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets all the prometheus metrics in the metricSet.
|
||||
func (m *metricSet) reset() {
|
||||
m.latencies.Reset()
|
||||
m.deprecatedLatencies.Reset()
|
||||
if m.latenciesSummary != nil {
|
||||
m.latenciesSummary.Reset()
|
||||
}
|
||||
if m.deprecatedLatenciesSummary != nil {
|
||||
m.deprecatedLatenciesSummary.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Observe records an observed admission event to all metrics in the metricSet.
|
||||
func (m *metricSet) observe(elapsed time.Duration, labels ...string) {
|
||||
elapsedSeconds := elapsed.Seconds()
|
||||
elapsedMicroseconds := float64(elapsed / time.Microsecond)
|
||||
m.latencies.WithLabelValues(labels...).Observe(elapsedSeconds)
|
||||
m.deprecatedLatencies.WithLabelValues(labels...).Observe(elapsedMicroseconds)
|
||||
if m.latenciesSummary != nil {
|
||||
m.latenciesSummary.WithLabelValues(labels...).Observe(elapsedSeconds)
|
||||
}
|
||||
if m.deprecatedLatenciesSummary != nil {
|
||||
m.deprecatedLatenciesSummary.WithLabelValues(labels...).Observe(elapsedMicroseconds)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
160
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go
generated
vendored
Normal file
160
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
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 webhook
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// WebhookAccessor provides a common interface to both mutating and validating webhook types.
|
||||
type WebhookAccessor interface {
|
||||
// GetUID gets a string that uniquely identifies the webhook.
|
||||
GetUID() string
|
||||
|
||||
// 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
|
||||
// GetRules gets the webhook Rules field.
|
||||
GetRules() []v1beta1.RuleWithOperations
|
||||
// GetFailurePolicy gets the webhook FailurePolicy field.
|
||||
GetFailurePolicy() *v1beta1.FailurePolicyType
|
||||
// GetMatchPolicy gets the webhook MatchPolicy field.
|
||||
GetMatchPolicy() *v1beta1.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
|
||||
// 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)
|
||||
// GetValidatingWebhook if the accessor contains a ValidatingWebhook, returns it and true, else returns false.
|
||||
GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool)
|
||||
}
|
||||
|
||||
// NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook.
|
||||
func NewMutatingWebhookAccessor(uid string, h *v1beta1.MutatingWebhook) WebhookAccessor {
|
||||
return mutatingWebhookAccessor{uid: uid, MutatingWebhook: h}
|
||||
}
|
||||
|
||||
type mutatingWebhookAccessor struct {
|
||||
*v1beta1.MutatingWebhook
|
||||
uid string
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetUID() string {
|
||||
return m.uid
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetName() string {
|
||||
return m.Name
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig {
|
||||
return m.ClientConfig
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations {
|
||||
return m.Rules
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType {
|
||||
return m.FailurePolicy
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType {
|
||||
return m.MatchPolicy
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
return m.NamespaceSelector
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
return m.ObjectSelector
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass {
|
||||
return m.SideEffects
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
return m.TimeoutSeconds
|
||||
}
|
||||
func (m mutatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
return m.AdmissionReviewVersions
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) {
|
||||
return m.MutatingWebhook, true
|
||||
}
|
||||
|
||||
func (m mutatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.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}
|
||||
}
|
||||
|
||||
type validatingWebhookAccessor struct {
|
||||
*v1beta1.ValidatingWebhook
|
||||
uid string
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetUID() string {
|
||||
return v.uid
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetName() string {
|
||||
return v.Name
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig {
|
||||
return v.ClientConfig
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations {
|
||||
return v.Rules
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType {
|
||||
return v.FailurePolicy
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType {
|
||||
return v.MatchPolicy
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector {
|
||||
return v.NamespaceSelector
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector {
|
||||
return v.ObjectSelector
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass {
|
||||
return v.SideEffects
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetTimeoutSeconds() *int32 {
|
||||
return v.TimeoutSeconds
|
||||
}
|
||||
func (v validatingWebhookAccessor) GetAdmissionReviewVersions() []string {
|
||||
return v.AdmissionReviewVersions
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (v validatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) {
|
||||
return v.ValidatingWebhook, true
|
||||
}
|
||||
10
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/statuserror.go
generated
vendored
10
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/statuserror.go
generated
vendored
|
|
@ -18,6 +18,7 @@ package errors
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -32,6 +33,15 @@ func ToStatusErr(webhookName string, result *metav1.Status) *apierrors.StatusErr
|
|||
result = &metav1.Status{Status: metav1.StatusFailure}
|
||||
}
|
||||
|
||||
// Make sure we don't return < 400 status codes along with a rejection
|
||||
if result.Code < http.StatusBadRequest {
|
||||
result.Code = http.StatusBadRequest
|
||||
}
|
||||
// Make sure we don't return "" or "Success" status along with a rejection
|
||||
if result.Status == "" || result.Status == metav1.StatusSuccess {
|
||||
result.Status = metav1.StatusFailure
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(result.Message) > 0:
|
||||
result.Message = fmt.Sprintf("%s: %s", deniedBy, result.Message)
|
||||
|
|
|
|||
69
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/conversion.go
generated
vendored
69
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/conversion.go
generated
vendored
|
|
@ -41,3 +41,72 @@ func ConvertToGVK(obj runtime.Object, gvk schema.GroupVersionKind, o admission.O
|
|||
out.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NewVersionedAttributes returns versioned attributes with the old and new object (if non-nil) converted to the requested kind
|
||||
func NewVersionedAttributes(attr admission.Attributes, gvk schema.GroupVersionKind, o admission.ObjectInterfaces) (*VersionedAttributes, error) {
|
||||
// convert the old and new objects to the requested version
|
||||
versionedAttr := &VersionedAttributes{
|
||||
Attributes: attr,
|
||||
VersionedKind: gvk,
|
||||
}
|
||||
if oldObj := attr.GetOldObject(); oldObj != nil {
|
||||
out, err := ConvertToGVK(oldObj, gvk, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionedAttr.VersionedOldObject = out
|
||||
}
|
||||
if obj := attr.GetObject(); obj != nil {
|
||||
out, err := ConvertToGVK(obj, gvk, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionedAttr.VersionedObject = out
|
||||
}
|
||||
return versionedAttr, nil
|
||||
}
|
||||
|
||||
// ConvertVersionedAttributes converts VersionedObject and VersionedOldObject to the specified kind, if needed.
|
||||
// If attr.VersionedKind already matches the requested kind, no conversion is performed.
|
||||
// If conversion is required:
|
||||
// * attr.VersionedObject is used as the source for the new object if Dirty=true (and is round-tripped through attr.Attributes.Object, clearing Dirty in the process)
|
||||
// * attr.Attributes.Object is used as the source for the new object if Dirty=false
|
||||
// * attr.Attributes.OldObject is used as the source for the old object
|
||||
func ConvertVersionedAttributes(attr *VersionedAttributes, gvk schema.GroupVersionKind, o admission.ObjectInterfaces) error {
|
||||
// we already have the desired kind, we're done
|
||||
if attr.VersionedKind == gvk {
|
||||
return nil
|
||||
}
|
||||
|
||||
// convert the original old object to the desired GVK
|
||||
if oldObj := attr.Attributes.GetOldObject(); oldObj != nil {
|
||||
out, err := ConvertToGVK(oldObj, gvk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attr.VersionedOldObject = out
|
||||
}
|
||||
|
||||
if attr.VersionedObject != nil {
|
||||
// convert the existing versioned object to internal
|
||||
if attr.Dirty {
|
||||
err := o.GetObjectConvertor().Convert(attr.VersionedObject, attr.Attributes.GetObject(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// and back to external
|
||||
out, err := ConvertToGVK(attr.Attributes.GetObject(), gvk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attr.VersionedObject = out
|
||||
}
|
||||
|
||||
// Remember we converted to this version
|
||||
attr.VersionedKind = gvk
|
||||
attr.Dirty = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
40
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go
generated
vendored
40
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go
generated
vendored
|
|
@ -19,27 +19,57 @@ package generic
|
|||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
)
|
||||
|
||||
// Source can list dynamic webhook plugins.
|
||||
type Source interface {
|
||||
Webhooks() []v1beta1.Webhook
|
||||
Webhooks() []webhook.WebhookAccessor
|
||||
HasSynced() bool
|
||||
}
|
||||
|
||||
// VersionedAttributes is a wrapper around the original admission attributes, adding versioned
|
||||
// variants of the object and old object.
|
||||
type VersionedAttributes struct {
|
||||
// Attributes holds the original admission attributes
|
||||
admission.Attributes
|
||||
// VersionedOldObject holds Attributes.OldObject (if non-nil), converted to VersionedKind.
|
||||
// It must never be mutated.
|
||||
VersionedOldObject runtime.Object
|
||||
VersionedObject runtime.Object
|
||||
// VersionedObject holds Attributes.Object (if non-nil), converted to VersionedKind.
|
||||
// If mutated, Dirty must be set to true by the mutator.
|
||||
VersionedObject runtime.Object
|
||||
// VersionedKind holds the fully qualified kind
|
||||
VersionedKind schema.GroupVersionKind
|
||||
// Dirty indicates VersionedObject has been modified since being converted from Attributes.Object
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// GetObject overrides the Attributes.GetObject()
|
||||
func (v *VersionedAttributes) GetObject() runtime.Object {
|
||||
if v.VersionedObject != nil {
|
||||
return v.VersionedObject
|
||||
}
|
||||
return v.Attributes.GetObject()
|
||||
}
|
||||
|
||||
// WebhookInvocation describes how to call a webhook, including the resource and subresource the webhook registered for,
|
||||
// and the kind that should be sent to the webhook.
|
||||
type WebhookInvocation struct {
|
||||
Webhook webhook.WebhookAccessor
|
||||
Resource schema.GroupVersionResource
|
||||
Subresource string
|
||||
Kind schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// Dispatcher dispatches webhook call to a list of webhooks with admission attributes as argument.
|
||||
type Dispatcher interface {
|
||||
// Dispatch a request to the webhooks using the given webhooks. A non-nil error means the request is rejected.
|
||||
Dispatch(ctx context.Context, a *VersionedAttributes, o admission.ObjectInterfaces, hooks []*v1beta1.Webhook) error
|
||||
// Dispatch a request to the webhooks. Dispatcher may choose not to
|
||||
// call a hook, either because the rules of the hook does not match, or
|
||||
// the namespaceSelector or the objectSelector of the hook does not
|
||||
// match. A non-nil error means the request is rejected.
|
||||
Dispatch(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error
|
||||
}
|
||||
|
|
|
|||
128
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
generated
vendored
128
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
generated
vendored
|
|
@ -24,12 +24,15 @@ import (
|
|||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
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/util/webhook"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
|
@ -41,8 +44,9 @@ type Webhook struct {
|
|||
sourceFactory sourceFactory
|
||||
|
||||
hookSource Source
|
||||
clientManager *webhook.ClientManager
|
||||
clientManager *webhookutil.ClientManager
|
||||
namespaceMatcher *namespace.Matcher
|
||||
objectMatcher *object.Matcher
|
||||
dispatcher Dispatcher
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +56,7 @@ var (
|
|||
)
|
||||
|
||||
type sourceFactory func(f informers.SharedInformerFactory) Source
|
||||
type dispatcherFactory func(cm *webhook.ClientManager) Dispatcher
|
||||
type dispatcherFactory func(cm *webhookutil.ClientManager) Dispatcher
|
||||
|
||||
// NewWebhook creates a new generic admission webhook.
|
||||
func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
|
||||
|
|
@ -61,23 +65,24 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cm, err := webhook.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
||||
cm, err := webhookutil.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authInfoResolver, err := webhook.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
||||
authInfoResolver, err := webhookutil.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Set defaults which may be overridden later.
|
||||
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
||||
cm.SetServiceResolver(webhook.NewDefaultServiceResolver())
|
||||
cm.SetServiceResolver(webhookutil.NewDefaultServiceResolver())
|
||||
|
||||
return &Webhook{
|
||||
Handler: handler,
|
||||
sourceFactory: sourceFactory,
|
||||
clientManager: &cm,
|
||||
namespaceMatcher: &namespace.Matcher{},
|
||||
objectMatcher: &object.Matcher{},
|
||||
dispatcher: dispatcherFactory(&cm),
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -85,13 +90,13 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
|||
// SetAuthenticationInfoResolverWrapper sets the
|
||||
// AuthenticationInfoResolverWrapper.
|
||||
// TODO find a better way wire this, but keep this pull small for now.
|
||||
func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper) {
|
||||
func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhookutil.AuthenticationInfoResolverWrapper) {
|
||||
a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper)
|
||||
}
|
||||
|
||||
// SetServiceResolver sets a service resolver for the webhook admission plugin.
|
||||
// Passing a nil resolver does not have an effect, instead a default one will be used.
|
||||
func (a *Webhook) SetServiceResolver(sr webhook.ServiceResolver) {
|
||||
func (a *Webhook) SetServiceResolver(sr webhookutil.ServiceResolver) {
|
||||
a.clientManager.SetServiceResolver(sr)
|
||||
}
|
||||
|
||||
|
|
@ -125,23 +130,78 @@ func (a *Webhook) ValidateInitialization() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ShouldCallHook makes a decision on whether to call the webhook or not by the attribute.
|
||||
func (a *Webhook) ShouldCallHook(h *v1beta1.Webhook, attr admission.Attributes) (bool, *apierrors.StatusError) {
|
||||
var matches bool
|
||||
for _, r := range h.Rules {
|
||||
// ShouldCallHook returns invocation details if the webhook should be called, nil if the webhook should not be called,
|
||||
// or an error if an error was encountered during evaluation.
|
||||
func (a *Webhook) ShouldCallHook(h webhook.WebhookAccessor, attr admission.Attributes, o admission.ObjectInterfaces) (*WebhookInvocation, *apierrors.StatusError) {
|
||||
var err *apierrors.StatusError
|
||||
var invocation *WebhookInvocation
|
||||
for _, r := range h.GetRules() {
|
||||
m := rules.Matcher{Rule: r, Attr: attr}
|
||||
if m.Matches() {
|
||||
matches = true
|
||||
invocation = &WebhookInvocation{
|
||||
Webhook: h,
|
||||
Resource: attr.GetResource(),
|
||||
Subresource: attr.GetSubresource(),
|
||||
Kind: attr.GetKind(),
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matches {
|
||||
return false, nil
|
||||
if invocation == nil && h.GetMatchPolicy() != nil && *h.GetMatchPolicy() == v1beta1.Equivalent {
|
||||
attrWithOverride := &attrWithResourceOverride{Attributes: attr}
|
||||
equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource())
|
||||
// honor earlier rules first
|
||||
OuterLoop:
|
||||
for _, r := range h.GetRules() {
|
||||
// see if the rule matches any of the equivalent resources
|
||||
for _, equivalent := range equivalents {
|
||||
if equivalent == attr.GetResource() {
|
||||
// exclude attr.GetResource(), which we already checked
|
||||
continue
|
||||
}
|
||||
attrWithOverride.resource = equivalent
|
||||
m := rules.Matcher{Rule: r, Attr: attrWithOverride}
|
||||
if m.Matches() {
|
||||
kind := o.GetEquivalentResourceMapper().KindFor(equivalent, attr.GetSubresource())
|
||||
if kind.Empty() {
|
||||
return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert to %v: unknown kind", equivalent))
|
||||
}
|
||||
invocation = &WebhookInvocation{
|
||||
Webhook: h,
|
||||
Resource: equivalent,
|
||||
Subresource: attr.GetSubresource(),
|
||||
Kind: kind,
|
||||
}
|
||||
break OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a.namespaceMatcher.MatchNamespaceSelector(h, attr)
|
||||
if invocation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
matches, err := a.namespaceMatcher.MatchNamespaceSelector(h, attr)
|
||||
if !matches || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches, err = a.objectMatcher.MatchObjectSelector(h, attr)
|
||||
if !matches || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return invocation, nil
|
||||
}
|
||||
|
||||
type attrWithResourceOverride struct {
|
||||
admission.Attributes
|
||||
resource schema.GroupVersionResource
|
||||
}
|
||||
|
||||
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 {
|
||||
if rules.IsWebhookConfigurationResource(attr) {
|
||||
|
|
@ -154,39 +214,5 @@ func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfac
|
|||
// TODO: Figure out if adding one second timeout make sense here.
|
||||
ctx := context.TODO()
|
||||
|
||||
var relevantHooks []*v1beta1.Webhook
|
||||
for i := range hooks {
|
||||
call, err := a.ShouldCallHook(&hooks[i], attr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if call {
|
||||
relevantHooks = append(relevantHooks, &hooks[i])
|
||||
}
|
||||
}
|
||||
|
||||
if len(relevantHooks) == 0 {
|
||||
// no matching hooks
|
||||
return nil
|
||||
}
|
||||
|
||||
// convert the object to the external version before sending it to the webhook
|
||||
versionedAttr := VersionedAttributes{
|
||||
Attributes: attr,
|
||||
}
|
||||
if oldObj := attr.GetOldObject(); oldObj != nil {
|
||||
out, err := ConvertToGVK(oldObj, attr.GetKind(), o)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
versionedAttr.VersionedOldObject = out
|
||||
}
|
||||
if obj := attr.GetObject(); obj != nil {
|
||||
out, err := ConvertToGVK(obj, attr.GetKind(), o)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
versionedAttr.VersionedObject = out
|
||||
}
|
||||
return a.dispatcher.Dispatch(ctx, &versionedAttr, o, relevantHooks)
|
||||
return a.dispatcher.Dispatch(ctx, attr, o, hooks)
|
||||
}
|
||||
|
|
|
|||
148
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
generated
vendored
148
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
generated
vendored
|
|
@ -24,6 +24,7 @@ import (
|
|||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/klog"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
|
|
@ -35,76 +36,144 @@ import (
|
|||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||
"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"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
)
|
||||
|
||||
type mutatingDispatcher struct {
|
||||
cm *webhook.ClientManager
|
||||
cm *webhookutil.ClientManager
|
||||
plugin *Plugin
|
||||
}
|
||||
|
||||
func newMutatingDispatcher(p *Plugin) func(cm *webhook.ClientManager) generic.Dispatcher {
|
||||
return func(cm *webhook.ClientManager) generic.Dispatcher {
|
||||
func newMutatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||
return func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||
return &mutatingDispatcher{cm, p}
|
||||
}
|
||||
}
|
||||
|
||||
var _ generic.Dispatcher = &mutatingDispatcher{}
|
||||
|
||||
func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.VersionedAttributes, o admission.ObjectInterfaces, relevantHooks []*v1beta1.Webhook) error {
|
||||
for _, hook := range relevantHooks {
|
||||
func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error {
|
||||
reinvokeCtx := attr.GetReinvocationContext()
|
||||
var webhookReinvokeCtx *webhookReinvokeContext
|
||||
if v := reinvokeCtx.Value(PluginName); v != nil {
|
||||
webhookReinvokeCtx = v.(*webhookReinvokeContext)
|
||||
} else {
|
||||
webhookReinvokeCtx = &webhookReinvokeContext{}
|
||||
reinvokeCtx.SetValue(PluginName, webhookReinvokeCtx)
|
||||
}
|
||||
|
||||
if reinvokeCtx.IsReinvoke() && webhookReinvokeCtx.IsOutputChangedSinceLastWebhookInvocation(attr.GetObject()) {
|
||||
// If the object has changed, we know the in-tree plugin re-invocations have mutated the object,
|
||||
// and we need to reinvoke all eligible webhooks.
|
||||
webhookReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
|
||||
}
|
||||
defer func() {
|
||||
webhookReinvokeCtx.SetLastWebhookInvocationOutput(attr.GetObject())
|
||||
}()
|
||||
var versionedAttr *generic.VersionedAttributes
|
||||
for _, hook := range hooks {
|
||||
attrForCheck := attr
|
||||
if versionedAttr != nil {
|
||||
attrForCheck = versionedAttr
|
||||
}
|
||||
invocation, statusErr := a.plugin.ShouldCallHook(hook, attrForCheck, o)
|
||||
if statusErr != nil {
|
||||
return statusErr
|
||||
}
|
||||
if invocation == nil {
|
||||
continue
|
||||
}
|
||||
hook, ok := invocation.Webhook.GetMutatingWebhook()
|
||||
if !ok {
|
||||
return fmt.Errorf("mutating webhook dispatch requires v1beta1.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
|
||||
// skipped in the first round because of mismatching labels,
|
||||
// even if the labels become matching, the webhook does not
|
||||
// get called during reinvocation.
|
||||
if reinvokeCtx.IsReinvoke() && !webhookReinvokeCtx.ShouldReinvokeWebhook(invocation.Webhook.GetUID()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if versionedAttr == nil {
|
||||
// First webhook, create versioned attributes
|
||||
var err error
|
||||
if versionedAttr, err = generic.NewVersionedAttributes(attr, invocation.Kind, o); err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
} else {
|
||||
// Subsequent webhook, convert existing versioned attributes to this webhook's version
|
||||
if err := generic.ConvertVersionedAttributes(versionedAttr, invocation.Kind, o); err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
err := a.callAttrMutatingHook(ctx, hook, attr, o)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, attr.Attributes, "admit", hook.Name)
|
||||
|
||||
changed, err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, 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 {
|
||||
webhookReinvokeCtx.AddReinvocableWebhookToPreviouslyInvoked(invocation.Webhook.GetUID())
|
||||
}
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
||||
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
|
||||
}
|
||||
klog.Warningf("Failed calling webhook, failing closed %v: %v", hook.Name, err)
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
return apierrors.NewInternalError(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// convert attr.VersionedObject to the internal version in the underlying admission.Attributes
|
||||
if attr.VersionedObject != nil {
|
||||
return o.GetObjectConvertor().Convert(attr.VersionedObject, attr.Attributes.GetObject(), nil)
|
||||
// convert versionedAttr.VersionedObject to the internal version in the underlying admission.Attributes
|
||||
if versionedAttr != nil && versionedAttr.VersionedObject != nil && versionedAttr.Dirty {
|
||||
return o.GetObjectConvertor().Convert(versionedAttr.VersionedObject, versionedAttr.Attributes.GetObject(), nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// note that callAttrMutatingHook updates attr
|
||||
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) error {
|
||||
if attr.IsDryRun() {
|
||||
|
||||
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) (bool, error) {
|
||||
if attr.Attributes.IsDryRun() {
|
||||
if h.SideEffects == nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
}
|
||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||
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, h) {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")}
|
||||
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)
|
||||
client, err := a.cm.HookClient(util.HookClientConfigForWebhook(h))
|
||||
request := request.CreateAdmissionReview(attr, invocation)
|
||||
client, err := a.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook))
|
||||
if err != nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
response := &admissionv1beta1.AdmissionReview{}
|
||||
r := client.Post().Context(ctx).Body(&request)
|
||||
|
|
@ -112,49 +181,49 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
|
|||
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||
}
|
||||
if err := r.Do().Into(response); err != nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
|
||||
if response.Response == nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
}
|
||||
|
||||
for k, v := range response.Response.AuditAnnotations {
|
||||
key := h.Name + "/" + k
|
||||
if err := attr.AddAnnotation(key, v); err != nil {
|
||||
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 webhookerrors.ToStatusErr(h.Name, response.Response.Result)
|
||||
return false, webhookerrors.ToStatusErr(h.Name, response.Response.Result)
|
||||
}
|
||||
|
||||
patchJS := response.Response.Patch
|
||||
if len(patchJS) == 0 {
|
||||
return nil
|
||||
return false, nil
|
||||
}
|
||||
patchObj, err := jsonpatch.DecodePatch(patchJS)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
if len(patchObj) == 0 {
|
||||
return nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// if a non-empty patch was provided, and we have no object we can apply it to (e.g. a DELETE admission operation), error
|
||||
if attr.VersionedObject == nil {
|
||||
return apierrors.NewInternalError(fmt.Errorf("admission webhook %q attempted to modify the object, which is not supported for this operation", h.Name))
|
||||
return false, apierrors.NewInternalError(fmt.Errorf("admission webhook %q attempted to modify the object, which is not supported for this operation", h.Name))
|
||||
}
|
||||
|
||||
jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), false)
|
||||
objJS, err := runtime.Encode(jsonSerializer, attr.VersionedObject)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
patchedJS, err := patchObj.Apply(objJS)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
||||
var newVersionedObject runtime.Object
|
||||
|
|
@ -163,17 +232,22 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
|
|||
// They are represented as Unstructured.
|
||||
newVersionedObject = &unstructured.Unstructured{}
|
||||
} else {
|
||||
newVersionedObject, err = o.GetObjectCreater().New(attr.GetKind())
|
||||
newVersionedObject, err = o.GetObjectCreater().New(attr.VersionedKind)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: if we have multiple mutating webhooks, we can remember the json
|
||||
// instead of encoding and decoding for each one.
|
||||
if _, _, err := jsonSerializer.Decode(patchedJS, nil, newVersionedObject); err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
if newVersionedObject, _, err = jsonSerializer.Decode(patchedJS, nil, newVersionedObject); err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
|
||||
changed := !apiequality.Semantic.DeepEqual(attr.VersionedObject, newVersionedObject)
|
||||
|
||||
attr.Dirty = true
|
||||
attr.VersionedObject = newVersionedObject
|
||||
o.GetObjectDefaulter().Default(attr.VersionedObject)
|
||||
return nil
|
||||
return changed, nil
|
||||
}
|
||||
|
|
|
|||
68
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go
generated
vendored
Normal file
68
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
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 mutating
|
||||
|
||||
import (
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
type webhookReinvokeContext struct {
|
||||
// lastWebhookOutput holds the result of the last webhook admission plugin call
|
||||
lastWebhookOutput runtime.Object
|
||||
// previouslyInvokedReinvocableWebhooks holds the set of webhooks that have been invoked and
|
||||
// should be reinvoked if a later mutation occurs
|
||||
previouslyInvokedReinvocableWebhooks sets.String
|
||||
// reinvokeWebhooks holds the set of webhooks that should be reinvoked
|
||||
reinvokeWebhooks sets.String
|
||||
}
|
||||
|
||||
func (rc *webhookReinvokeContext) ShouldReinvokeWebhook(webhook string) bool {
|
||||
return rc.reinvokeWebhooks.Has(webhook)
|
||||
}
|
||||
|
||||
func (rc *webhookReinvokeContext) IsOutputChangedSinceLastWebhookInvocation(object runtime.Object) bool {
|
||||
return !apiequality.Semantic.DeepEqual(rc.lastWebhookOutput, object)
|
||||
}
|
||||
|
||||
func (rc *webhookReinvokeContext) SetLastWebhookInvocationOutput(object runtime.Object) {
|
||||
if object == nil {
|
||||
rc.lastWebhookOutput = nil
|
||||
return
|
||||
}
|
||||
rc.lastWebhookOutput = object.DeepCopyObject()
|
||||
}
|
||||
|
||||
func (rc *webhookReinvokeContext) AddReinvocableWebhookToPreviouslyInvoked(webhook string) {
|
||||
if rc.previouslyInvokedReinvocableWebhooks == nil {
|
||||
rc.previouslyInvokedReinvocableWebhooks = sets.NewString()
|
||||
}
|
||||
rc.previouslyInvokedReinvocableWebhooks.Insert(webhook)
|
||||
}
|
||||
|
||||
func (rc *webhookReinvokeContext) RequireReinvokingPreviouslyInvokedPlugins() {
|
||||
if len(rc.previouslyInvokedReinvocableWebhooks) > 0 {
|
||||
if rc.reinvokeWebhooks == nil {
|
||||
rc.reinvokeWebhooks = sets.NewString()
|
||||
}
|
||||
for s := range rc.previouslyInvokedReinvocableWebhooks {
|
||||
rc.reinvokeWebhooks.Insert(s)
|
||||
}
|
||||
rc.previouslyInvokedReinvocableWebhooks = sets.NewString()
|
||||
}
|
||||
}
|
||||
18
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
generated
vendored
18
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
generated
vendored
|
|
@ -19,13 +19,13 @@ package namespace
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
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"
|
||||
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"
|
||||
)
|
||||
|
|
@ -86,7 +86,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 *v1beta1.Webhook, attr admission.Attributes) (bool, *apierrors.StatusError) {
|
||||
func (m *Matcher) MatchNamespaceSelector(h webhook.WebhookAccessor, 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
|
||||
|
|
@ -95,6 +95,15 @@ func (m *Matcher) MatchNamespaceSelector(h *v1beta1.Webhook, attr admission.Attr
|
|||
// 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())
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
if selector.Empty() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
namespaceLabels, err := m.GetNamespaceLabels(attr)
|
||||
// this means the namespace is not found, for backwards compatibility,
|
||||
// return a 404
|
||||
|
|
@ -108,10 +117,5 @@ func (m *Matcher) MatchNamespaceSelector(h *v1beta1.Webhook, attr admission.Attr
|
|||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
// TODO: adding an LRU cache to cache the translation
|
||||
selector, err := metav1.LabelSelectorAsSelector(h.NamespaceSelector)
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
return selector.Matches(labels.Set(namespaceLabels)), nil
|
||||
}
|
||||
|
|
|
|||
20
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
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 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"
|
||||
59
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go
generated
vendored
Normal file
59
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
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 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"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// Matcher decides if a request selected by the ObjectSelector.
|
||||
type Matcher struct {
|
||||
}
|
||||
|
||||
func matchObject(obj runtime.Object, selector labels.Selector) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
klog.V(5).Infof("cannot access metadata of %v: %v", obj, err)
|
||||
return false
|
||||
}
|
||||
return selector.Matches(labels.Set(accessor.GetLabels()))
|
||||
|
||||
}
|
||||
|
||||
// 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())
|
||||
if err != nil {
|
||||
return false, apierrors.NewInternalError(err)
|
||||
}
|
||||
if selector.Empty() {
|
||||
return true, nil
|
||||
}
|
||||
return matchObject(attr.GetObject(), selector) || matchObject(attr.GetOldObject(), selector), nil
|
||||
}
|
||||
39
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
generated
vendored
39
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
generated
vendored
|
|
@ -26,9 +26,14 @@ import (
|
|||
)
|
||||
|
||||
// CreateAdmissionReview creates an AdmissionReview for the provided admission.Attributes
|
||||
func CreateAdmissionReview(attr *generic.VersionedAttributes) admissionv1beta1.AdmissionReview {
|
||||
gvk := attr.GetKind()
|
||||
gvr := attr.GetResource()
|
||||
func CreateAdmissionReview(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),
|
||||
|
|
@ -56,18 +61,32 @@ func CreateAdmissionReview(attr *generic.VersionedAttributes) admissionv1beta1.A
|
|||
Resource: gvr.Resource,
|
||||
Version: gvr.Version,
|
||||
},
|
||||
SubResource: attr.GetSubresource(),
|
||||
Name: attr.GetName(),
|
||||
Namespace: attr.GetNamespace(),
|
||||
Operation: admissionv1beta1.Operation(attr.GetOperation()),
|
||||
UserInfo: userInfo,
|
||||
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: admissionv1beta1.Operation(attr.GetOperation()),
|
||||
UserInfo: userInfo,
|
||||
Object: runtime.RawExtension{
|
||||
Object: attr.VersionedObject,
|
||||
Object: versionedAttributes.VersionedObject,
|
||||
},
|
||||
OldObject: runtime.RawExtension{
|
||||
Object: attr.VersionedOldObject,
|
||||
Object: versionedAttributes.VersionedOldObject,
|
||||
},
|
||||
DryRun: &dryRun,
|
||||
Options: runtime.RawExtension{
|
||||
Object: attr.GetOperationOptions(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
40
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go
generated
vendored
40
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go
generated
vendored
|
|
@ -17,33 +17,39 @@ limitations under the License.
|
|||
package util
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
)
|
||||
|
||||
// HookClientConfigForWebhook construct a webhook.ClientConfig using a v1beta1.Webhook API object.
|
||||
// webhook.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 *v1beta1.Webhook) webhook.ClientConfig {
|
||||
ret := webhook.ClientConfig{Name: w.Name, CABundle: w.ClientConfig.CABundle}
|
||||
if w.ClientConfig.URL != nil {
|
||||
ret.URL = *w.ClientConfig.URL
|
||||
// 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.ClientConfig.Service != nil {
|
||||
ret.Service = &webhook.ClientConfigService{
|
||||
Name: w.ClientConfig.Service.Name,
|
||||
Namespace: w.ClientConfig.Service.Namespace,
|
||||
if w.GetClientConfig().Service != nil {
|
||||
ret.Service = &webhookutil.ClientConfigService{
|
||||
Name: w.GetClientConfig().Service.Name,
|
||||
Namespace: w.GetClientConfig().Service.Namespace,
|
||||
}
|
||||
if w.ClientConfig.Service.Path != nil {
|
||||
ret.Service.Path = *w.ClientConfig.Service.Path
|
||||
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 *v1beta1.Webhook) bool {
|
||||
for _, b := range w.AdmissionReviewVersions {
|
||||
func HasAdmissionReviewVersion(a string, w webhook.WebhookAccessor) bool {
|
||||
for _, b := range w.GetAdmissionReviewVersions() {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
83
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
generated
vendored
83
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
generated
vendored
|
|
@ -22,48 +22,85 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||
"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"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
type validatingDispatcher struct {
|
||||
cm *webhook.ClientManager
|
||||
cm *webhookutil.ClientManager
|
||||
plugin *Plugin
|
||||
}
|
||||
|
||||
func newValidatingDispatcher(cm *webhook.ClientManager) generic.Dispatcher {
|
||||
return &validatingDispatcher{cm}
|
||||
func newValidatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||
return func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||
return &validatingDispatcher{cm, p}
|
||||
}
|
||||
}
|
||||
|
||||
var _ generic.Dispatcher = &validatingDispatcher{}
|
||||
|
||||
func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.VersionedAttributes, o admission.ObjectInterfaces, relevantHooks []*v1beta1.Webhook) error {
|
||||
func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error {
|
||||
var relevantHooks []*generic.WebhookInvocation
|
||||
// Construct all the versions we need to call our webhooks
|
||||
versionedAttrs := map[schema.GroupVersionKind]*generic.VersionedAttributes{}
|
||||
for _, hook := range hooks {
|
||||
invocation, statusError := d.plugin.ShouldCallHook(hook, attr, o)
|
||||
if statusError != nil {
|
||||
return statusError
|
||||
}
|
||||
if invocation == nil {
|
||||
continue
|
||||
}
|
||||
relevantHooks = append(relevantHooks, invocation)
|
||||
// If we already have this version, continue
|
||||
if _, ok := versionedAttrs[invocation.Kind]; ok {
|
||||
continue
|
||||
}
|
||||
versionedAttr, err := generic.NewVersionedAttributes(attr, invocation.Kind, o)
|
||||
if err != nil {
|
||||
return apierrors.NewInternalError(err)
|
||||
}
|
||||
versionedAttrs[invocation.Kind] = versionedAttr
|
||||
}
|
||||
|
||||
if len(relevantHooks) == 0 {
|
||||
// no matching hooks
|
||||
return nil
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
errCh := make(chan error, len(relevantHooks))
|
||||
wg.Add(len(relevantHooks))
|
||||
for i := range relevantHooks {
|
||||
go func(hook *v1beta1.Webhook) {
|
||||
go func(invocation *generic.WebhookInvocation) {
|
||||
defer wg.Done()
|
||||
|
||||
hook, ok := invocation.Webhook.GetValidatingWebhook()
|
||||
if !ok {
|
||||
utilruntime.HandleError(fmt.Errorf("validating webhook dispatch requires v1beta1.ValidatingWebhook, but got %T", hook))
|
||||
return
|
||||
}
|
||||
versionedAttr := versionedAttrs[invocation.Kind]
|
||||
t := time.Now()
|
||||
err := d.callHook(ctx, hook, attr)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, attr.Attributes, "validating", hook.Name)
|
||||
err := d.callHook(ctx, hook, invocation, versionedAttr)
|
||||
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "validating", hook.Name)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
||||
if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok {
|
||||
if ignoreClientCallFailures {
|
||||
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||
utilruntime.HandleError(callErr)
|
||||
|
|
@ -98,10 +135,10 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.Versi
|
|||
return errs[0]
|
||||
}
|
||||
|
||||
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
|
||||
if attr.IsDryRun() {
|
||||
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.ValidatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error {
|
||||
if attr.Attributes.IsDryRun() {
|
||||
if h.SideEffects == nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||
}
|
||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||
|
|
@ -110,15 +147,15 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook,
|
|||
|
||||
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
||||
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
||||
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, h) {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")}
|
||||
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)
|
||||
client, err := d.cm.HookClient(util.HookClientConfigForWebhook(h))
|
||||
request := request.CreateAdmissionReview(attr, invocation)
|
||||
client, err := d.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook))
|
||||
if err != nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
response := &admissionv1beta1.AdmissionReview{}
|
||||
r := client.Post().Context(ctx).Body(&request)
|
||||
|
|
@ -126,15 +163,15 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook,
|
|||
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||
}
|
||||
if err := r.Do().Into(response); err != nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||
}
|
||||
|
||||
if response.Response == nil {
|
||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||
}
|
||||
for k, v := range response.Response.AuditAnnotations {
|
||||
key := h.Name + "/" + k
|
||||
if err := attr.AddAnnotation(key, v); err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
generated
vendored
6
vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
generated
vendored
|
|
@ -51,11 +51,13 @@ var _ admission.ValidationInterface = &Plugin{}
|
|||
// NewValidatingAdmissionWebhook returns a generic admission webhook plugin.
|
||||
func NewValidatingAdmissionWebhook(configFile io.Reader) (*Plugin, error) {
|
||||
handler := admission.NewHandler(admission.Connect, admission.Create, admission.Delete, admission.Update)
|
||||
webhook, err := generic.NewWebhook(handler, configFile, configuration.NewValidatingWebhookConfigurationManager, newValidatingDispatcher)
|
||||
p := &Plugin{}
|
||||
var err error
|
||||
p.Webhook, err = generic.NewWebhook(handler, configFile, configuration.NewValidatingWebhookConfigurationManager, newValidatingDispatcher(p))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Plugin{webhook}, nil
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Validate makes an admission decision based on the request attributes.
|
||||
|
|
|
|||
2
vendor/k8s.io/apiserver/pkg/admission/plugins.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/admission/plugins.go
generated
vendored
|
|
@ -160,7 +160,7 @@ func (ps *Plugins) NewFromPlugins(pluginNames []string, configProvider ConfigPro
|
|||
if len(validationPlugins) != 0 {
|
||||
klog.Infof("Loaded %d validating admission controller(s) successfully in the following order: %s.", len(validationPlugins), strings.Join(validationPlugins, ","))
|
||||
}
|
||||
return chainAdmissionHandler(handlers), nil
|
||||
return newReinvocationHandler(chainAdmissionHandler(handlers)), nil
|
||||
}
|
||||
|
||||
// InitPlugin creates an instance of the named interface.
|
||||
|
|
|
|||
62
vendor/k8s.io/apiserver/pkg/admission/reinvocation.go
generated
vendored
Normal file
62
vendor/k8s.io/apiserver/pkg/admission/reinvocation.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
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 admission
|
||||
|
||||
// newReinvocationHandler creates a handler that wraps the provided admission chain and reinvokes it
|
||||
// if needed according to re-invocation policy of the webhooks.
|
||||
func newReinvocationHandler(admissionChain Interface) Interface {
|
||||
return &reinvoker{admissionChain}
|
||||
}
|
||||
|
||||
type reinvoker struct {
|
||||
admissionChain Interface
|
||||
}
|
||||
|
||||
// Admit performs an admission control check using the wrapped admission chain, reinvoking the
|
||||
// admission chain if needed according to the reinvocation policy. Plugins are expected to check
|
||||
// the admission attributes' reinvocation context against their reinvocation policy to decide if
|
||||
// they should re-run, and to update the reinvocation context if they perform any mutations.
|
||||
func (r *reinvoker) Admit(a Attributes, o ObjectInterfaces) error {
|
||||
if mutator, ok := r.admissionChain.(MutationInterface); ok {
|
||||
err := mutator.Admit(a, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s := a.GetReinvocationContext()
|
||||
if s.ShouldReinvoke() {
|
||||
s.SetIsReinvoke()
|
||||
// Calling admit a second time will reinvoke all in-tree plugins
|
||||
// as well as any webhook plugins that need to be reinvoked based on the
|
||||
// reinvocation policy.
|
||||
return mutator.Admit(a, o)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate performs an admission control check using the wrapped admission chain, and returns immediately on first error.
|
||||
func (r *reinvoker) Validate(a Attributes, o ObjectInterfaces) error {
|
||||
if validator, ok := r.admissionChain.(ValidationInterface); ok {
|
||||
return validator.Validate(a, o)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handles will return true if any of the admission chain handlers handle the given operation.
|
||||
func (r *reinvoker) Handles(operation Operation) bool {
|
||||
return r.admissionChain.Handles(operation)
|
||||
}
|
||||
31
vendor/k8s.io/apiserver/pkg/admission/util.go
generated
vendored
31
vendor/k8s.io/apiserver/pkg/admission/util.go
generated
vendored
|
|
@ -18,11 +18,30 @@ package admission
|
|||
|
||||
import "k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
type SchemeBasedObjectInterfaces struct {
|
||||
Scheme *runtime.Scheme
|
||||
type RuntimeObjectInterfaces struct {
|
||||
runtime.ObjectCreater
|
||||
runtime.ObjectTyper
|
||||
runtime.ObjectDefaulter
|
||||
runtime.ObjectConvertor
|
||||
runtime.EquivalentResourceMapper
|
||||
}
|
||||
|
||||
func (r *SchemeBasedObjectInterfaces) GetObjectCreater() runtime.ObjectCreater { return r.Scheme }
|
||||
func (r *SchemeBasedObjectInterfaces) GetObjectTyper() runtime.ObjectTyper { return r.Scheme }
|
||||
func (r *SchemeBasedObjectInterfaces) GetObjectDefaulter() runtime.ObjectDefaulter { return r.Scheme }
|
||||
func (r *SchemeBasedObjectInterfaces) GetObjectConvertor() runtime.ObjectConvertor { return r.Scheme }
|
||||
func NewObjectInterfacesFromScheme(scheme *runtime.Scheme) ObjectInterfaces {
|
||||
return &RuntimeObjectInterfaces{scheme, scheme, scheme, scheme, runtime.NewEquivalentResourceRegistry()}
|
||||
}
|
||||
|
||||
func (r *RuntimeObjectInterfaces) GetObjectCreater() runtime.ObjectCreater {
|
||||
return r.ObjectCreater
|
||||
}
|
||||
func (r *RuntimeObjectInterfaces) GetObjectTyper() runtime.ObjectTyper {
|
||||
return r.ObjectTyper
|
||||
}
|
||||
func (r *RuntimeObjectInterfaces) GetObjectDefaulter() runtime.ObjectDefaulter {
|
||||
return r.ObjectDefaulter
|
||||
}
|
||||
func (r *RuntimeObjectInterfaces) GetObjectConvertor() runtime.ObjectConvertor {
|
||||
return r.ObjectConvertor
|
||||
}
|
||||
func (r *RuntimeObjectInterfaces) GetEquivalentResourceMapper() runtime.EquivalentResourceMapper {
|
||||
return r.EquivalentResourceMapper
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue