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)
+	}
+}