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