git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server
subrepo:
subdir: "metrics-server"
merged: "92d8412"
upstream:
origin: "https://github.com/kubernetes-incubator/metrics-server.git"
branch: "master"
commit: "92d8412"
git-subrepo:
version: "0.4.0"
origin: "???"
commit: "???"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/attributes.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/attributes.go
new file mode 100644
index 0000000..7272e88
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/attributes.go
@@ -0,0 +1,140 @@
+/*
+Copyright 2014 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
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apiserver/pkg/authentication/user"
+)
+
+type attributesRecord struct {
+ kind schema.GroupVersionKind
+ namespace string
+ name string
+ resource schema.GroupVersionResource
+ subresource string
+ operation Operation
+ object runtime.Object
+ oldObject runtime.Object
+ userInfo user.Info
+
+ // other elements are always accessed in single goroutine.
+ // But ValidatingAdmissionWebhook add annotations concurrently.
+ annotations map[string]string
+ annotationsLock sync.RWMutex
+}
+
+func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, userInfo user.Info) Attributes {
+ return &attributesRecord{
+ kind: kind,
+ namespace: namespace,
+ name: name,
+ resource: resource,
+ subresource: subresource,
+ operation: operation,
+ object: object,
+ oldObject: oldObject,
+ userInfo: userInfo,
+ }
+}
+
+func (record *attributesRecord) GetKind() schema.GroupVersionKind {
+ return record.kind
+}
+
+func (record *attributesRecord) GetNamespace() string {
+ return record.namespace
+}
+
+func (record *attributesRecord) GetName() string {
+ return record.name
+}
+
+func (record *attributesRecord) GetResource() schema.GroupVersionResource {
+ return record.resource
+}
+
+func (record *attributesRecord) GetSubresource() string {
+ return record.subresource
+}
+
+func (record *attributesRecord) GetOperation() Operation {
+ return record.operation
+}
+
+func (record *attributesRecord) GetObject() runtime.Object {
+ return record.object
+}
+
+func (record *attributesRecord) GetOldObject() runtime.Object {
+ return record.oldObject
+}
+
+func (record *attributesRecord) GetUserInfo() user.Info {
+ return record.userInfo
+}
+
+// getAnnotations implements privateAnnotationsGetter.It's a private method used
+// by WithAudit decorator.
+func (record *attributesRecord) getAnnotations() map[string]string {
+ record.annotationsLock.RLock()
+ defer record.annotationsLock.RUnlock()
+
+ if record.annotations == nil {
+ return nil
+ }
+ cp := make(map[string]string, len(record.annotations))
+ for key, value := range record.annotations {
+ cp[key] = value
+ }
+ return cp
+}
+
+func (record *attributesRecord) AddAnnotation(key, value string) error {
+ if err := checkKeyFormat(key); err != nil {
+ return err
+ }
+
+ record.annotationsLock.Lock()
+ defer record.annotationsLock.Unlock()
+
+ if record.annotations == nil {
+ record.annotations = make(map[string]string)
+ }
+ if v, ok := record.annotations[key]; ok && v != value {
+ return fmt.Errorf("admission annotations are not allowd to be overwritten, key:%q, old value: %q, new value:%q", key, record.annotations[key], value)
+ }
+ record.annotations[key] = value
+ return nil
+}
+
+func checkKeyFormat(key string) error {
+ parts := strings.Split(key, "/")
+ if len(parts) != 2 {
+ return fmt.Errorf("annotation key has invalid format, the right format is a DNS subdomain prefix and '/' and key name. (e.g. 'podsecuritypolicy.admission.k8s.io/admit-policy')")
+ }
+ if msgs := validation.IsQualifiedName(key); len(msgs) != 0 {
+ return fmt.Errorf("annotation key has invalid format %s. A qualified name like 'podsecuritypolicy.admission.k8s.io/admit-policy' is required.", strings.Join(msgs, ","))
+ }
+ return nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/audit.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/audit.go
new file mode 100644
index 0000000..13d86b3
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/audit.go
@@ -0,0 +1,95 @@
+/*
+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 admission
+
+import (
+ "fmt"
+
+ auditinternal "k8s.io/apiserver/pkg/apis/audit"
+ "k8s.io/apiserver/pkg/audit"
+)
+
+// auditHandler logs annotations set by other admission handlers
+type auditHandler struct {
+ Interface
+ ae *auditinternal.Event
+}
+
+var _ Interface = &auditHandler{}
+var _ MutationInterface = &auditHandler{}
+var _ ValidationInterface = &auditHandler{}
+
+// WithAudit is a decorator for a admission phase. It saves annotations
+// of attribute into the audit event. Attributes passed to the Admit and
+// Validate function must be instance of privateAnnotationsGetter or
+// AnnotationsGetter, otherwise an error is returned.
+func WithAudit(i Interface, ae *auditinternal.Event) Interface {
+ if i == nil {
+ return i
+ }
+ return &auditHandler{i, ae}
+}
+
+func (handler auditHandler) Admit(a Attributes) error {
+ if !handler.Interface.Handles(a.GetOperation()) {
+ return nil
+ }
+ if err := ensureAnnotationGetter(a); err != nil {
+ return err
+ }
+ var err error
+ if mutator, ok := handler.Interface.(MutationInterface); ok {
+ err = mutator.Admit(a)
+ handler.logAnnotations(a)
+ }
+ return err
+}
+
+func (handler auditHandler) Validate(a Attributes) error {
+ if !handler.Interface.Handles(a.GetOperation()) {
+ return nil
+ }
+ if err := ensureAnnotationGetter(a); err != nil {
+ return err
+ }
+ var err error
+ if validator, ok := handler.Interface.(ValidationInterface); ok {
+ err = validator.Validate(a)
+ handler.logAnnotations(a)
+ }
+ return err
+}
+
+func ensureAnnotationGetter(a Attributes) error {
+ _, okPrivate := a.(privateAnnotationsGetter)
+ _, okPublic := a.(AnnotationsGetter)
+ if okPrivate || okPublic {
+ return nil
+ }
+ return fmt.Errorf("attributes must be an instance of privateAnnotationsGetter or AnnotationsGetter")
+}
+
+func (handler auditHandler) logAnnotations(a Attributes) {
+ switch a := a.(type) {
+ case privateAnnotationsGetter:
+ audit.LogAnnotations(handler.ae, a.getAnnotations())
+ case AnnotationsGetter:
+ audit.LogAnnotations(handler.ae, a.GetAnnotations())
+ default:
+ // this will never happen, because we have already checked it in ensureAnnotationGetter
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/chain.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/chain.go
new file mode 100644
index 0000000..011641f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/chain.go
@@ -0,0 +1,68 @@
+/*
+Copyright 2014 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
+
+// chainAdmissionHandler is an instance of admission.NamedHandler that performs admission control using
+// a chain of admission handlers
+type chainAdmissionHandler []Interface
+
+// NewChainHandler creates a new chain handler from an array of handlers. Used for testing.
+func NewChainHandler(handlers ...Interface) chainAdmissionHandler {
+ return chainAdmissionHandler(handlers)
+}
+
+// Admit performs an admission control check using a chain of handlers, and returns immediately on first error
+func (admissionHandler chainAdmissionHandler) Admit(a Attributes) error {
+ for _, handler := range admissionHandler {
+ if !handler.Handles(a.GetOperation()) {
+ continue
+ }
+ if mutator, ok := handler.(MutationInterface); ok {
+ err := mutator.Admit(a)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Validate performs an admission control check using a chain of handlers, and returns immediately on first error
+func (admissionHandler chainAdmissionHandler) Validate(a Attributes) error {
+ for _, handler := range admissionHandler {
+ if !handler.Handles(a.GetOperation()) {
+ continue
+ }
+ if validator, ok := handler.(ValidationInterface); ok {
+ err := validator.Validate(a)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Handles will return true if any of the handlers handles the given operation
+func (admissionHandler chainAdmissionHandler) Handles(operation Operation) bool {
+ for _, handler := range admissionHandler {
+ if handler.Handles(operation) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/config.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/config.go
new file mode 100644
index 0000000..f59d060
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/config.go
@@ -0,0 +1,178 @@
+/*
+Copyright 2017 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
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+
+ "github.com/ghodss/yaml"
+ "github.com/golang/glog"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apiserver/pkg/apis/apiserver"
+ apiserverv1alpha1 "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
+)
+
+func makeAbs(path, base string) (string, error) {
+ if filepath.IsAbs(path) {
+ return path, nil
+ }
+ if len(base) == 0 || base == "." {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ base = cwd
+ }
+ return filepath.Join(base, path), nil
+}
+
+// ReadAdmissionConfiguration reads the admission configuration at the specified path.
+// It returns the loaded admission configuration if the input file aligns with the required syntax.
+// If it does not align with the provided syntax, it returns a default configuration for the enumerated
+// set of pluginNames whose config location references the specified configFilePath.
+// It does this to preserve backward compatibility when admission control files were opaque.
+// It returns an error if the file did not exist.
+func ReadAdmissionConfiguration(pluginNames []string, configFilePath string, configScheme *runtime.Scheme) (ConfigProvider, error) {
+ if configFilePath == "" {
+ return configProvider{config: &apiserver.AdmissionConfiguration{}}, nil
+ }
+ // a file was provided, so we just read it.
+ data, err := ioutil.ReadFile(configFilePath)
+ if err != nil {
+ return nil, fmt.Errorf("unable to read admission control configuration from %q [%v]", configFilePath, err)
+ }
+ codecs := serializer.NewCodecFactory(configScheme)
+ decoder := codecs.UniversalDecoder()
+ decodedObj, err := runtime.Decode(decoder, data)
+ // we were able to decode the file successfully
+ if err == nil {
+ decodedConfig, ok := decodedObj.(*apiserver.AdmissionConfiguration)
+ if !ok {
+ return nil, fmt.Errorf("unexpected type: %T", decodedObj)
+ }
+ baseDir := path.Dir(configFilePath)
+ for i := range decodedConfig.Plugins {
+ if decodedConfig.Plugins[i].Path == "" {
+ continue
+ }
+ // we update relative file paths to absolute paths
+ absPath, err := makeAbs(decodedConfig.Plugins[i].Path, baseDir)
+ if err != nil {
+ return nil, err
+ }
+ decodedConfig.Plugins[i].Path = absPath
+ }
+ return configProvider{
+ config: decodedConfig,
+ scheme: configScheme,
+ }, nil
+ }
+ // we got an error where the decode wasn't related to a missing type
+ if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) {
+ return nil, err
+ }
+
+ // Only tolerate load errors if the file appears to be one of the two legacy plugin configs
+ unstructuredData := map[string]interface{}{}
+ if err2 := yaml.Unmarshal(data, &unstructuredData); err2 != nil {
+ return nil, err
+ }
+ _, isLegacyImagePolicy := unstructuredData["imagePolicy"]
+ _, isLegacyPodNodeSelector := unstructuredData["podNodeSelectorPluginConfig"]
+ if !isLegacyImagePolicy && !isLegacyPodNodeSelector {
+ return nil, err
+ }
+
+ // convert the legacy format to the new admission control format
+ // in order to preserve backwards compatibility, we set plugins that
+ // previously read input from a non-versioned file configuration to the
+ // current input file.
+ legacyPluginsWithUnversionedConfig := sets.NewString("ImagePolicyWebhook", "PodNodeSelector")
+ externalConfig := &apiserverv1alpha1.AdmissionConfiguration{}
+ for _, pluginName := range pluginNames {
+ if legacyPluginsWithUnversionedConfig.Has(pluginName) {
+ externalConfig.Plugins = append(externalConfig.Plugins,
+ apiserverv1alpha1.AdmissionPluginConfiguration{
+ Name: pluginName,
+ Path: configFilePath})
+ }
+ }
+ configScheme.Default(externalConfig)
+ internalConfig := &apiserver.AdmissionConfiguration{}
+ if err := configScheme.Convert(externalConfig, internalConfig, nil); err != nil {
+ return nil, err
+ }
+ return configProvider{
+ config: internalConfig,
+ scheme: configScheme,
+ }, nil
+}
+
+type configProvider struct {
+ config *apiserver.AdmissionConfiguration
+ scheme *runtime.Scheme
+}
+
+// GetAdmissionPluginConfigurationFor returns a reader that holds the admission plugin configuration.
+func GetAdmissionPluginConfigurationFor(pluginCfg apiserver.AdmissionPluginConfiguration) (io.Reader, error) {
+ // if there is a nest object, return it directly
+ if pluginCfg.Configuration != nil {
+ return bytes.NewBuffer(pluginCfg.Configuration.Raw), nil
+ }
+ // there is nothing nested, so we delegate to path
+ if pluginCfg.Path != "" {
+ content, err := ioutil.ReadFile(pluginCfg.Path)
+ if err != nil {
+ glog.Fatalf("Couldn't open admission plugin configuration %s: %#v", pluginCfg.Path, err)
+ return nil, err
+ }
+ return bytes.NewBuffer(content), nil
+ }
+ // there is no special config at all
+ return nil, nil
+}
+
+// ConfigFor returns a reader for the specified plugin.
+// If no specific configuration is present, we return a nil reader.
+func (p configProvider) ConfigFor(pluginName string) (io.Reader, error) {
+ // there is no config, so there is no potential config
+ if p.config == nil {
+ return nil, nil
+ }
+ // look for matching plugin and get configuration
+ for _, pluginCfg := range p.config.Plugins {
+ if pluginName != pluginCfg.Name {
+ continue
+ }
+ pluginConfig, err := GetAdmissionPluginConfigurationFor(pluginCfg)
+ if err != nil {
+ return nil, err
+ }
+ return pluginConfig, nil
+ }
+ // there is no registered config that matches on plugin name.
+ return nil, nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/configuration_manager.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/configuration_manager.go
new file mode 100644
index 0000000..4c4bf74
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/configuration_manager.go
@@ -0,0 +1,166 @@
+/*
+Copyright 2017 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 configuration
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+)
+
+const (
+ defaultInterval = 1 * time.Second
+ defaultFailureThreshold = 5
+ defaultBootstrapRetries = 5
+ defaultBootstrapGraceperiod = 5 * time.Second
+)
+
+var (
+ ErrNotReady = fmt.Errorf("configuration is not ready")
+ ErrDisabled = fmt.Errorf("disabled")
+)
+
+type getFunc func() (runtime.Object, error)
+
+// When running, poller calls `get` every `interval`. If `get` is
+// successful, `Ready()` returns ready and `configuration()` returns the
+// `mergedConfiguration`; if `get` has failed more than `failureThreshold ` times,
+// `Ready()` returns not ready and `configuration()` returns nil configuration.
+// In an HA setup, the poller is consistent only if the `get` is
+// doing consistent read.
+type poller struct {
+ // a function to consistently read the latest configuration
+ get getFunc
+ // consistent read interval
+ // read-only
+ interval time.Duration
+ // if the number of consecutive read failure equals or exceeds the failureThreshold , the
+ // configuration is regarded as not ready.
+ // read-only
+ failureThreshold int
+ // number of consecutive failures so far.
+ failures int
+ // If the poller has passed the bootstrap phase. The poller is considered
+ // bootstrapped either bootstrapGracePeriod after the first call of
+ // configuration(), or when setConfigurationAndReady() is called, whichever
+ // comes first.
+ bootstrapped bool
+ // configuration() retries bootstrapRetries times if poller is not bootstrapped
+ // read-only
+ bootstrapRetries int
+ // Grace period for bootstrapping
+ // read-only
+ bootstrapGracePeriod time.Duration
+ once sync.Once
+ // if the configuration is regarded as ready.
+ ready bool
+ mergedConfiguration runtime.Object
+ lastErr error
+ // lock must be hold when reading/writing the data fields of poller.
+ lock sync.RWMutex
+}
+
+func newPoller(get getFunc) *poller {
+ p := poller{
+ get: get,
+ interval: defaultInterval,
+ failureThreshold: defaultFailureThreshold,
+ bootstrapRetries: defaultBootstrapRetries,
+ bootstrapGracePeriod: defaultBootstrapGraceperiod,
+ }
+ return &p
+}
+
+func (a *poller) lastError(err error) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+ a.lastErr = err
+}
+
+func (a *poller) notReady() {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+ a.ready = false
+}
+
+func (a *poller) bootstrapping() {
+ // bootstrapGracePeriod is read-only, so no lock is required
+ timer := time.NewTimer(a.bootstrapGracePeriod)
+ go func() {
+ defer timer.Stop()
+ <-timer.C
+ a.lock.Lock()
+ defer a.lock.Unlock()
+ a.bootstrapped = true
+ }()
+}
+
+// If the poller is not bootstrapped yet, the configuration() gets a few chances
+// to retry. This hides transient failures during system startup.
+func (a *poller) configuration() (runtime.Object, error) {
+ a.once.Do(a.bootstrapping)
+ a.lock.RLock()
+ defer a.lock.RUnlock()
+ retries := 1
+ if !a.bootstrapped {
+ retries = a.bootstrapRetries
+ }
+ for count := 0; count < retries; count++ {
+ if count > 0 {
+ a.lock.RUnlock()
+ time.Sleep(a.interval)
+ a.lock.RLock()
+ }
+ if a.ready {
+ return a.mergedConfiguration, nil
+ }
+ }
+ if a.lastErr != nil {
+ return nil, a.lastErr
+ }
+ return nil, ErrNotReady
+}
+
+func (a *poller) setConfigurationAndReady(value runtime.Object) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+ a.bootstrapped = true
+ a.mergedConfiguration = value
+ a.ready = true
+ a.lastErr = nil
+}
+
+func (a *poller) Run(stopCh <-chan struct{}) {
+ go wait.Until(a.sync, a.interval, stopCh)
+}
+
+func (a *poller) sync() {
+ configuration, err := a.get()
+ if err != nil {
+ a.failures++
+ a.lastError(err)
+ if a.failures >= a.failureThreshold {
+ a.notReady()
+ }
+ return
+ }
+ a.failures = 0
+ a.setConfigurationAndReady(configuration)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/initializer_manager.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/initializer_manager.go
new file mode 100644
index 0000000..986524b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/initializer_manager.go
@@ -0,0 +1,88 @@
+/*
+Copyright 2017 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 configuration
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+
+ "github.com/golang/glog"
+
+ "k8s.io/api/admissionregistration/v1alpha1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+type InitializerConfigurationLister interface {
+ List(opts metav1.ListOptions) (*v1alpha1.InitializerConfigurationList, error)
+}
+
+type InitializerConfigurationManager struct {
+ *poller
+}
+
+func NewInitializerConfigurationManager(c InitializerConfigurationLister) *InitializerConfigurationManager {
+ getFn := func() (runtime.Object, error) {
+ list, err := c.List(metav1.ListOptions{})
+ if err != nil {
+ if errors.IsNotFound(err) || errors.IsForbidden(err) {
+ glog.V(5).Infof("Initializers are disabled due to an error: %v", err)
+ return nil, ErrDisabled
+ }
+ return nil, err
+ }
+ return mergeInitializerConfigurations(list), nil
+ }
+ return &InitializerConfigurationManager{
+ newPoller(getFn),
+ }
+}
+
+// Initializers returns the merged InitializerConfiguration.
+func (im *InitializerConfigurationManager) Initializers() (*v1alpha1.InitializerConfiguration, error) {
+ configuration, err := im.poller.configuration()
+ if err != nil {
+ return nil, err
+ }
+ initializerConfiguration, ok := configuration.(*v1alpha1.InitializerConfiguration)
+ if !ok {
+ return nil, fmt.Errorf("expected type %v, got type %v", reflect.TypeOf(initializerConfiguration), reflect.TypeOf(configuration))
+ }
+ return initializerConfiguration, nil
+}
+
+func (im *InitializerConfigurationManager) Run(stopCh <-chan struct{}) {
+ im.poller.Run(stopCh)
+}
+
+func mergeInitializerConfigurations(initializerConfigurationList *v1alpha1.InitializerConfigurationList) *v1alpha1.InitializerConfiguration {
+ configurations := initializerConfigurationList.Items
+ sort.SliceStable(configurations, InitializerConfigurationSorter(configurations).ByName)
+ var ret v1alpha1.InitializerConfiguration
+ for _, c := range configurations {
+ ret.Initializers = append(ret.Initializers, c.Initializers...)
+ }
+ return &ret
+}
+
+type InitializerConfigurationSorter []v1alpha1.InitializerConfiguration
+
+func (a InitializerConfigurationSorter) ByName(i, j int) bool {
+ return a[i].Name < a[j].Name
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go
new file mode 100644
index 0000000..4b2256e
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go
@@ -0,0 +1,97 @@
+/*
+Copyright 2017 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 configuration
+
+import (
+ "fmt"
+ "sort"
+ "sync/atomic"
+
+ "k8s.io/api/admissionregistration/v1beta1"
+ "k8s.io/apimachinery/pkg/labels"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+ "k8s.io/client-go/informers"
+ admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
+ "k8s.io/client-go/tools/cache"
+)
+
+// mutatingWebhookConfigurationManager collects the mutating webhook objects so that they can be called.
+type mutatingWebhookConfigurationManager struct {
+ configuration *atomic.Value
+ lister admissionregistrationlisters.MutatingWebhookConfigurationLister
+ hasSynced func() bool
+}
+
+var _ generic.Source = &mutatingWebhookConfigurationManager{}
+
+func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source {
+ informer := f.Admissionregistration().V1beta1().MutatingWebhookConfigurations()
+ manager := &mutatingWebhookConfigurationManager{
+ configuration: &atomic.Value{},
+ lister: informer.Lister(),
+ hasSynced: informer.Informer().HasSynced,
+ }
+
+ // Start with an empty list
+ manager.configuration.Store(&v1beta1.MutatingWebhookConfiguration{})
+
+ // On any change, rebuild the config
+ informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: func(_ interface{}) { manager.updateConfiguration() },
+ UpdateFunc: func(_, _ interface{}) { manager.updateConfiguration() },
+ DeleteFunc: func(_ interface{}) { manager.updateConfiguration() },
+ })
+
+ return manager
+}
+
+// Webhooks returns the merged MutatingWebhookConfiguration.
+func (m *mutatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
+ return m.configuration.Load().(*v1beta1.MutatingWebhookConfiguration).Webhooks
+}
+
+func (m *mutatingWebhookConfigurationManager) HasSynced() bool {
+ return m.hasSynced()
+}
+
+func (m *mutatingWebhookConfigurationManager) updateConfiguration() {
+ configurations, err := m.lister.List(labels.Everything())
+ if err != nil {
+ utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
+ return
+ }
+ m.configuration.Store(mergeMutatingWebhookConfigurations(configurations))
+}
+
+func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) *v1beta1.MutatingWebhookConfiguration {
+ var ret v1beta1.MutatingWebhookConfiguration
+ // 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)
+ for _, c := range configurations {
+ ret.Webhooks = append(ret.Webhooks, c.Webhooks...)
+ }
+ return &ret
+}
+
+type MutatingWebhookConfigurationSorter []*v1beta1.MutatingWebhookConfiguration
+
+func (a MutatingWebhookConfigurationSorter) ByName(i, j int) bool {
+ return a[i].Name < a[j].Name
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go
new file mode 100644
index 0000000..9258258
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go
@@ -0,0 +1,97 @@
+/*
+Copyright 2017 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 configuration
+
+import (
+ "fmt"
+ "sort"
+ "sync/atomic"
+
+ "k8s.io/api/admissionregistration/v1beta1"
+ "k8s.io/apimachinery/pkg/labels"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+ "k8s.io/client-go/informers"
+ admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
+ "k8s.io/client-go/tools/cache"
+)
+
+// validatingWebhookConfigurationManager collects the validating webhook objects so that they can be called.
+type validatingWebhookConfigurationManager struct {
+ configuration *atomic.Value
+ lister admissionregistrationlisters.ValidatingWebhookConfigurationLister
+ hasSynced func() bool
+}
+
+var _ generic.Source = &validatingWebhookConfigurationManager{}
+
+func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) generic.Source {
+ informer := f.Admissionregistration().V1beta1().ValidatingWebhookConfigurations()
+ manager := &validatingWebhookConfigurationManager{
+ configuration: &atomic.Value{},
+ lister: informer.Lister(),
+ hasSynced: informer.Informer().HasSynced,
+ }
+
+ // Start with an empty list
+ manager.configuration.Store(&v1beta1.ValidatingWebhookConfiguration{})
+
+ // On any change, rebuild the config
+ informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: func(_ interface{}) { manager.updateConfiguration() },
+ UpdateFunc: func(_, _ interface{}) { manager.updateConfiguration() },
+ DeleteFunc: func(_ interface{}) { manager.updateConfiguration() },
+ })
+
+ return manager
+}
+
+// Webhooks returns the merged ValidatingWebhookConfiguration.
+func (v *validatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
+ return v.configuration.Load().(*v1beta1.ValidatingWebhookConfiguration).Webhooks
+}
+
+// HasSynced returns true if the shared informers have synced.
+func (v *validatingWebhookConfigurationManager) HasSynced() bool {
+ return v.hasSynced()
+}
+
+func (v *validatingWebhookConfigurationManager) updateConfiguration() {
+ configurations, err := v.lister.List(labels.Everything())
+ if err != nil {
+ utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
+ return
+ }
+ v.configuration.Store(mergeValidatingWebhookConfigurations(configurations))
+}
+
+func mergeValidatingWebhookConfigurations(
+ configurations []*v1beta1.ValidatingWebhookConfiguration,
+) *v1beta1.ValidatingWebhookConfiguration {
+ sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName)
+ var ret v1beta1.ValidatingWebhookConfiguration
+ for _, c := range configurations {
+ ret.Webhooks = append(ret.Webhooks, c.Webhooks...)
+ }
+ return &ret
+}
+
+type ValidatingWebhookConfigurationSorter []*v1beta1.ValidatingWebhookConfiguration
+
+func (a ValidatingWebhookConfigurationSorter) ByName(i, j int) bool {
+ return a[i].Name < a[j].Name
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/decorator.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/decorator.go
new file mode 100644
index 0000000..a4b0b28
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/decorator.go
@@ -0,0 +1,39 @@
+/*
+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 admission
+
+type Decorator interface {
+ Decorate(handler Interface, name string) Interface
+}
+
+type DecoratorFunc func(handler Interface, name string) Interface
+
+func (d DecoratorFunc) Decorate(handler Interface, name string) Interface {
+ return d(handler, name)
+}
+
+type Decorators []Decorator
+
+// Decorate applies the decorator in inside-out order, i.e. the first decorator in the slice is first applied to the given handler.
+func (d Decorators) Decorate(handler Interface, name string) Interface {
+ result := handler
+ for _, d := range d {
+ result = d.Decorate(result, name)
+ }
+
+ return result
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/errors.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/errors.go
new file mode 100644
index 0000000..9a069a2
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/errors.go
@@ -0,0 +1,72 @@
+/*
+Copyright 2015 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
+
+import (
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+)
+
+func extractResourceName(a Attributes) (name string, resource schema.GroupResource, err error) {
+ resource = a.GetResource().GroupResource()
+
+ if len(a.GetName()) > 0 {
+ return a.GetName(), resource, nil
+ }
+
+ name = "Unknown"
+ obj := a.GetObject()
+ if obj != nil {
+ accessor, err := meta.Accessor(obj)
+ if err != nil {
+ // not all object have ObjectMeta. If we don't, return a name with a slash (always illegal)
+ return "Unknown/errorGettingName", resource, nil
+ }
+
+ // this is necessary because name object name generation has not occurred yet
+ if len(accessor.GetName()) > 0 {
+ name = accessor.GetName()
+ } else if len(accessor.GetGenerateName()) > 0 {
+ name = accessor.GetGenerateName()
+ }
+ }
+ return name, resource, nil
+}
+
+// NewForbidden is a utility function to return a well-formatted admission control error response
+func NewForbidden(a Attributes, internalError error) error {
+ // do not double wrap an error of same type
+ if apierrors.IsForbidden(internalError) {
+ return internalError
+ }
+ name, resource, err := extractResourceName(a)
+ if err != nil {
+ return apierrors.NewInternalError(utilerrors.NewAggregate([]error{internalError, err}))
+ }
+ return apierrors.NewForbidden(resource, name, internalError)
+}
+
+// NewNotFound is a utility function to return a well-formatted admission control error response
+func NewNotFound(a Attributes) error {
+ name, resource, err := extractResourceName(a)
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ return apierrors.NewNotFound(resource, name)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/handler.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/handler.go
new file mode 100644
index 0000000..d2a9e7d
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/handler.go
@@ -0,0 +1,79 @@
+/*
+Copyright 2015 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
+
+import (
+ "time"
+
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
+const (
+ // timeToWaitForReady is the amount of time to wait to let an admission controller to be ready to satisfy a request.
+ // this is useful when admission controllers need to warm their caches before letting requests through.
+ timeToWaitForReady = 10 * time.Second
+)
+
+// ReadyFunc is a function that returns true if the admission controller is ready to handle requests.
+type ReadyFunc func() bool
+
+// Handler is a base for admission control handlers that
+// support a predefined set of operations
+type Handler struct {
+ operations sets.String
+ readyFunc ReadyFunc
+}
+
+// Handles returns true for methods that this handler supports
+func (h *Handler) Handles(operation Operation) bool {
+ return h.operations.Has(string(operation))
+}
+
+// NewHandler creates a new base handler that handles the passed
+// in operations
+func NewHandler(ops ...Operation) *Handler {
+ operations := sets.NewString()
+ for _, op := range ops {
+ operations.Insert(string(op))
+ }
+ return &Handler{
+ operations: operations,
+ }
+}
+
+// SetReadyFunc allows late registration of a ReadyFunc to know if the handler is ready to process requests.
+func (h *Handler) SetReadyFunc(readyFunc ReadyFunc) {
+ h.readyFunc = readyFunc
+}
+
+// WaitForReady will wait for the readyFunc (if registered) to return ready, and in case of timeout, will return false.
+func (h *Handler) WaitForReady() bool {
+ // there is no ready func configured, so we return immediately
+ if h.readyFunc == nil {
+ return true
+ }
+
+ timeout := time.After(timeToWaitForReady)
+ for !h.readyFunc() {
+ select {
+ case <-time.After(100 * time.Millisecond):
+ case <-timeout:
+ return h.readyFunc()
+ }
+ }
+ return true
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/initializer.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/initializer.go
new file mode 100644
index 0000000..abe764b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/initializer.go
@@ -0,0 +1,70 @@
+/*
+Copyright 2017 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 initializer
+
+import (
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/authorization/authorizer"
+ "k8s.io/client-go/informers"
+ "k8s.io/client-go/kubernetes"
+)
+
+type pluginInitializer struct {
+ externalClient kubernetes.Interface
+ externalInformers informers.SharedInformerFactory
+ authorizer authorizer.Authorizer
+ scheme *runtime.Scheme
+}
+
+// New creates an instance of admission plugins initializer.
+// TODO(p0lyn0mial): make the parameters public, this construction seems to be redundant.
+func New(
+ extClientset kubernetes.Interface,
+ extInformers informers.SharedInformerFactory,
+ authz authorizer.Authorizer,
+ scheme *runtime.Scheme,
+) pluginInitializer {
+ return pluginInitializer{
+ externalClient: extClientset,
+ externalInformers: extInformers,
+ authorizer: authz,
+ scheme: scheme,
+ }
+}
+
+// Initialize checks the initialization interfaces implemented by a plugin
+// and provide the appropriate initialization data
+func (i pluginInitializer) Initialize(plugin admission.Interface) {
+ if wants, ok := plugin.(WantsExternalKubeClientSet); ok {
+ wants.SetExternalKubeClientSet(i.externalClient)
+ }
+
+ if wants, ok := plugin.(WantsExternalKubeInformerFactory); ok {
+ wants.SetExternalKubeInformerFactory(i.externalInformers)
+ }
+
+ if wants, ok := plugin.(WantsAuthorizer); ok {
+ wants.SetAuthorizer(i.authorizer)
+ }
+
+ if wants, ok := plugin.(WantsScheme); ok {
+ wants.SetScheme(i.scheme)
+ }
+}
+
+var _ admission.PluginInitializer = pluginInitializer{}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/interfaces.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/interfaces.go
new file mode 100644
index 0000000..98a0758
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/initializer/interfaces.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2017 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 initializer
+
+import (
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/authorization/authorizer"
+ "k8s.io/client-go/informers"
+ "k8s.io/client-go/kubernetes"
+)
+
+// WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it
+type WantsExternalKubeClientSet interface {
+ SetExternalKubeClientSet(kubernetes.Interface)
+ admission.InitializationValidator
+}
+
+// WantsExternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it
+type WantsExternalKubeInformerFactory interface {
+ SetExternalKubeInformerFactory(informers.SharedInformerFactory)
+ admission.InitializationValidator
+}
+
+// WantsAuthorizer defines a function which sets Authorizer for admission plugins that need it.
+type WantsAuthorizer interface {
+ SetAuthorizer(authorizer.Authorizer)
+ admission.InitializationValidator
+}
+
+// WantsScheme defines a function that accepts runtime.Scheme for admission plugins that need it.
+type WantsScheme interface {
+ SetScheme(*runtime.Scheme)
+ admission.InitializationValidator
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/interfaces.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/interfaces.go
new file mode 100644
index 0000000..68ef558
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/interfaces.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2014 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
+
+import (
+ "io"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apiserver/pkg/authentication/user"
+)
+
+// Attributes is an interface used by AdmissionController to get information about a request
+// that is used to make an admission decision.
+type Attributes interface {
+ // GetName returns the name of the object as presented in the request. On a CREATE operation, the client
+ // may omit name and rely on the server to generate the name. If that is the case, this method will return
+ // the empty string
+ GetName() string
+ // GetNamespace is the namespace associated with the request (if any)
+ GetNamespace() string
+ // GetResource is the name of the resource being requested. This is not the kind. For example: pods
+ GetResource() schema.GroupVersionResource
+ // GetSubresource is the name of the subresource being requested. This is a different resource, scoped to the parent resource, but it may have a different kind.
+ // For instance, /pods has the resource "pods" and the kind "Pod", while /pods/foo/status has the resource "pods", the sub resource "status", and the kind "Pod"
+ // (because status operates on pods). The binding resource for a pod though may be /pods/foo/binding, which has resource "pods", subresource "binding", and kind "Binding".
+ GetSubresource() string
+ // GetOperation is the operation being performed
+ GetOperation() Operation
+ // GetObject is the object from the incoming request prior to default values being applied
+ GetObject() runtime.Object
+ // GetOldObject is the existing object. Only populated for UPDATE requests.
+ GetOldObject() runtime.Object
+ // GetKind is the type of object being manipulated. For example: Pod
+ GetKind() schema.GroupVersionKind
+ // GetUserInfo is information about the requesting user
+ GetUserInfo() user.Info
+
+ // AddAnnotation sets annotation according to key-value pair. The key should be qualified, e.g., podsecuritypolicy.admission.k8s.io/admit-policy, where
+ // "podsecuritypolicy" is the name of the plugin, "admission.k8s.io" is the name of the organization, "admit-policy" is the key name.
+ // 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
+}
+
+// privateAnnotationsGetter is a private interface which allows users to get annotations from Attributes.
+type privateAnnotationsGetter interface {
+ getAnnotations() map[string]string
+}
+
+// AnnotationsGetter allows users to get annotations from Attributes. An alternate Attribute should implement
+// this interface.
+type AnnotationsGetter interface {
+ GetAnnotations() map[string]string
+}
+
+// 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
+ // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
+ Handles(operation Operation) bool
+}
+
+type MutationInterface interface {
+ Interface
+
+ // Admit makes an admission decision based on the request attributes
+ Admit(a Attributes) (err error)
+}
+
+// ValidationInterface is an abstract, pluggable interface for Admission Control decisions.
+type ValidationInterface interface {
+ Interface
+
+ // Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate
+ Validate(a Attributes) (err error)
+}
+
+// Operation is the type of resource operation being checked for admission control
+type Operation string
+
+// Operation constants
+const (
+ Create Operation = "CREATE"
+ Update Operation = "UPDATE"
+ Delete Operation = "DELETE"
+ Connect Operation = "CONNECT"
+)
+
+// PluginInitializer is used for initialization of shareable resources between admission plugins.
+// After initialization the resources have to be set separately
+type PluginInitializer interface {
+ Initialize(plugin Interface)
+}
+
+// InitializationValidator holds ValidateInitialization functions, which are responsible for validation of initialized
+// shared resources and should be implemented on admission plugins
+type InitializationValidator interface {
+ ValidateInitialization() error
+}
+
+// ConfigProvider provides a way to get configuration for an admission plugin based on its name
+type ConfigProvider interface {
+ ConfigFor(pluginName string) (io.Reader, error)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/metrics/metrics.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/metrics/metrics.go
new file mode 100644
index 0000000..0955a98
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/metrics/metrics.go
@@ -0,0 +1,217 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+ "fmt"
+ "strconv"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+
+ "k8s.io/apiserver/pkg/admission"
+)
+
+const (
+ namespace = "apiserver"
+ subsystem = "admission"
+)
+
+var (
+ // Use buckets ranging from 25 ms to ~2.5 seconds.
+ latencyBuckets = prometheus.ExponentialBuckets(25000, 2.5, 5)
+ latencySummaryMaxAge = 5 * time.Hour
+
+ // Metrics provides access to all admission metrics.
+ Metrics = newAdmissionMetrics()
+)
+
+// ObserverFunc is a func that emits metrics.
+type ObserverFunc func(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string)
+
+const (
+ stepValidate = "validate"
+ stepAdmit = "admit"
+)
+
+// WithControllerMetrics is a decorator for named admission handlers.
+func WithControllerMetrics(i admission.Interface, name string) admission.Interface {
+ return WithMetrics(i, Metrics.ObserveAdmissionController, name)
+}
+
+// WithStepMetrics is a decorator for a whole admission phase, i.e. admit or validation.admission step.
+func WithStepMetrics(i admission.Interface) admission.Interface {
+ return WithMetrics(i, Metrics.ObserveAdmissionStep)
+}
+
+// WithMetrics is a decorator for admission handlers with a generic observer func.
+func WithMetrics(i admission.Interface, observer ObserverFunc, extraLabels ...string) admission.Interface {
+ return &pluginHandlerWithMetrics{
+ Interface: i,
+ observer: observer,
+ extraLabels: extraLabels,
+ }
+}
+
+// pluginHandlerWithMetrics decorates a admission handler with metrics.
+type pluginHandlerWithMetrics struct {
+ admission.Interface
+ observer ObserverFunc
+ extraLabels []string
+}
+
+// Admit performs a mutating admission control check and emit metrics.
+func (p pluginHandlerWithMetrics) Admit(a admission.Attributes) error {
+ mutatingHandler, ok := p.Interface.(admission.MutationInterface)
+ if !ok {
+ return nil
+ }
+
+ start := time.Now()
+ err := mutatingHandler.Admit(a)
+ p.observer(time.Since(start), err != nil, a, stepAdmit, p.extraLabels...)
+ return err
+}
+
+// Validate performs a non-mutating admission control check and emits metrics.
+func (p pluginHandlerWithMetrics) Validate(a admission.Attributes) error {
+ validatingHandler, ok := p.Interface.(admission.ValidationInterface)
+ if !ok {
+ return nil
+ }
+
+ start := time.Now()
+ err := validatingHandler.Validate(a)
+ p.observer(time.Since(start), err != nil, a, stepValidate, p.extraLabels...)
+ return err
+}
+
+// AdmissionMetrics instruments admission with prometheus metrics.
+type AdmissionMetrics struct {
+ step *metricSet
+ controller *metricSet
+ webhook *metricSet
+}
+
+// newAdmissionMetrics create a new AdmissionMetrics, configured with default metric names.
+func newAdmissionMetrics() *AdmissionMetrics {
+ // Admission metrics for a step of the admission flow. The entire admission flow is broken down into a series of steps
+ // Each step is identified by a distinct type label value.
+ step := newMetricSet("step",
+ []string{"type", "operation", "group", "version", "resource", "subresource", "rejected"},
+ "Admission sub-step %s, broken out for each operation and API resource and step type (validate or admit).", true)
+
+ // Built-in admission controller metrics. Each admission controller is identified by name.
+ controller := newMetricSet("controller",
+ []string{"name", "type", "operation", "group", "version", "resource", "subresource", "rejected"},
+ "Admission controller %s, identified by name and broken out for each operation and API resource and type (validate or admit).", false)
+
+ // Admission webhook metrics. Each webhook is identified by name.
+ webhook := newMetricSet("webhook",
+ []string{"name", "type", "operation", "group", "version", "resource", "subresource", "rejected"},
+ "Admission webhook %s, identified by name and broken out for each operation and API resource and type (validate or admit).", false)
+
+ step.mustRegister()
+ controller.mustRegister()
+ webhook.mustRegister()
+ return &AdmissionMetrics{step: step, controller: controller, webhook: webhook}
+}
+
+func (m *AdmissionMetrics) reset() {
+ m.step.reset()
+ m.controller.reset()
+ m.webhook.reset()
+}
+
+// ObserveAdmissionStep records admission related metrics for a admission step, identified by step type.
+func (m *AdmissionMetrics) ObserveAdmissionStep(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
+ gvr := attr.GetResource()
+ m.step.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), gvr.Group, gvr.Version, gvr.Resource, attr.GetSubresource(), strconv.FormatBool(rejected))...)
+}
+
+// ObserveAdmissionController records admission related metrics for a built-in admission controller, identified by it's plugin handler name.
+func (m *AdmissionMetrics) ObserveAdmissionController(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
+ gvr := attr.GetResource()
+ m.controller.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), gvr.Group, gvr.Version, gvr.Resource, attr.GetSubresource(), strconv.FormatBool(rejected))...)
+}
+
+// ObserveWebhook records admission related metrics for a admission webhook.
+func (m *AdmissionMetrics) ObserveWebhook(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
+ gvr := attr.GetResource()
+ m.webhook.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), gvr.Group, gvr.Version, gvr.Resource, attr.GetSubresource(), strconv.FormatBool(rejected))...)
+}
+
+type metricSet struct {
+ latencies *prometheus.HistogramVec
+ latenciesSummary *prometheus.SummaryVec
+}
+
+func newMetricSet(name string, labels []string, helpTemplate string, hasSummary bool) *metricSet {
+ var summary *prometheus.SummaryVec
+ if hasSummary {
+ summary = prometheus.NewSummaryVec(
+ prometheus.SummaryOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: fmt.Sprintf("%s_admission_latencies_seconds_summary", name),
+ Help: fmt.Sprintf(helpTemplate, "latency summary"),
+ MaxAge: latencySummaryMaxAge,
+ },
+ labels,
+ )
+ }
+
+ return &metricSet{
+ latencies: prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: fmt.Sprintf("%s_admission_latencies_seconds", name),
+ Help: fmt.Sprintf(helpTemplate, "latency histogram"),
+ Buckets: latencyBuckets,
+ },
+ labels,
+ ),
+
+ latenciesSummary: summary,
+ }
+}
+
+// MustRegister registers all the prometheus metrics in the metricSet.
+func (m *metricSet) mustRegister() {
+ prometheus.MustRegister(m.latencies)
+ if m.latenciesSummary != nil {
+ prometheus.MustRegister(m.latenciesSummary)
+ }
+}
+
+// Reset resets all the prometheus metrics in the metricSet.
+func (m *metricSet) reset() {
+ m.latencies.Reset()
+ if m.latenciesSummary != nil {
+ m.latenciesSummary.Reset()
+ }
+}
+
+// Observe records an observed admission event to all metrics in the metricSet.
+func (m *metricSet) observe(elapsed time.Duration, labels ...string) {
+ elapsedMicroseconds := float64(elapsed / time.Microsecond)
+ m.latencies.WithLabelValues(labels...).Observe(elapsedMicroseconds)
+ if m.latenciesSummary != nil {
+ m.latenciesSummary.WithLabelValues(labels...).Observe(elapsedMicroseconds)
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go
new file mode 100644
index 0000000..1bb59da
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/initialization/initialization.go
@@ -0,0 +1,368 @@
+/*
+Copyright 2017 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 initialization
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/golang/glog"
+
+ "k8s.io/api/admissionregistration/v1alpha1"
+ "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/api/validation"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/admission/configuration"
+ "k8s.io/apiserver/pkg/authorization/authorizer"
+ "k8s.io/apiserver/pkg/features"
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
+ clientset "k8s.io/client-go/kubernetes"
+)
+
+const (
+ // Name of admission plug-in
+ PluginName = "Initializers"
+)
+
+// Register registers a plugin
+func Register(plugins *admission.Plugins) {
+ plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
+ return NewInitializer(), nil
+ })
+}
+
+type initializerOptions struct {
+ Initializers []string
+}
+
+type InitializationConfig interface {
+ Run(stopCh <-chan struct{})
+ Initializers() (*v1alpha1.InitializerConfiguration, error)
+}
+
+type initializer struct {
+ config InitializationConfig
+ authorizer authorizer.Authorizer
+}
+
+// NewInitializer creates a new initializer plugin which assigns newly created resources initializers
+// based on configuration loaded from the admission API group.
+// FUTURE: this may be moved to the storage layer of the apiserver, but for now this is an alpha feature
+// that can be disabled.
+func NewInitializer() admission.Interface {
+ return &initializer{}
+}
+
+// ValidateInitialization implements the InitializationValidator interface.
+func (i *initializer) ValidateInitialization() error {
+ if i.config == nil {
+ return fmt.Errorf("the Initializer admission plugin requires a Kubernetes client to be provided")
+ }
+ if i.authorizer == nil {
+ return fmt.Errorf("the Initializer admission plugin requires an authorizer to be provided")
+ }
+
+ if !utilfeature.DefaultFeatureGate.Enabled(features.Initializers) {
+ if err := utilfeature.DefaultFeatureGate.Set(string(features.Initializers) + "=true"); err != nil {
+ glog.Errorf("error enabling Initializers feature as part of admission plugin setup: %v", err)
+ } else {
+ glog.Infof("enabled Initializers feature as part of admission plugin setup")
+ }
+ }
+
+ i.config.Run(wait.NeverStop)
+ return nil
+}
+
+// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface.
+func (i *initializer) SetExternalKubeClientSet(client clientset.Interface) {
+ i.config = configuration.NewInitializerConfigurationManager(client.AdmissionregistrationV1alpha1().InitializerConfigurations())
+}
+
+// SetAuthorizer implements the WantsAuthorizer interface.
+func (i *initializer) SetAuthorizer(a authorizer.Authorizer) {
+ i.authorizer = a
+}
+
+var initializerFieldPath = field.NewPath("metadata", "initializers")
+
+// readConfig holds requests instead of failing them if the server is not yet initialized
+// or is unresponsive. It formats the returned error for client use if necessary.
+func (i *initializer) readConfig(a admission.Attributes) (*v1alpha1.InitializerConfiguration, error) {
+ // read initializers from config
+ config, err := i.config.Initializers()
+ if err == nil {
+ return config, nil
+ }
+
+ // if initializer configuration is disabled, fail open
+ if err == configuration.ErrDisabled {
+ return &v1alpha1.InitializerConfiguration{}, nil
+ }
+
+ e := errors.NewServerTimeout(a.GetResource().GroupResource(), "create", 1)
+ if err == configuration.ErrNotReady {
+ e.ErrStatus.Message = fmt.Sprintf("Waiting for initialization configuration to load: %v", err)
+ e.ErrStatus.Reason = "LoadingConfiguration"
+ e.ErrStatus.Details.Causes = append(e.ErrStatus.Details.Causes, metav1.StatusCause{
+ Type: "InitializerConfigurationPending",
+ Message: "The server is waiting for the initializer configuration to be loaded.",
+ })
+ } else {
+ e.ErrStatus.Message = fmt.Sprintf("Unable to refresh the initializer configuration: %v", err)
+ e.ErrStatus.Reason = "LoadingConfiguration"
+ e.ErrStatus.Details.Causes = append(e.ErrStatus.Details.Causes, metav1.StatusCause{
+ Type: "InitializerConfigurationFailure",
+ Message: "An error has occurred while refreshing the initializer configuration, no resources can be created until a refresh succeeds.",
+ })
+ }
+ return nil, e
+}
+
+// Admit checks for create requests to add initializers, or update request to enforce invariants.
+// The admission controller fails open if the object doesn't have ObjectMeta (can't be initialized).
+// A client with sufficient permission ("initialize" verb on resource) can specify its own initializers
+// or an empty initializers struct (which bypasses initialization). Only clients with the initialize verb
+// can update objects that have not completed initialization. Sub resources can still be modified on
+// resources that are undergoing initialization.
+// TODO: once this logic is ready for beta, move it into the REST storage layer.
+func (i *initializer) Admit(a admission.Attributes) (err error) {
+ switch a.GetOperation() {
+ case admission.Create, admission.Update:
+ default:
+ return nil
+ }
+
+ // TODO: should sub-resource action should be denied until the object is initialized?
+ if len(a.GetSubresource()) > 0 {
+ return nil
+ }
+
+ switch a.GetOperation() {
+ case admission.Create:
+ accessor, err := meta.Accessor(a.GetObject())
+ if err != nil {
+ // objects without meta accessor cannot be checked for initialization, and it is possible to make calls
+ // via our API that don't have ObjectMeta
+ return nil
+ }
+ existing := accessor.GetInitializers()
+ if existing != nil {
+ glog.V(5).Infof("Admin bypassing initialization for %s", a.GetResource())
+
+ // it must be possible for some users to bypass initialization - for now, check the initialize operation
+ if err := i.canInitialize(a, "create with initializers denied"); err != nil {
+ return err
+ }
+ // allow administrators to bypass initialization by setting an empty initializers struct
+ if len(existing.Pending) == 0 && existing.Result == nil {
+ accessor.SetInitializers(nil)
+ return nil
+ }
+ } else {
+ glog.V(5).Infof("Checking initialization for %s", a.GetResource())
+
+ config, err := i.readConfig(a)
+ if err != nil {
+ return err
+ }
+
+ // Mirror pods are exempt from initialization because they are created and initialized
+ // on the Kubelet before they appear in the API.
+ // TODO: once this moves to REST storage layer, this becomes a pod specific concern
+ if a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Pod").GroupKind() {
+ accessor, err := meta.Accessor(a.GetObject())
+ if err != nil {
+ return err
+ }
+ annotations := accessor.GetAnnotations()
+ if _, isMirror := annotations[v1.MirrorPodAnnotationKey]; isMirror {
+ return nil
+ }
+ }
+
+ names := findInitializers(config, a.GetResource())
+ if len(names) == 0 {
+ glog.V(5).Infof("No initializers needed")
+ return nil
+ }
+
+ glog.V(5).Infof("Found initializers for %s: %v", a.GetResource(), names)
+ accessor.SetInitializers(newInitializers(names))
+ }
+
+ case admission.Update:
+ accessor, err := meta.Accessor(a.GetObject())
+ if err != nil {
+ // objects without meta accessor cannot be checked for initialization, and it is possible to make calls
+ // via our API that don't have ObjectMeta
+ return nil
+ }
+ updated := accessor.GetInitializers()
+
+ // controllers deployed with an empty initializers.pending have their initializers set to nil
+ // but should be able to update without changing their manifest
+ if updated != nil && len(updated.Pending) == 0 && updated.Result == nil {
+ accessor.SetInitializers(nil)
+ updated = nil
+ }
+
+ existingAccessor, err := meta.Accessor(a.GetOldObject())
+ if err != nil {
+ // if the old object does not have an accessor, but the new one does, error out
+ return fmt.Errorf("initialized resources must be able to set initializers (%T): %v", a.GetOldObject(), err)
+ }
+ existing := existingAccessor.GetInitializers()
+
+ // updates on initialized resources are allowed
+ if updated == nil && existing == nil {
+ return nil
+ }
+
+ glog.V(5).Infof("Modifying uninitialized resource %s", a.GetResource())
+
+ // because we are called before validation, we need to ensure the update transition is valid.
+ if errs := validation.ValidateInitializersUpdate(updated, existing, initializerFieldPath); len(errs) > 0 {
+ return errors.NewInvalid(a.GetKind().GroupKind(), a.GetName(), errs)
+ }
+
+ // caller must have the ability to mutate un-initialized resources
+ if err := i.canInitialize(a, "update to uninitialized resource denied"); err != nil {
+ return err
+ }
+
+ // TODO: restrict initialization list changes to specific clients?
+ }
+
+ return nil
+}
+
+func (i *initializer) canInitialize(a admission.Attributes, message string) error {
+ // caller must have the ability to mutate un-initialized resources
+ decision, reason, err := i.authorizer.Authorize(authorizer.AttributesRecord{
+ Name: a.GetName(),
+ ResourceRequest: true,
+ User: a.GetUserInfo(),
+ Verb: "initialize",
+ Namespace: a.GetNamespace(),
+ APIGroup: a.GetResource().Group,
+ APIVersion: a.GetResource().Version,
+ Resource: a.GetResource().Resource,
+ })
+ if err != nil {
+ return err
+ }
+ if decision != authorizer.DecisionAllow {
+ return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("%s: %s", message, reason))
+ }
+ return nil
+}
+
+// Handles returns true if this admission controller can handle the given operation
+// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
+func (i *initializer) Handles(op admission.Operation) bool {
+ return op == admission.Create || op == admission.Update
+}
+
+// newInitializers populates an Initializers struct.
+func newInitializers(names []string) *metav1.Initializers {
+ if len(names) == 0 {
+ return nil
+ }
+ var init []metav1.Initializer
+ for _, name := range names {
+ init = append(init, metav1.Initializer{Name: name})
+ }
+ return &metav1.Initializers{
+ Pending: init,
+ }
+}
+
+// findInitializers returns the list of initializer names that apply to a config. It returns an empty list
+// if no initializers apply.
+func findInitializers(initializers *v1alpha1.InitializerConfiguration, gvr schema.GroupVersionResource) []string {
+ var names []string
+ for _, init := range initializers.Initializers {
+ if !matchRule(init.Rules, gvr) {
+ continue
+ }
+ names = append(names, init.Name)
+ }
+ return names
+}
+
+// matchRule returns true if any rule matches the provided group version resource.
+func matchRule(rules []v1alpha1.Rule, gvr schema.GroupVersionResource) bool {
+ for _, rule := range rules {
+ if !hasGroup(rule.APIGroups, gvr.Group) {
+ return false
+ }
+ if !hasVersion(rule.APIVersions, gvr.Version) {
+ return false
+ }
+ if !hasResource(rule.Resources, gvr.Resource) {
+ return false
+ }
+ }
+ return len(rules) > 0
+}
+
+func hasGroup(groups []string, group string) bool {
+ if groups[0] == "*" {
+ return true
+ }
+ for _, g := range groups {
+ if g == group {
+ return true
+ }
+ }
+ return false
+}
+
+func hasVersion(versions []string, version string) bool {
+ if versions[0] == "*" {
+ return true
+ }
+ for _, v := range versions {
+ if v == version {
+ return true
+ }
+ }
+ return false
+}
+
+func hasResource(resources []string, resource string) bool {
+ if resources[0] == "*" || resources[0] == "*/*" {
+ return true
+ }
+ for _, r := range resources {
+ if strings.Contains(r, "/") {
+ continue
+ }
+ if r == resource {
+ return true
+ }
+ }
+ return false
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go
new file mode 100644
index 0000000..81c24f6
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go
@@ -0,0 +1,223 @@
+/*
+Copyright 2015 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 lifecycle
+
+import (
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/golang/glog"
+
+ "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"
+ utilcache "k8s.io/apimachinery/pkg/util/cache"
+ "k8s.io/apimachinery/pkg/util/clock"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/admission/initializer"
+ "k8s.io/client-go/informers"
+ "k8s.io/client-go/kubernetes"
+ corelisters "k8s.io/client-go/listers/core/v1"
+)
+
+const (
+ // Name of admission plug-in
+ PluginName = "NamespaceLifecycle"
+ // how long a namespace stays in the force live lookup cache before expiration.
+ forceLiveLookupTTL = 30 * time.Second
+ // how long to wait for a missing namespace before re-checking the cache (and then doing a live lookup)
+ // this accomplishes two things:
+ // 1. It allows a watch-fed cache time to observe a namespace creation event
+ // 2. It allows time for a namespace creation to distribute to members of a storage cluster,
+ // so the live lookup has a better chance of succeeding even if it isn't performed against the leader.
+ missingNamespaceWait = 50 * time.Millisecond
+)
+
+// Register registers a plugin
+func Register(plugins *admission.Plugins) {
+ plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
+ return NewLifecycle(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem, metav1.NamespacePublic))
+ })
+}
+
+// Lifecycle is an implementation of admission.Interface.
+// It enforces life-cycle constraints around a Namespace depending on its Phase
+type Lifecycle struct {
+ *admission.Handler
+ client kubernetes.Interface
+ immortalNamespaces sets.String
+ namespaceLister corelisters.NamespaceLister
+ // forceLiveLookupCache holds a list of entries for namespaces that we have a strong reason to believe are stale in our local cache.
+ // if a namespace is in this cache, then we will ignore our local state and always fetch latest from api server.
+ forceLiveLookupCache *utilcache.LRUExpireCache
+}
+
+var _ = initializer.WantsExternalKubeInformerFactory(&Lifecycle{})
+var _ = initializer.WantsExternalKubeClientSet(&Lifecycle{})
+
+func (l *Lifecycle) Admit(a admission.Attributes) 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"))
+ }
+
+ // always allow non-namespaced resources
+ if len(a.GetNamespace()) == 0 && a.GetKind().GroupKind() != v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
+ return nil
+ }
+
+ if a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
+ // if a namespace is deleted, we want to prevent all further creates into it
+ // while it is undergoing termination. to reduce incidences where the cache
+ // is slow to update, we add the namespace into a force live lookup list to ensure
+ // we are not looking at stale state.
+ if a.GetOperation() == admission.Delete {
+ l.forceLiveLookupCache.Add(a.GetName(), true, forceLiveLookupTTL)
+ }
+ // allow all operations to namespaces
+ return nil
+ }
+
+ // always allow deletion of other resources
+ if a.GetOperation() == admission.Delete {
+ return nil
+ }
+
+ // always allow access review checks. Returning status about the namespace would be leaking information
+ if isAccessReview(a) {
+ return nil
+ }
+
+ // we need to wait for our caches to warm
+ if !l.WaitForReady() {
+ return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request"))
+ }
+
+ var (
+ exists bool
+ err error
+ )
+
+ namespace, err := l.namespaceLister.Get(a.GetNamespace())
+ if err != nil {
+ if !errors.IsNotFound(err) {
+ return errors.NewInternalError(err)
+ }
+ } else {
+ exists = true
+ }
+
+ if !exists && a.GetOperation() == admission.Create {
+ // give the cache time to observe the namespace before rejecting a create.
+ // this helps when creating a namespace and immediately creating objects within it.
+ time.Sleep(missingNamespaceWait)
+ namespace, err = l.namespaceLister.Get(a.GetNamespace())
+ switch {
+ case errors.IsNotFound(err):
+ // no-op
+ case err != nil:
+ return errors.NewInternalError(err)
+ default:
+ exists = true
+ }
+ if exists {
+ glog.V(4).Infof("found %s in cache after waiting", a.GetNamespace())
+ }
+ }
+
+ // forceLiveLookup if true will skip looking at local cache state and instead always make a live call to server.
+ forceLiveLookup := false
+ if _, ok := l.forceLiveLookupCache.Get(a.GetNamespace()); ok {
+ // we think the namespace was marked for deletion, but our current local cache says otherwise, we will force a live lookup.
+ forceLiveLookup = exists && namespace.Status.Phase == v1.NamespaceActive
+ }
+
+ // refuse to operate on non-existent namespaces
+ if !exists || forceLiveLookup {
+ // as a last resort, make a call directly to storage
+ namespace, err = l.client.CoreV1().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{})
+ switch {
+ case errors.IsNotFound(err):
+ return err
+ case err != nil:
+ return errors.NewInternalError(err)
+ }
+ glog.V(4).Infof("found %s via storage lookup", a.GetNamespace())
+ }
+
+ // ensure that we're not trying to create objects in terminating namespaces
+ if a.GetOperation() == admission.Create {
+ if namespace.Status.Phase != v1.NamespaceTerminating {
+ 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()))
+ }
+
+ return nil
+}
+
+// NewLifecycle creates a new namespace Lifecycle admission control handler
+func NewLifecycle(immortalNamespaces sets.String) (*Lifecycle, error) {
+ return newLifecycleWithClock(immortalNamespaces, clock.RealClock{})
+}
+
+func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (*Lifecycle, error) {
+ forceLiveLookupCache := utilcache.NewLRUExpireCacheWithClock(100, clock)
+ return &Lifecycle{
+ Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
+ immortalNamespaces: immortalNamespaces,
+ forceLiveLookupCache: forceLiveLookupCache,
+ }, nil
+}
+
+// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface.
+func (l *Lifecycle) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
+ namespaceInformer := f.Core().V1().Namespaces()
+ l.namespaceLister = namespaceInformer.Lister()
+ l.SetReadyFunc(namespaceInformer.Informer().HasSynced)
+}
+
+// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface.
+func (l *Lifecycle) SetExternalKubeClientSet(client kubernetes.Interface) {
+ l.client = client
+}
+
+// ValidateInitialization implements the InitializationValidator interface.
+func (l *Lifecycle) ValidateInitialization() error {
+ if l.namespaceLister == nil {
+ return fmt.Errorf("missing namespaceLister")
+ }
+ if l.client == nil {
+ return fmt.Errorf("missing client")
+ }
+ return nil
+}
+
+// accessReviewResources are resources which give a view into permissions in a namespace. Users must be allowed to create these
+// resources because returning "not found" errors allows someone to search for the "people I'm going to fire in 2017" namespace.
+var accessReviewResources = map[schema.GroupResource]bool{
+ {Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: true,
+}
+
+func isAccessReview(a admission.Attributes) bool {
+ return accessReviewResources[a.GetResource().GroupResource()]
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/doc.go
new file mode 100644
index 0000000..63ab310
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2017 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
+
+package webhookadmission
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/register.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/register.go
new file mode 100644
index 0000000..c958d15
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/register.go
@@ -0,0 +1,51 @@
+/*
+Copyright 2017 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 webhookadmission
+
+import (
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+var (
+ SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+ AddToScheme = SchemeBuilder.AddToScheme
+)
+
+// 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: runtime.APIVersionInternal}
+
+// Kind takes an unqualified kind and returns a Group qualified GroupKind
+func Kind(kind string) schema.GroupKind {
+ return SchemeGroupVersion.WithKind(kind).GroupKind()
+}
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+ return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+ // TODO this will get cleaned up with the scheme types are fixed
+ scheme.AddKnownTypes(SchemeGroupVersion,
+ &WebhookAdmission{},
+ )
+ return nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/types.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/types.go
new file mode 100644
index 0000000..71ce47b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/types.go
@@ -0,0 +1,29 @@
+/*
+Copyright 2017 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 webhookadmission
+
+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
+
+ // KubeConfigFile is the path to the kubeconfig file.
+ KubeConfigFile string
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/doc.go
new file mode 100644
index 0000000..04c376f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/doc.go
@@ -0,0 +1,23 @@
+/*
+Copyright 2017 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
+
+// Package v1alpha1 is the v1alpha1 version of the API.
+// +groupName=apiserver.config.k8s.io
+package v1alpha1
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/register.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/register.go
new file mode 100644
index 0000000..56489f7
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/register.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2017 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 v1alpha1
+
+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: "v1alpha1"}
+
+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.AddKnownTypes(SchemeGroupVersion,
+ &WebhookAdmission{},
+ )
+ return nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/types.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/types.go
new file mode 100644
index 0000000..a49a6a8
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/types.go
@@ -0,0 +1,29 @@
+/*
+Copyright 2017 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 v1alpha1
+
+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"`
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.conversion.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.conversion.go
new file mode 100644
index 0000000..3fb2e24
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.conversion.go
@@ -0,0 +1,60 @@
+// +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 v1alpha1
+
+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(scheme *runtime.Scheme) error {
+ return scheme.AddGeneratedConversionFuncs(
+ Convert_v1alpha1_WebhookAdmission_To_webhookadmission_WebhookAdmission,
+ Convert_webhookadmission_WebhookAdmission_To_v1alpha1_WebhookAdmission,
+ )
+}
+
+func autoConvert_v1alpha1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in *WebhookAdmission, out *webhookadmission.WebhookAdmission, s conversion.Scope) error {
+ out.KubeConfigFile = in.KubeConfigFile
+ return nil
+}
+
+// Convert_v1alpha1_WebhookAdmission_To_webhookadmission_WebhookAdmission is an autogenerated conversion function.
+func Convert_v1alpha1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in *WebhookAdmission, out *webhookadmission.WebhookAdmission, s conversion.Scope) error {
+ return autoConvert_v1alpha1_WebhookAdmission_To_webhookadmission_WebhookAdmission(in, out, s)
+}
+
+func autoConvert_webhookadmission_WebhookAdmission_To_v1alpha1_WebhookAdmission(in *webhookadmission.WebhookAdmission, out *WebhookAdmission, s conversion.Scope) error {
+ out.KubeConfigFile = in.KubeConfigFile
+ return nil
+}
+
+// Convert_webhookadmission_WebhookAdmission_To_v1alpha1_WebhookAdmission is an autogenerated conversion function.
+func Convert_webhookadmission_WebhookAdmission_To_v1alpha1_WebhookAdmission(in *webhookadmission.WebhookAdmission, out *WebhookAdmission, s conversion.Scope) error {
+ return autoConvert_webhookadmission_WebhookAdmission_To_v1alpha1_WebhookAdmission(in, out, s)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.deepcopy.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..a59d62d
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.deepcopy.go
@@ -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 v1alpha1
+
+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
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.defaults.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.defaults.go
new file mode 100644
index 0000000..dd621a3
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1/zz_generated.defaults.go
@@ -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 v1alpha1
+
+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
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/zz_generated.deepcopy.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/zz_generated.deepcopy.go
new file mode 100644
index 0000000..90b7e0a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/zz_generated.deepcopy.go
@@ -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 webhookadmission
+
+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
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/authentication.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/authentication.go
new file mode 100644
index 0000000..9b70c97
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/authentication.go
@@ -0,0 +1,175 @@
+/*
+Copyright 2017 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 config
+
+import (
+ "fmt"
+ "io/ioutil"
+ "strings"
+ "time"
+
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+ clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+// AuthenticationInfoResolverWrapper can be used to inject Dial function to the
+// rest.Config generated by the resolver.
+type AuthenticationInfoResolverWrapper func(AuthenticationInfoResolver) AuthenticationInfoResolver
+
+// AuthenticationInfoResolver builds rest.Config base on the server or service
+// name and service namespace.
+type AuthenticationInfoResolver interface {
+ // ClientConfigFor builds rest.Config based on the server.
+ ClientConfigFor(server string) (*rest.Config, error)
+ // ClientConfigForService builds rest.Config based on the serviceName and
+ // serviceNamespace.
+ ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error)
+}
+
+// AuthenticationInfoResolverDelegator implements AuthenticationInfoResolver.
+type AuthenticationInfoResolverDelegator struct {
+ ClientConfigForFunc func(server string) (*rest.Config, error)
+ ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error)
+}
+
+func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) {
+ return a.ClientConfigForFunc(server)
+}
+
+func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
+ return a.ClientConfigForServiceFunc(serviceName, serviceNamespace)
+}
+
+type defaultAuthenticationInfoResolver struct {
+ kubeconfig clientcmdapi.Config
+}
+
+// NewDefaultAuthenticationInfoResolver generates an AuthenticationInfoResolver
+// that builds rest.Config based on the kubeconfig file. kubeconfigFile is the
+// path to the kubeconfig.
+func NewDefaultAuthenticationInfoResolver(kubeconfigFile string) (AuthenticationInfoResolver, error) {
+ if len(kubeconfigFile) == 0 {
+ return &defaultAuthenticationInfoResolver{}, nil
+ }
+
+ loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
+ loadingRules.ExplicitPath = kubeconfigFile
+ loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
+ clientConfig, err := loader.RawConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ return &defaultAuthenticationInfoResolver{kubeconfig: clientConfig}, nil
+}
+
+func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) {
+ return c.clientConfig(server)
+}
+
+func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
+ return c.clientConfig(serviceName + "." + serviceNamespace + ".svc")
+}
+
+func (c *defaultAuthenticationInfoResolver) clientConfig(target string) (*rest.Config, error) {
+ // exact match
+ if authConfig, ok := c.kubeconfig.AuthInfos[target]; ok {
+ return restConfigFromKubeconfig(authConfig)
+ }
+
+ // star prefixed match
+ serverSteps := strings.Split(target, ".")
+ for i := 1; i < len(serverSteps); i++ {
+ nickName := "*." + strings.Join(serverSteps[i:], ".")
+ if authConfig, ok := c.kubeconfig.AuthInfos[nickName]; ok {
+ return restConfigFromKubeconfig(authConfig)
+ }
+ }
+
+ // if we're trying to hit the kube-apiserver and there wasn't an explicit config, use the in-cluster config
+ if target == "kubernetes.default.svc" {
+ // if we can find an in-cluster-config use that. If we can't, fall through.
+ inClusterConfig, err := rest.InClusterConfig()
+ if err == nil {
+ return setGlobalDefaults(inClusterConfig), nil
+ }
+ }
+
+ // star (default) match
+ if authConfig, ok := c.kubeconfig.AuthInfos["*"]; ok {
+ return restConfigFromKubeconfig(authConfig)
+ }
+
+ // use the current context from the kubeconfig if possible
+ if len(c.kubeconfig.CurrentContext) > 0 {
+ if currContext, ok := c.kubeconfig.Contexts[c.kubeconfig.CurrentContext]; ok {
+ if len(currContext.AuthInfo) > 0 {
+ if currAuth, ok := c.kubeconfig.AuthInfos[currContext.AuthInfo]; ok {
+ return restConfigFromKubeconfig(currAuth)
+ }
+ }
+ }
+ }
+
+ // anonymous
+ return setGlobalDefaults(&rest.Config{}), nil
+}
+
+func restConfigFromKubeconfig(configAuthInfo *clientcmdapi.AuthInfo) (*rest.Config, error) {
+ config := &rest.Config{}
+
+ // blindly overwrite existing values based on precedence
+ if len(configAuthInfo.Token) > 0 {
+ config.BearerToken = configAuthInfo.Token
+ } else if len(configAuthInfo.TokenFile) > 0 {
+ tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
+ if err != nil {
+ return nil, err
+ }
+ config.BearerToken = string(tokenBytes)
+ }
+ if len(configAuthInfo.Impersonate) > 0 {
+ config.Impersonate = rest.ImpersonationConfig{
+ UserName: configAuthInfo.Impersonate,
+ Groups: configAuthInfo.ImpersonateGroups,
+ Extra: configAuthInfo.ImpersonateUserExtra,
+ }
+ }
+ if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
+ config.CertFile = configAuthInfo.ClientCertificate
+ config.CertData = configAuthInfo.ClientCertificateData
+ config.KeyFile = configAuthInfo.ClientKey
+ config.KeyData = configAuthInfo.ClientKeyData
+ }
+ if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
+ config.Username = configAuthInfo.Username
+ config.Password = configAuthInfo.Password
+ }
+ if configAuthInfo.AuthProvider != nil {
+ return nil, fmt.Errorf("auth provider not supported")
+ }
+
+ return setGlobalDefaults(config), nil
+}
+
+func setGlobalDefaults(config *rest.Config) *rest.Config {
+ config.UserAgent = "kube-apiserver-admission"
+ config.Timeout = 30 * time.Second
+
+ return config
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/client.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/client.go
new file mode 100644
index 0000000..d520fd0
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/client.go
@@ -0,0 +1,187 @@
+/*
+Copyright 2017 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 config
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+
+ lru "github.com/hashicorp/golang-lru"
+ admissionv1beta1 "k8s.io/api/admission/v1beta1"
+ "k8s.io/api/admissionregistration/v1beta1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+ webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
+ "k8s.io/client-go/rest"
+)
+
+const (
+ defaultCacheSize = 200
+)
+
+var (
+ ErrNeedServiceOrURL = errors.New("webhook configuration must have either service or URL")
+)
+
+// ClientManager builds REST clients to talk to webhooks. It caches the clients
+// to avoid duplicate creation.
+type ClientManager struct {
+ authInfoResolver AuthenticationInfoResolver
+ serviceResolver ServiceResolver
+ negotiatedSerializer runtime.NegotiatedSerializer
+ cache *lru.Cache
+}
+
+// NewClientManager creates a clientManager.
+func NewClientManager() (ClientManager, error) {
+ cache, err := lru.New(defaultCacheSize)
+ if err != nil {
+ return ClientManager{}, err
+ }
+ admissionScheme := runtime.NewScheme()
+ admissionv1beta1.AddToScheme(admissionScheme)
+ return ClientManager{
+ cache: cache,
+ negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{
+ Serializer: serializer.NewCodecFactory(admissionScheme).LegacyCodec(admissionv1beta1.SchemeGroupVersion),
+ }),
+ }, nil
+}
+
+// SetAuthenticationInfoResolverWrapper sets the
+// AuthenticationInfoResolverWrapper.
+func (cm *ClientManager) SetAuthenticationInfoResolverWrapper(wrapper AuthenticationInfoResolverWrapper) {
+ if wrapper != nil {
+ cm.authInfoResolver = wrapper(cm.authInfoResolver)
+ }
+}
+
+// SetAuthenticationInfoResolver sets the AuthenticationInfoResolver.
+func (cm *ClientManager) SetAuthenticationInfoResolver(resolver AuthenticationInfoResolver) {
+ cm.authInfoResolver = resolver
+}
+
+// SetServiceResolver sets the ServiceResolver.
+func (cm *ClientManager) SetServiceResolver(sr ServiceResolver) {
+ if sr != nil {
+ cm.serviceResolver = sr
+ }
+}
+
+// Validate checks if ClientManager is properly set up.
+func (cm *ClientManager) Validate() error {
+ var errs []error
+ if cm.negotiatedSerializer == nil {
+ errs = append(errs, fmt.Errorf("the clientManager requires a negotiatedSerializer"))
+ }
+ if cm.serviceResolver == nil {
+ errs = append(errs, fmt.Errorf("the clientManager requires a serviceResolver"))
+ }
+ if cm.authInfoResolver == nil {
+ errs = append(errs, fmt.Errorf("the clientManager requires an authInfoResolver"))
+ }
+ return utilerrors.NewAggregate(errs)
+}
+
+// HookClient get a RESTClient from the cache, or constructs one based on the
+// webhook configuration.
+func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error) {
+ cacheKey, err := json.Marshal(h.ClientConfig)
+ if err != nil {
+ return nil, err
+ }
+ if client, ok := cm.cache.Get(string(cacheKey)); ok {
+ return client.(*rest.RESTClient), nil
+ }
+
+ complete := func(cfg *rest.Config) (*rest.RESTClient, error) {
+ // Combine CAData from the config with any existing CA bundle provided
+ if len(cfg.TLSClientConfig.CAData) > 0 {
+ cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, '\n')
+ }
+ cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, h.ClientConfig.CABundle...)
+
+ cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer
+ cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
+ client, err := rest.UnversionedRESTClientFor(cfg)
+ if err == nil {
+ cm.cache.Add(string(cacheKey), client)
+ }
+ return client, err
+ }
+
+ if svc := h.ClientConfig.Service; svc != nil {
+ restConfig, err := cm.authInfoResolver.ClientConfigForService(svc.Name, svc.Namespace)
+ if err != nil {
+ return nil, err
+ }
+ cfg := rest.CopyConfig(restConfig)
+ serverName := svc.Name + "." + svc.Namespace + ".svc"
+ host := serverName + ":443"
+ cfg.Host = "https://" + host
+ if svc.Path != nil {
+ cfg.APIPath = *svc.Path
+ }
+ // Set the server name if not already set
+ if len(cfg.TLSClientConfig.ServerName) == 0 {
+ cfg.TLSClientConfig.ServerName = serverName
+ }
+
+ delegateDialer := cfg.Dial
+ if delegateDialer == nil {
+ var d net.Dialer
+ delegateDialer = d.DialContext
+ }
+ cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) {
+ if addr == host {
+ u, err := cm.serviceResolver.ResolveEndpoint(svc.Namespace, svc.Name)
+ if err != nil {
+ return nil, err
+ }
+ addr = u.Host
+ }
+ return delegateDialer(ctx, network, addr)
+ }
+
+ return complete(cfg)
+ }
+
+ if h.ClientConfig.URL == nil {
+ return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: ErrNeedServiceOrURL}
+ }
+
+ u, err := url.Parse(*h.ClientConfig.URL)
+ if err != nil {
+ return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
+ }
+
+ restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg := rest.CopyConfig(restConfig)
+ cfg.Host = u.Scheme + "://" + u.Host
+ cfg.APIPath = u.Path
+
+ return complete(cfg)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/kubeconfig.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/kubeconfig.go
new file mode 100644
index 0000000..7cf0d31
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/kubeconfig.go
@@ -0,0 +1,68 @@
+/*
+Copyright 2017 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 config
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "path"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer"
+ "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/v1alpha1"
+)
+
+var (
+ scheme = runtime.NewScheme()
+ codecs = serializer.NewCodecFactory(scheme)
+)
+
+func init() {
+ webhookadmission.AddToScheme(scheme)
+ v1alpha1.AddToScheme(scheme)
+}
+
+// LoadConfig extract the KubeConfigFile from configFile
+func LoadConfig(configFile io.Reader) (string, error) {
+ var kubeconfigFile string
+ if configFile != nil {
+ // we have a config so parse it.
+ data, err := ioutil.ReadAll(configFile)
+ if err != nil {
+ return "", err
+ }
+ decoder := codecs.UniversalDecoder()
+ decodedObj, err := runtime.Decode(decoder, data)
+ if err != nil {
+ return "", err
+ }
+ config, ok := decodedObj.(*webhookadmission.WebhookAdmission)
+ if !ok {
+ return "", fmt.Errorf("unexpected type: %T", decodedObj)
+ }
+
+ if !path.IsAbs(config.KubeConfigFile) {
+ return "", field.Invalid(field.NewPath("kubeConfigFile"), config.KubeConfigFile, "must be an absolute file path")
+ }
+
+ kubeconfigFile = config.KubeConfigFile
+ }
+ return kubeconfigFile, nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/serviceresolver.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/serviceresolver.go
new file mode 100644
index 0000000..47b96a7
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/serviceresolver.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2017 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 config
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+)
+
+// ServiceResolver knows how to convert a service reference into an actual location.
+type ServiceResolver interface {
+ ResolveEndpoint(namespace, name string) (*url.URL, error)
+}
+
+type defaultServiceResolver struct{}
+
+func NewDefaultServiceResolver() ServiceResolver {
+ return &defaultServiceResolver{}
+}
+
+// ResolveEndpoint constructs a service URL from a given namespace and name
+// note that the name and namespace are required and by default all created addresses use HTTPS scheme.
+// for example:
+// name=ross namespace=andromeda resolves to https://ross.andromeda.svc:443
+func (sr defaultServiceResolver) ResolveEndpoint(namespace, name string) (*url.URL, error) {
+ if len(name) == 0 || len(namespace) == 0 {
+ return nil, errors.New("cannot resolve an empty service name or namespace")
+ }
+ return &url.URL{Scheme: "https", Host: fmt.Sprintf("%s.%s.svc:443", name, namespace)}, nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/doc.go
new file mode 100644
index 0000000..6e86a1b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2017 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 errors contains utilities for admission webhook specific errors
+package errors // import "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/errors.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/errors.go
new file mode 100644
index 0000000..2396152
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/errors.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2017 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 errors
+
+import "fmt"
+
+// ErrCallingWebhook is returned for transport-layer errors calling webhooks. It
+// represents a failure to talk to the webhook, not the webhook rejecting a
+// request.
+type ErrCallingWebhook struct {
+ WebhookName string
+ Reason error
+}
+
+func (e *ErrCallingWebhook) Error() string {
+ if e.Reason != nil {
+ return fmt.Sprintf("failed calling admission webhook %q: %v", e.WebhookName, e.Reason)
+ }
+ return fmt.Sprintf("failed calling admission webhook %q; no further details available", e.WebhookName)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/statuserror.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/statuserror.go
new file mode 100644
index 0000000..f37dec0
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors/statuserror.go
@@ -0,0 +1,47 @@
+/*
+Copyright 2017 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 errors
+
+import (
+ "fmt"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ToStatusErr returns a StatusError with information about the webhook plugin
+func ToStatusErr(webhookName string, result *metav1.Status) *apierrors.StatusError {
+ deniedBy := fmt.Sprintf("admission webhook %q denied the request", webhookName)
+ const noExp = "without explanation"
+
+ if result == nil {
+ result = &metav1.Status{Status: metav1.StatusFailure}
+ }
+
+ switch {
+ case len(result.Message) > 0:
+ result.Message = fmt.Sprintf("%s: %s", deniedBy, result.Message)
+ case len(result.Reason) > 0:
+ result.Message = fmt.Sprintf("%s: %s", deniedBy, result.Reason)
+ default:
+ result.Message = fmt.Sprintf("%s %s", deniedBy, noExp)
+ }
+
+ return &apierrors.StatusError{
+ ErrStatus: *result,
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/conversion.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/conversion.go
new file mode 100644
index 0000000..a75c63f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/conversion.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2017 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 generic
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// convertor converts objects to the desired version.
+type convertor struct {
+ Scheme *runtime.Scheme
+}
+
+// ConvertToGVK converts object to the desired gvk.
+func (c *convertor) ConvertToGVK(obj runtime.Object, gvk schema.GroupVersionKind) (runtime.Object, error) {
+ // Unlike other resources, custom resources do not have internal version, so
+ // if obj is a custom resource, it should not need conversion.
+ if obj.GetObjectKind().GroupVersionKind() == gvk {
+ return obj, nil
+ }
+ out, err := c.Scheme.New(gvk)
+ if err != nil {
+ return nil, err
+ }
+ err = c.Scheme.Convert(obj, out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// Validate checks if the conversion has a scheme.
+func (c *convertor) Validate() error {
+ if c.Scheme == nil {
+ return fmt.Errorf("the convertor requires a scheme")
+ }
+ return nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go
new file mode 100644
index 0000000..3a7edb5
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go
@@ -0,0 +1,45 @@
+/*
+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 generic
+
+import (
+ "context"
+
+ "k8s.io/api/admissionregistration/v1beta1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apiserver/pkg/admission"
+)
+
+// Source can list dynamic webhook plugins.
+type Source interface {
+ Webhooks() []v1beta1.Webhook
+ HasSynced() bool
+}
+
+// VersionedAttributes is a wrapper around the original admission attributes, adding versioned
+// variants of the object and old object.
+type VersionedAttributes struct {
+ admission.Attributes
+ VersionedOldObject runtime.Object
+ VersionedObject runtime.Object
+}
+
+// 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, hooks []*v1beta1.Webhook) error
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
new file mode 100644
index 0000000..fdcbdd9
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go
@@ -0,0 +1,202 @@
+/*
+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 generic
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "k8s.io/api/admissionregistration/v1beta1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apiserver/pkg/admission"
+ genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
+ "k8s.io/client-go/informers"
+ clientset "k8s.io/client-go/kubernetes"
+)
+
+// Webhook is an abstract admission plugin with all the infrastructure to define Admit or Validate on-top.
+type Webhook struct {
+ *admission.Handler
+
+ sourceFactory sourceFactory
+
+ hookSource Source
+ clientManager *config.ClientManager
+ convertor *convertor
+ namespaceMatcher *namespace.Matcher
+ dispatcher Dispatcher
+}
+
+var (
+ _ genericadmissioninit.WantsExternalKubeClientSet = &Webhook{}
+ _ admission.Interface = &Webhook{}
+)
+
+type sourceFactory func(f informers.SharedInformerFactory) Source
+type dispatcherFactory func(cm *config.ClientManager) Dispatcher
+
+// NewWebhook creates a new generic admission webhook.
+func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
+ kubeconfigFile, err := config.LoadConfig(configFile)
+ if err != nil {
+ return nil, err
+ }
+
+ cm, err := config.NewClientManager()
+ if err != nil {
+ return nil, err
+ }
+ authInfoResolver, err := config.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
+ if err != nil {
+ return nil, err
+ }
+ // Set defaults which may be overridden later.
+ cm.SetAuthenticationInfoResolver(authInfoResolver)
+ cm.SetServiceResolver(config.NewDefaultServiceResolver())
+
+ return &Webhook{
+ Handler: handler,
+ sourceFactory: sourceFactory,
+ clientManager: &cm,
+ convertor: &convertor{},
+ namespaceMatcher: &namespace.Matcher{},
+ dispatcher: dispatcherFactory(&cm),
+ }, nil
+}
+
+// SetAuthenticationInfoResolverWrapper sets the
+// AuthenticationInfoResolverWrapper.
+// TODO find a better way wire this, but keep this pull small for now.
+func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper config.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 config.ServiceResolver) {
+ a.clientManager.SetServiceResolver(sr)
+}
+
+// SetScheme sets a serializer(NegotiatedSerializer) which is derived from the scheme
+func (a *Webhook) SetScheme(scheme *runtime.Scheme) {
+ if scheme != nil {
+ a.convertor.Scheme = scheme
+ }
+}
+
+// SetExternalKubeClientSet implements the WantsExternalKubeInformerFactory interface.
+// It sets external ClientSet for admission plugins that need it
+func (a *Webhook) SetExternalKubeClientSet(client clientset.Interface) {
+ a.namespaceMatcher.Client = client
+}
+
+// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface.
+func (a *Webhook) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
+ namespaceInformer := f.Core().V1().Namespaces()
+ a.namespaceMatcher.NamespaceLister = namespaceInformer.Lister()
+ a.hookSource = a.sourceFactory(f)
+ a.SetReadyFunc(func() bool {
+ return namespaceInformer.Informer().HasSynced() && a.hookSource.HasSynced()
+ })
+}
+
+// ValidateInitialization implements the InitializationValidator interface.
+func (a *Webhook) ValidateInitialization() error {
+ if a.hookSource == nil {
+ return fmt.Errorf("kubernetes client is not properly setup")
+ }
+ if err := a.namespaceMatcher.Validate(); err != nil {
+ return fmt.Errorf("namespaceMatcher is not properly setup: %v", err)
+ }
+ if err := a.clientManager.Validate(); err != nil {
+ return fmt.Errorf("clientManager is not properly setup: %v", err)
+ }
+ if err := a.convertor.Validate(); err != nil {
+ return fmt.Errorf("convertor is not properly setup: %v", err)
+ }
+ 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 {
+ m := rules.Matcher{Rule: r, Attr: attr}
+ if m.Matches() {
+ matches = true
+ break
+ }
+ }
+ if !matches {
+ return false, nil
+ }
+
+ return a.namespaceMatcher.MatchNamespaceSelector(h, attr)
+}
+
+// Dispatch is called by the downstream Validate or Admit methods.
+func (a *Webhook) Dispatch(attr admission.Attributes) error {
+ if rules.IsWebhookConfigurationResource(attr) {
+ return nil
+ }
+ if !a.WaitForReady() {
+ return admission.NewForbidden(attr, fmt.Errorf("not yet ready to handle request"))
+ }
+ hooks := a.hookSource.Webhooks()
+ 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 := a.convertor.ConvertToGVK(oldObj, attr.GetKind())
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ versionedAttr.VersionedOldObject = out
+ }
+ if obj := attr.GetObject(); obj != nil {
+ out, err := a.convertor.ConvertToGVK(obj, attr.GetKind())
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ versionedAttr.VersionedObject = out
+ }
+ return a.dispatcher.Dispatch(ctx, &versionedAttr, relevantHooks)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
new file mode 100644
index 0000000..88e23c2
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go
@@ -0,0 +1,149 @@
+/*
+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 mutating delegates admission checks to dynamically configured
+// mutating webhooks.
+package mutating
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ jsonpatch "github.com/evanphx/json-patch"
+ "github.com/golang/glog"
+
+ admissionv1beta1 "k8s.io/api/admission/v1beta1"
+ "k8s.io/api/admissionregistration/v1beta1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
+ 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"
+)
+
+type mutatingDispatcher struct {
+ cm *config.ClientManager
+ plugin *Plugin
+}
+
+func newMutatingDispatcher(p *Plugin) func(cm *config.ClientManager) generic.Dispatcher {
+ return func(cm *config.ClientManager) generic.Dispatcher {
+ return &mutatingDispatcher{cm, p}
+ }
+}
+
+var _ generic.Dispatcher = &mutatingDispatcher{}
+
+func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.VersionedAttributes, relevantHooks []*v1beta1.Webhook) error {
+ for _, hook := range relevantHooks {
+ t := time.Now()
+ err := a.callAttrMutatingHook(ctx, hook, attr)
+ admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, attr.Attributes, "admit", hook.Name)
+ if err == nil {
+ continue
+ }
+
+ ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
+ if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok {
+ if ignoreClientCallFailures {
+ glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
+ utilruntime.HandleError(callErr)
+ continue
+ }
+ glog.Warningf("Failed calling webhook, failing closed %v: %v", hook.Name, err)
+ }
+ return apierrors.NewInternalError(err)
+ }
+
+ // convert attr.VersionedObject to the internal version in the underlying admission.Attributes
+ if attr.VersionedObject != nil {
+ return a.plugin.scheme.Convert(attr.VersionedObject, attr.Attributes.GetObject(), nil)
+ }
+ return nil
+}
+
+// note that callAttrMutatingHook updates attr
+func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
+ // Make the webhook request
+ request := request.CreateAdmissionReview(attr)
+ client, err := a.cm.HookClient(h)
+ if err != nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
+ }
+ response := &admissionv1beta1.AdmissionReview{}
+ if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
+ }
+
+ if response.Response == nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
+ }
+
+ if !response.Response.Allowed {
+ return webhookerrors.ToStatusErr(h.Name, response.Response.Result)
+ }
+
+ patchJS := response.Response.Patch
+ if len(patchJS) == 0 {
+ return nil
+ }
+ patchObj, err := jsonpatch.DecodePatch(patchJS)
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ if len(patchObj) == 0 {
+ return 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))
+ }
+
+ objJS, err := runtime.Encode(a.plugin.jsonSerializer, attr.VersionedObject)
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ patchedJS, err := patchObj.Apply(objJS)
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+
+ var newVersionedObject runtime.Object
+ if _, ok := attr.VersionedObject.(*unstructured.Unstructured); ok {
+ // Custom Resources don't have corresponding Go struct's.
+ // They are represented as Unstructured.
+ newVersionedObject = &unstructured.Unstructured{}
+ } else {
+ newVersionedObject, err = a.plugin.scheme.New(attr.GetKind())
+ if err != nil {
+ return 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 := a.plugin.jsonSerializer.Decode(patchedJS, nil, newVersionedObject); err != nil {
+ return apierrors.NewInternalError(err)
+ }
+ attr.VersionedObject = newVersionedObject
+ a.plugin.scheme.Default(attr.VersionedObject)
+ return nil
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/doc.go
new file mode 100644
index 0000000..d804aca
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2017 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 makes calls to mutating webhooks during the admission
+// process.
+package mutating // import "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin.go
new file mode 100644
index 0000000..f03b1b3
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin.go
@@ -0,0 +1,96 @@
+/*
+Copyright 2017 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 (
+ "fmt"
+ "io"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/serializer/json"
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/admission/configuration"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+)
+
+const (
+ // Name of admission plug-in
+ PluginName = "MutatingAdmissionWebhook"
+)
+
+// Register registers a plugin
+func Register(plugins *admission.Plugins) {
+ plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
+ plugin, err := NewMutatingWebhook(configFile)
+ if err != nil {
+ return nil, err
+ }
+
+ return plugin, nil
+ })
+}
+
+// Plugin is an implementation of admission.Interface.
+type Plugin struct {
+ *generic.Webhook
+
+ scheme *runtime.Scheme
+ jsonSerializer *json.Serializer
+}
+
+var _ admission.MutationInterface = &Plugin{}
+
+// NewMutatingWebhook returns a generic admission webhook plugin.
+func NewMutatingWebhook(configFile io.Reader) (*Plugin, error) {
+ handler := admission.NewHandler(admission.Connect, admission.Create, admission.Delete, admission.Update)
+ p := &Plugin{}
+ var err error
+ p.Webhook, err = generic.NewWebhook(handler, configFile, configuration.NewMutatingWebhookConfigurationManager, newMutatingDispatcher(p))
+ if err != nil {
+ return nil, err
+ }
+
+ return p, nil
+}
+
+// SetScheme sets a serializer(NegotiatedSerializer) which is derived from the scheme
+func (a *Plugin) SetScheme(scheme *runtime.Scheme) {
+ a.Webhook.SetScheme(scheme)
+ if scheme != nil {
+ a.scheme = scheme
+ a.jsonSerializer = json.NewSerializer(json.DefaultMetaFactory, scheme, scheme, false)
+ }
+}
+
+// ValidateInitialization implements the InitializationValidator interface.
+func (a *Plugin) ValidateInitialization() error {
+ if err := a.Webhook.ValidateInitialization(); err != nil {
+ return err
+ }
+ if a.scheme == nil {
+ return fmt.Errorf("scheme is not properly setup")
+ }
+ if a.jsonSerializer == nil {
+ return fmt.Errorf("jsonSerializer is not properly setup")
+ }
+ return nil
+}
+
+// Admit makes an admission decision based on the request attributes.
+func (a *Plugin) Admit(attr admission.Attributes) error {
+ return a.Webhook.Dispatch(attr)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/doc.go
new file mode 100644
index 0000000..d1a2853
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2017 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 namespace defines the utilities that are used by the webhook
+// plugin to decide if a webhook should be applied to an object based on its
+// namespace.
+package namespace // import "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
new file mode 100644
index 0000000..a054119
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go
@@ -0,0 +1,117 @@
+/*
+Copyright 2017 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 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"
+ clientset "k8s.io/client-go/kubernetes"
+ corelisters "k8s.io/client-go/listers/core/v1"
+)
+
+// Matcher decides if a request is exempted by the NamespaceSelector of a
+// webhook configuration.
+type Matcher struct {
+ NamespaceLister corelisters.NamespaceLister
+ Client clientset.Interface
+}
+
+// Validate checks if the Matcher has a NamespaceLister and Client.
+func (m *Matcher) Validate() error {
+ var errs []error
+ if m.NamespaceLister == nil {
+ errs = append(errs, fmt.Errorf("the namespace matcher requires a namespaceLister"))
+ }
+ if m.Client == nil {
+ errs = append(errs, fmt.Errorf("the namespace matcher requires a namespaceLister"))
+ }
+ return utilerrors.NewAggregate(errs)
+}
+
+// GetNamespaceLabels gets the labels of the namespace related to the attr.
+func (m *Matcher) GetNamespaceLabels(attr admission.Attributes) (map[string]string, error) {
+ // If the request itself is creating or updating a namespace, then get the
+ // labels from attr.Object, because namespaceLister doesn't have the latest
+ // namespace yet.
+ //
+ // However, if the request is deleting a namespace, then get the label from
+ // the namespace in the namespaceLister, because a delete request is not
+ // going to change the object, and attr.Object will be a DeleteOptions
+ // rather than a namespace object.
+ if attr.GetResource().Resource == "namespaces" &&
+ len(attr.GetSubresource()) == 0 &&
+ (attr.GetOperation() == admission.Create || attr.GetOperation() == admission.Update) {
+ accessor, err := meta.Accessor(attr.GetObject())
+ if err != nil {
+ return nil, err
+ }
+ return accessor.GetLabels(), nil
+ }
+
+ namespaceName := attr.GetNamespace()
+ namespace, err := m.NamespaceLister.Get(namespaceName)
+ if err != nil && !apierrors.IsNotFound(err) {
+ return nil, err
+ }
+ if apierrors.IsNotFound(err) {
+ // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
+ namespace, err = m.Client.CoreV1().Namespaces().Get(namespaceName, metav1.GetOptions{})
+ if err != nil {
+ return nil, err
+ }
+ }
+ return namespace.Labels, nil
+}
+
+// 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) {
+ 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
+ // namespace, it is never exempted.
+ // TODO: figure out a way selective exempt cluster scoped resources.
+ // Also update the comment in types.go
+ return true, nil
+ }
+ namespaceLabels, err := m.GetNamespaceLabels(attr)
+ // this means the namespace is not found, for backwards compatibility,
+ // return a 404
+ if apierrors.IsNotFound(err) {
+ status, ok := err.(apierrors.APIStatus)
+ if !ok {
+ return false, apierrors.NewInternalError(err)
+ }
+ return false, &apierrors.StatusError{status.Status()}
+ }
+ 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
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
new file mode 100644
index 0000000..663349a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go
@@ -0,0 +1,71 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package request
+
+import (
+ 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/util/uuid"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+)
+
+// CreateAdmissionReview creates an AdmissionReview for the provided admission.Attributes
+func CreateAdmissionReview(attr *generic.VersionedAttributes) admissionv1beta1.AdmissionReview {
+ gvk := attr.GetKind()
+ gvr := attr.GetResource()
+ aUserInfo := attr.GetUserInfo()
+ userInfo := authenticationv1.UserInfo{
+ Extra: make(map[string]authenticationv1.ExtraValue),
+ Groups: aUserInfo.GetGroups(),
+ UID: aUserInfo.GetUID(),
+ Username: aUserInfo.GetName(),
+ }
+
+ // 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(),
+ 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: attr.GetSubresource(),
+ Name: attr.GetName(),
+ Namespace: attr.GetNamespace(),
+ Operation: admissionv1beta1.Operation(attr.GetOperation()),
+ UserInfo: userInfo,
+ Object: runtime.RawExtension{
+ Object: attr.VersionedObject,
+ },
+ OldObject: runtime.RawExtension{
+ Object: attr.VersionedOldObject,
+ },
+ },
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/doc.go
new file mode 100644
index 0000000..fbacf33
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package request creates admissionReview request based on admission attributes.
+package request // import "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules/rules.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules/rules.go
new file mode 100644
index 0000000..096ab50
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules/rules.go
@@ -0,0 +1,107 @@
+/*
+Copyright 2017 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 rules
+
+import (
+ "strings"
+
+ "k8s.io/api/admissionregistration/v1beta1"
+ "k8s.io/apiserver/pkg/admission"
+)
+
+// Matcher determines if the Attr matches the Rule.
+type Matcher struct {
+ Rule v1beta1.RuleWithOperations
+ Attr admission.Attributes
+}
+
+// Matches returns if the Attr matches the Rule.
+func (r *Matcher) Matches() bool {
+ return r.operation() &&
+ r.group() &&
+ r.version() &&
+ r.resource()
+}
+
+func exactOrWildcard(items []string, requested string) bool {
+ for _, item := range items {
+ if item == "*" {
+ return true
+ }
+ if item == requested {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (r *Matcher) group() bool {
+ return exactOrWildcard(r.Rule.APIGroups, r.Attr.GetResource().Group)
+}
+
+func (r *Matcher) version() bool {
+ return exactOrWildcard(r.Rule.APIVersions, r.Attr.GetResource().Version)
+}
+
+func (r *Matcher) operation() bool {
+ attrOp := r.Attr.GetOperation()
+ for _, op := range r.Rule.Operations {
+ if op == v1beta1.OperationAll {
+ return true
+ }
+ // The constants are the same such that this is a valid cast (and this
+ // is tested).
+ if op == v1beta1.OperationType(attrOp) {
+ return true
+ }
+ }
+ return false
+}
+
+func splitResource(resSub string) (res, sub string) {
+ parts := strings.SplitN(resSub, "/", 2)
+ if len(parts) == 2 {
+ return parts[0], parts[1]
+ }
+ return parts[0], ""
+}
+
+func (r *Matcher) resource() bool {
+ opRes, opSub := r.Attr.GetResource().Resource, r.Attr.GetSubresource()
+ for _, res := range r.Rule.Resources {
+ res, sub := splitResource(res)
+ resMatch := res == "*" || res == opRes
+ subMatch := sub == "*" || sub == opSub
+ if resMatch && subMatch {
+ return true
+ }
+ }
+ return false
+}
+
+// IsWebhookConfigurationResource determines if an admission.Attributes object is describing
+// the admission of a ValidatingWebhookConfiguration or a MutatingWebhookConfiguration
+func IsWebhookConfigurationResource(attr admission.Attributes) bool {
+ gvk := attr.GetKind()
+ if gvk.Group == "admissionregistration.k8s.io" {
+ if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" {
+ return true
+ }
+ }
+ return false
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
new file mode 100644
index 0000000..528d79a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go
@@ -0,0 +1,118 @@
+/*
+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 validating
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/golang/glog"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+
+ admissionv1beta1 "k8s.io/api/admission/v1beta1"
+ "k8s.io/api/admissionregistration/v1beta1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
+ webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
+)
+
+type validatingDispatcher struct {
+ cm *config.ClientManager
+}
+
+func newValidatingDispatcher(cm *config.ClientManager) generic.Dispatcher {
+ return &validatingDispatcher{cm}
+}
+
+var _ generic.Dispatcher = &validatingDispatcher{}
+
+func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.VersionedAttributes, relevantHooks []*v1beta1.Webhook) error {
+ wg := sync.WaitGroup{}
+ errCh := make(chan error, len(relevantHooks))
+ wg.Add(len(relevantHooks))
+ for i := range relevantHooks {
+ go func(hook *v1beta1.Webhook) {
+ defer wg.Done()
+
+ t := time.Now()
+ err := d.callHook(ctx, hook, attr)
+ admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, attr.Attributes, "validating", hook.Name)
+ if err == nil {
+ return
+ }
+
+ ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
+ if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok {
+ if ignoreClientCallFailures {
+ glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
+ utilruntime.HandleError(callErr)
+ return
+ }
+
+ glog.Warningf("Failed calling webhook, failing closed %v: %v", hook.Name, err)
+ errCh <- apierrors.NewInternalError(err)
+ return
+ }
+
+ glog.Warningf("rejected by webhook %q: %#v", hook.Name, err)
+ errCh <- err
+ }(relevantHooks[i])
+ }
+ wg.Wait()
+ close(errCh)
+
+ var errs []error
+ for e := range errCh {
+ errs = append(errs, e)
+ }
+ if len(errs) == 0 {
+ return nil
+ }
+ if len(errs) > 1 {
+ for i := 1; i < len(errs); i++ {
+ // TODO: merge status errors; until then, just return the first one.
+ utilruntime.HandleError(errs[i])
+ }
+ }
+ return errs[0]
+}
+
+func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
+ // Make the webhook request
+ request := request.CreateAdmissionReview(attr)
+ client, err := d.cm.HookClient(h)
+ if err != nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
+ }
+ response := &admissionv1beta1.AdmissionReview{}
+ if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
+ }
+
+ if response.Response == nil {
+ return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
+ }
+ if response.Response.Allowed {
+ return nil
+ }
+ return webhookerrors.ToStatusErr(h.Name, response.Response.Result)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/doc.go
new file mode 100644
index 0000000..ede53c6
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2017 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 validating makes calls to validating (i.e., non-mutating) webhooks
+// during the admission process.
+package validating // import "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
new file mode 100644
index 0000000..8417ccf
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go
@@ -0,0 +1,64 @@
+/*
+Copyright 2017 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 validating
+
+import (
+ "io"
+
+ "k8s.io/apiserver/pkg/admission"
+ "k8s.io/apiserver/pkg/admission/configuration"
+ "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
+)
+
+const (
+ // Name of admission plug-in
+ PluginName = "ValidatingAdmissionWebhook"
+)
+
+// Register registers a plugin
+func Register(plugins *admission.Plugins) {
+ plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
+ plugin, err := NewValidatingAdmissionWebhook(configFile)
+ if err != nil {
+ return nil, err
+ }
+
+ return plugin, nil
+ })
+}
+
+// Plugin is an implementation of admission.Interface.
+type Plugin struct {
+ *generic.Webhook
+}
+
+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)
+ if err != nil {
+ return nil, err
+ }
+ return &Plugin{webhook}, nil
+}
+
+// Validate makes an admission decision based on the request attributes.
+func (a *Plugin) Validate(attr admission.Attributes) error {
+ return a.Webhook.Dispatch(attr)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugins.go b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugins.go
new file mode 100644
index 0000000..c17d62c
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/admission/plugins.go
@@ -0,0 +1,208 @@
+/*
+Copyright 2014 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
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/golang/glog"
+)
+
+// Factory is a function that returns an Interface for admission decisions.
+// The config parameter provides an io.Reader handler to the factory in
+// order to load specific configurations. If no configuration is provided
+// the parameter is nil.
+type Factory func(config io.Reader) (Interface, error)
+
+type Plugins struct {
+ lock sync.Mutex
+ registry map[string]Factory
+}
+
+func NewPlugins() *Plugins {
+ return &Plugins{}
+}
+
+// All registered admission options.
+var (
+ // PluginEnabledFn checks whether a plugin is enabled. By default, if you ask about it, it's enabled.
+ PluginEnabledFn = func(name string, config io.Reader) bool {
+ return true
+ }
+)
+
+// PluginEnabledFunc is a function type that can provide an external check on whether an admission plugin may be enabled
+type PluginEnabledFunc func(name string, config io.Reader) bool
+
+// Registered enumerates the names of all registered plugins.
+func (ps *Plugins) Registered() []string {
+ ps.lock.Lock()
+ defer ps.lock.Unlock()
+ keys := []string{}
+ for k := range ps.registry {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+// Register registers a plugin Factory by name. This
+// is expected to happen during app startup.
+func (ps *Plugins) Register(name string, plugin Factory) {
+ ps.lock.Lock()
+ defer ps.lock.Unlock()
+ if ps.registry != nil {
+ _, found := ps.registry[name]
+ if found {
+ glog.Fatalf("Admission plugin %q was registered twice", name)
+ }
+ } else {
+ ps.registry = map[string]Factory{}
+ }
+
+ glog.V(1).Infof("Registered admission plugin %q", name)
+ ps.registry[name] = plugin
+}
+
+// getPlugin creates an instance of the named plugin. It returns `false` if the
+// the name is not known. The error is returned only when the named provider was
+// known but failed to initialize. The config parameter specifies the io.Reader
+// handler of the configuration file for the cloud provider, or nil for no configuration.
+func (ps *Plugins) getPlugin(name string, config io.Reader) (Interface, bool, error) {
+ ps.lock.Lock()
+ defer ps.lock.Unlock()
+ f, found := ps.registry[name]
+ if !found {
+ return nil, false, nil
+ }
+
+ config1, config2, err := splitStream(config)
+ if err != nil {
+ return nil, true, err
+ }
+ if !PluginEnabledFn(name, config1) {
+ return nil, true, nil
+ }
+
+ ret, err := f(config2)
+ return ret, true, err
+}
+
+// splitStream reads the stream bytes and constructs two copies of it.
+func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
+ if config == nil || reflect.ValueOf(config).IsNil() {
+ return nil, nil, nil
+ }
+
+ configBytes, err := ioutil.ReadAll(config)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil
+}
+
+// NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all
+// the given plugins.
+func (ps *Plugins) NewFromPlugins(pluginNames []string, configProvider ConfigProvider, pluginInitializer PluginInitializer, decorator Decorator) (Interface, error) {
+ handlers := []Interface{}
+ mutationPlugins := []string{}
+ validationPlugins := []string{}
+ for _, pluginName := range pluginNames {
+ pluginConfig, err := configProvider.ConfigFor(pluginName)
+ if err != nil {
+ return nil, err
+ }
+
+ plugin, err := ps.InitPlugin(pluginName, pluginConfig, pluginInitializer)
+ if err != nil {
+ return nil, err
+ }
+ if plugin != nil {
+ if decorator != nil {
+ handlers = append(handlers, decorator.Decorate(plugin, pluginName))
+ } else {
+ handlers = append(handlers, plugin)
+ }
+
+ if _, ok := plugin.(MutationInterface); ok {
+ mutationPlugins = append(mutationPlugins, pluginName)
+ }
+ if _, ok := plugin.(ValidationInterface); ok {
+ validationPlugins = append(validationPlugins, pluginName)
+ }
+ }
+ }
+ if len(mutationPlugins) != 0 {
+ glog.Infof("Loaded %d mutating admission controller(s) successfully in the following order: %s.", len(mutationPlugins), strings.Join(mutationPlugins, ","))
+ }
+ if len(validationPlugins) != 0 {
+ glog.Infof("Loaded %d validating admission controller(s) successfully in the following order: %s.", len(validationPlugins), strings.Join(validationPlugins, ","))
+ }
+ return chainAdmissionHandler(handlers), nil
+}
+
+// InitPlugin creates an instance of the named interface.
+func (ps *Plugins) InitPlugin(name string, config io.Reader, pluginInitializer PluginInitializer) (Interface, error) {
+ if name == "" {
+ glog.Info("No admission plugin specified.")
+ return nil, nil
+ }
+
+ plugin, found, err := ps.getPlugin(name, config)
+ if err != nil {
+ return nil, fmt.Errorf("couldn't init admission plugin %q: %v", name, err)
+ }
+ if !found {
+ return nil, fmt.Errorf("unknown admission plugin: %s", name)
+ }
+
+ pluginInitializer.Initialize(plugin)
+ // ensure that plugins have been properly initialized
+ if err := ValidateInitialization(plugin); err != nil {
+ return nil, fmt.Errorf("failed to initialize admission plugin %q: %v", name, err)
+ }
+
+ return plugin, nil
+}
+
+// ValidateInitialization will call the InitializationValidate function in each plugin if they implement
+// the InitializationValidator interface.
+func ValidateInitialization(plugin Interface) error {
+ if validater, ok := plugin.(InitializationValidator); ok {
+ err := validater.ValidateInitialization()
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type PluginInitializers []PluginInitializer
+
+func (pp PluginInitializers) Initialize(plugin Interface) {
+ for _, p := range pp {
+ p.Initialize(plugin)
+ }
+}