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/endpoints/handlers/create.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go
new file mode 100644
index 0000000..5427600
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.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 handlers
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/audit"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	utiltrace "k8s.io/apiserver/pkg/util/trace"
+)
+
+func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		// For performance tracking purposes.
+		trace := utiltrace.New("Create " + req.URL.Path)
+		defer trace.LogIfLong(500 * time.Millisecond)
+
+		if isDryRun(req.URL) {
+			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
+			return
+		}
+
+		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
+		timeout := parseTimeout(req.URL.Query().Get("timeout"))
+
+		var (
+			namespace, name string
+			err             error
+		)
+		if includeName {
+			namespace, name, err = scope.Namer.Name(req)
+		} else {
+			namespace, err = scope.Namer.Namespace(req)
+		}
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+
+		gv := scope.Kind.GroupVersion()
+		s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		decoder := scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
+
+		body, err := readBody(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		defaultGVK := scope.Kind
+		original := r.New()
+		trace.Step("About to convert to expected version")
+		obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
+		if err != nil {
+			err = transformDecodeError(scope.Typer, err, original, gvk, body)
+			scope.err(err, w, req)
+			return
+		}
+		if gvk.GroupVersion() != gv {
+			err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String()))
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Conversion done")
+
+		ae := request.AuditEventFrom(ctx)
+		admit = admission.WithAudit(admit, ae)
+		audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
+
+		userInfo, _ := request.UserFrom(ctx)
+		admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo)
+		if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
+			err = mutatingAdmission.Admit(admissionAttributes)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+		}
+
+		// TODO: replace with content type negotiation?
+		includeUninitialized := req.URL.Query().Get("includeUninitialized") == "1"
+
+		trace.Step("About to store object in database")
+		result, err := finishRequest(timeout, func() (runtime.Object, error) {
+			return r.Create(
+				ctx,
+				name,
+				obj,
+				rest.AdmissionToValidateObjectFunc(admit, admissionAttributes),
+				includeUninitialized,
+			)
+		})
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Object stored in database")
+
+		requestInfo, ok := request.RequestInfoFrom(ctx)
+		if !ok {
+			scope.err(fmt.Errorf("missing requestInfo"), w, req)
+			return
+		}
+		if err := setSelfLink(result, requestInfo, scope.Namer); err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Self-link added")
+
+		// If the object is partially initialized, always indicate it via StatusAccepted
+		code := http.StatusCreated
+		if accessor, err := meta.Accessor(result); err == nil {
+			if accessor.GetInitializers() != nil {
+				code = http.StatusAccepted
+			}
+		}
+		status, ok := result.(*metav1.Status)
+		if ok && err == nil && status.Code == 0 {
+			status.Code = int32(code)
+		}
+
+		transformResponseObject(ctx, scope, req, w, code, result)
+	}
+}
+
+// CreateNamedResource returns a function that will handle a resource creation with name.
+func CreateNamedResource(r rest.NamedCreater, scope RequestScope, admission admission.Interface) http.HandlerFunc {
+	return createHandler(r, scope, admission, true)
+}
+
+// CreateResource returns a function that will handle a resource creation.
+func CreateResource(r rest.Creater, scope RequestScope, admission admission.Interface) http.HandlerFunc {
+	return createHandler(&namedCreaterAdapter{r}, scope, admission, false)
+}
+
+type namedCreaterAdapter struct {
+	rest.Creater
+}
+
+func (c *namedCreaterAdapter) Create(ctx context.Context, name string, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
+	return c.Creater.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
new file mode 100644
index 0000000..03576d7
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go
@@ -0,0 +1,296 @@
+/*
+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 handlers
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/audit"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	utiltrace "k8s.io/apiserver/pkg/util/trace"
+)
+
+// DeleteResource returns a function that will handle a resource deletion
+// TODO admission here becomes solely validating admission
+func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		// For performance tracking purposes.
+		trace := utiltrace.New("Delete " + req.URL.Path)
+		defer trace.LogIfLong(500 * time.Millisecond)
+
+		if isDryRun(req.URL) {
+			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
+			return
+		}
+
+		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
+		timeout := parseTimeout(req.URL.Query().Get("timeout"))
+
+		namespace, name, err := scope.Namer.Name(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+		ae := request.AuditEventFrom(ctx)
+		admit = admission.WithAudit(admit, ae)
+
+		options := &metav1.DeleteOptions{}
+		if allowsOptions {
+			body, err := readBody(req)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			if len(body) > 0 {
+				s, err := negotiation.NegotiateInputSerializer(req, false, metainternalversion.Codecs)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+				// For backwards compatibility, we need to allow existing clients to submit per group DeleteOptions
+				// It is also allowed to pass a body with meta.k8s.io/v1.DeleteOptions
+				defaultGVK := scope.MetaGroupVersion.WithKind("DeleteOptions")
+				obj, _, err := metainternalversion.Codecs.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+				if obj != options {
+					scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), w, req)
+					return
+				}
+				trace.Step("Decoded delete options")
+
+				ae := request.AuditEventFrom(ctx)
+				audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
+				trace.Step("Recorded the audit event")
+			} else {
+				if values := req.URL.Query(); len(values) > 0 {
+					if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, options); err != nil {
+						err = errors.NewBadRequest(err.Error())
+						scope.err(err, w, req)
+						return
+					}
+				}
+			}
+		}
+
+		trace.Step("About to check admission control")
+		if admit != nil && admit.Handles(admission.Delete) {
+			userInfo, _ := request.UserFrom(ctx)
+			attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo)
+			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
+				if err := mutatingAdmission.Admit(attrs); err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+			if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
+				if err := validatingAdmission.Validate(attrs); err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+		}
+
+		trace.Step("About to delete object from database")
+		wasDeleted := true
+		result, err := finishRequest(timeout, func() (runtime.Object, error) {
+			obj, deleted, err := r.Delete(ctx, name, options)
+			wasDeleted = deleted
+			return obj, err
+		})
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Object deleted from database")
+
+		status := http.StatusOK
+		// Return http.StatusAccepted if the resource was not deleted immediately and
+		// user requested cascading deletion by setting OrphanDependents=false.
+		// Note: We want to do this always if resource was not deleted immediately, but
+		// that will break existing clients.
+		// Other cases where resource is not instantly deleted are: namespace deletion
+		// and pod graceful deletion.
+		if !wasDeleted && options.OrphanDependents != nil && *options.OrphanDependents == false {
+			status = http.StatusAccepted
+		}
+		// if the rest.Deleter returns a nil object, fill out a status. Callers may return a valid
+		// object with the response.
+		if result == nil {
+			result = &metav1.Status{
+				Status: metav1.StatusSuccess,
+				Code:   int32(status),
+				Details: &metav1.StatusDetails{
+					Name: name,
+					Kind: scope.Kind.Kind,
+				},
+			}
+		} else {
+			// when a non-status response is returned, set the self link
+			requestInfo, ok := request.RequestInfoFrom(ctx)
+			if !ok {
+				scope.err(fmt.Errorf("missing requestInfo"), w, req)
+				return
+			}
+			if _, ok := result.(*metav1.Status); !ok {
+				if err := setSelfLink(result, requestInfo, scope.Namer); err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+		}
+
+		transformResponseObject(ctx, scope, req, w, status, result)
+	}
+}
+
+// DeleteCollection returns a function that will handle a collection deletion
+func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		if isDryRun(req.URL) {
+			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
+			return
+		}
+
+		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
+		timeout := parseTimeout(req.URL.Query().Get("timeout"))
+
+		namespace, err := scope.Namer.Namespace(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+		ae := request.AuditEventFrom(ctx)
+		admit = admission.WithAudit(admit, ae)
+
+		if admit != nil && admit.Handles(admission.Delete) {
+			userInfo, _ := request.UserFrom(ctx)
+			attrs := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo)
+			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
+				err = mutatingAdmission.Admit(attrs)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+
+			if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
+				err = validatingAdmission.Validate(attrs)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+		}
+
+		listOptions := metainternalversion.ListOptions{}
+		if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, &listOptions); err != nil {
+			err = errors.NewBadRequest(err.Error())
+			scope.err(err, w, req)
+			return
+		}
+
+		// transform fields
+		// TODO: DecodeParametersInto should do this.
+		if listOptions.FieldSelector != nil {
+			fn := func(label, value string) (newLabel, newValue string, err error) {
+				return scope.Convertor.ConvertFieldLabel(scope.Kind.GroupVersion().String(), scope.Kind.Kind, label, value)
+			}
+			if listOptions.FieldSelector, err = listOptions.FieldSelector.Transform(fn); err != nil {
+				// TODO: allow bad request to set field causes based on query parameters
+				err = errors.NewBadRequest(err.Error())
+				scope.err(err, w, req)
+				return
+			}
+		}
+
+		options := &metav1.DeleteOptions{}
+		if checkBody {
+			body, err := readBody(req)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			if len(body) > 0 {
+				s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+				defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
+				obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+				if obj != options {
+					scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), w, req)
+					return
+				}
+
+				ae := request.AuditEventFrom(ctx)
+				audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
+			}
+		}
+
+		result, err := finishRequest(timeout, func() (runtime.Object, error) {
+			return r.DeleteCollection(ctx, options, &listOptions)
+		})
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		// if the rest.Deleter returns a nil object, fill out a status. Callers may return a valid
+		// object with the response.
+		if result == nil {
+			result = &metav1.Status{
+				Status: metav1.StatusSuccess,
+				Code:   http.StatusOK,
+				Details: &metav1.StatusDetails{
+					Kind: scope.Kind.Kind,
+				},
+			}
+		} else {
+			// when a non-status response is returned, set the self link
+			if _, ok := result.(*metav1.Status); !ok {
+				if _, err := setListSelfLink(result, ctx, req, scope.Namer); err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+		}
+
+		transformResponseObject(ctx, scope, req, w, http.StatusOK, result)
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/doc.go
new file mode 100644
index 0000000..d398338
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2016 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 handlers contains HTTP handlers to implement the apiserver APIs.
+package handlers // import "k8s.io/apiserver/pkg/endpoints/handlers"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/get.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/get.go
new file mode 100644
index 0000000..7672859
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/get.go
@@ -0,0 +1,285 @@
+/*
+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 handlers
+
+import (
+	"context"
+	"fmt"
+	"math/rand"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/golang/glog"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/fields"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apiserver/pkg/endpoints/metrics"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	utiltrace "k8s.io/apiserver/pkg/util/trace"
+)
+
+// getterFunc performs a get request with the given context and object name. The request
+// may be used to deserialize an options object to pass to the getter.
+type getterFunc func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error)
+
+// getResourceHandler is an HTTP handler function for get requests. It delegates to the
+// passed-in getterFunc to perform the actual get.
+func getResourceHandler(scope RequestScope, getter getterFunc) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		trace := utiltrace.New("Get " + req.URL.Path)
+		defer trace.LogIfLong(500 * time.Millisecond)
+
+		namespace, name, err := scope.Namer.Name(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+
+		result, err := getter(ctx, name, req, trace)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		requestInfo, ok := request.RequestInfoFrom(ctx)
+		if !ok {
+			scope.err(fmt.Errorf("missing requestInfo"), w, req)
+			return
+		}
+		if err := setSelfLink(result, requestInfo, scope.Namer); err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		trace.Step("About to write a response")
+		transformResponseObject(ctx, scope, req, w, http.StatusOK, result)
+	}
+}
+
+// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
+func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) http.HandlerFunc {
+	return getResourceHandler(scope,
+		func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
+			// check for export
+			options := metav1.GetOptions{}
+			if values := req.URL.Query(); len(values) > 0 {
+				exports := metav1.ExportOptions{}
+				if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &exports); err != nil {
+					err = errors.NewBadRequest(err.Error())
+					return nil, err
+				}
+				if exports.Export {
+					if e == nil {
+						return nil, errors.NewBadRequest(fmt.Sprintf("export of %q is not supported", scope.Resource.Resource))
+					}
+					return e.Export(ctx, name, exports)
+				}
+				if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &options); err != nil {
+					err = errors.NewBadRequest(err.Error())
+					return nil, err
+				}
+			}
+			if trace != nil {
+				trace.Step("About to Get from storage")
+			}
+			return r.Get(ctx, name, &options)
+		})
+}
+
+// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
+func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope, isSubresource bool) http.HandlerFunc {
+	return getResourceHandler(scope,
+		func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
+			opts, subpath, subpathKey := r.NewGetOptions()
+			trace.Step("About to process Get options")
+			if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
+				err = errors.NewBadRequest(err.Error())
+				return nil, err
+			}
+			if trace != nil {
+				trace.Step("About to Get from storage")
+			}
+			return r.Get(ctx, name, opts)
+		})
+}
+
+// getRequestOptions parses out options and can include path information.  The path information shouldn't include the subresource.
+func getRequestOptions(req *http.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string, isSubresource bool) error {
+	if into == nil {
+		return nil
+	}
+
+	query := req.URL.Query()
+	if subpath {
+		newQuery := make(url.Values)
+		for k, v := range query {
+			newQuery[k] = v
+		}
+
+		ctx := req.Context()
+		requestInfo, _ := request.RequestInfoFrom(ctx)
+		startingIndex := 2
+		if isSubresource {
+			startingIndex = 3
+		}
+
+		p := strings.Join(requestInfo.Parts[startingIndex:], "/")
+
+		// ensure non-empty subpaths correctly reflect a leading slash
+		if len(p) > 0 && !strings.HasPrefix(p, "/") {
+			p = "/" + p
+		}
+
+		// ensure subpaths correctly reflect the presence of a trailing slash on the original request
+		if strings.HasSuffix(requestInfo.Path, "/") && !strings.HasSuffix(p, "/") {
+			p += "/"
+		}
+
+		newQuery[subpathKey] = []string{p}
+		query = newQuery
+	}
+	return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into)
+}
+
+func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		// For performance tracking purposes.
+		trace := utiltrace.New("List " + req.URL.Path)
+
+		namespace, err := scope.Namer.Namespace(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		// Watches for single objects are routed to this function.
+		// Treat a name parameter the same as a field selector entry.
+		hasName := true
+		_, name, err := scope.Namer.Name(req)
+		if err != nil {
+			hasName = false
+		}
+
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+
+		opts := metainternalversion.ListOptions{}
+		if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, &opts); err != nil {
+			err = errors.NewBadRequest(err.Error())
+			scope.err(err, w, req)
+			return
+		}
+
+		// transform fields
+		// TODO: DecodeParametersInto should do this.
+		if opts.FieldSelector != nil {
+			fn := func(label, value string) (newLabel, newValue string, err error) {
+				return scope.Convertor.ConvertFieldLabel(scope.Kind.GroupVersion().String(), scope.Kind.Kind, label, value)
+			}
+			if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil {
+				// TODO: allow bad request to set field causes based on query parameters
+				err = errors.NewBadRequest(err.Error())
+				scope.err(err, w, req)
+				return
+			}
+		}
+
+		if hasName {
+			// metadata.name is the canonical internal name.
+			// SelectionPredicate will notice that this is a request for
+			// a single object and optimize the storage query accordingly.
+			nameSelector := fields.OneTermEqualSelector("metadata.name", name)
+
+			// Note that fieldSelector setting explicitly the "metadata.name"
+			// will result in reaching this branch (as the value of that field
+			// is propagated to requestInfo as the name parameter.
+			// That said, the allowed field selectors in this branch are:
+			// nil, fields.Everything and field selector matching metadata.name
+			// for our name.
+			if opts.FieldSelector != nil && !opts.FieldSelector.Empty() {
+				selectedName, ok := opts.FieldSelector.RequiresExactMatch("metadata.name")
+				if !ok || name != selectedName {
+					scope.err(errors.NewBadRequest("fieldSelector metadata.name doesn't match requested name"), w, req)
+					return
+				}
+			} else {
+				opts.FieldSelector = nameSelector
+			}
+		}
+
+		if opts.Watch || forceWatch {
+			if rw == nil {
+				scope.err(errors.NewMethodNotSupported(scope.Resource.GroupResource(), "watch"), w, req)
+				return
+			}
+			// TODO: Currently we explicitly ignore ?timeout= and use only ?timeoutSeconds=.
+			timeout := time.Duration(0)
+			if opts.TimeoutSeconds != nil {
+				timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+			}
+			if timeout == 0 && minRequestTimeout > 0 {
+				timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0))
+			}
+			glog.V(2).Infof("Starting watch for %s, rv=%s labels=%s fields=%s timeout=%s", req.URL.Path, opts.ResourceVersion, opts.LabelSelector, opts.FieldSelector, timeout)
+
+			watcher, err := rw.Watch(ctx, &opts)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			requestInfo, _ := request.RequestInfoFrom(ctx)
+			metrics.RecordLongRunning(req, requestInfo, func() {
+				serveWatch(watcher, scope, req, w, timeout)
+			})
+			return
+		}
+
+		// Log only long List requests (ignore Watch).
+		defer trace.LogIfLong(500 * time.Millisecond)
+		trace.Step("About to List from storage")
+		result, err := r.List(ctx, &opts)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Listing from storage done")
+		numberOfItems, err := setListSelfLink(result, ctx, req, scope.Namer)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Self-linking done")
+		// Ensure empty lists return a non-nil items slice
+		if numberOfItems == 0 && meta.IsListType(result) {
+			if err := meta.SetList(result, []runtime.Object{}); err != nil {
+				scope.err(err, w, req)
+				return
+			}
+		}
+
+		transformResponseObject(ctx, scope, req, w, http.StatusOK, result)
+		trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems))
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/namer.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/namer.go
new file mode 100644
index 0000000..16b4199
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/namer.go
@@ -0,0 +1,135 @@
+/*
+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 handlers
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apiserver/pkg/endpoints/request"
+)
+
+// ScopeNamer handles accessing names from requests and objects
+type ScopeNamer interface {
+	// Namespace returns the appropriate namespace value from the request (may be empty) or an
+	// error.
+	Namespace(req *http.Request) (namespace string, err error)
+	// Name returns the name from the request, and an optional namespace value if this is a namespace
+	// scoped call. An error is returned if the name is not available.
+	Name(req *http.Request) (namespace, name string, err error)
+	// ObjectName returns the namespace and name from an object if they exist, or an error if the object
+	// does not support names.
+	ObjectName(obj runtime.Object) (namespace, name string, err error)
+	// SetSelfLink sets the provided URL onto the object. The method should return nil if the object
+	// does not support selfLinks.
+	SetSelfLink(obj runtime.Object, url string) error
+	// GenerateLink creates an encoded URI for a given runtime object that represents the canonical path
+	// and query.
+	GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error)
+	// GenerateListLink creates an encoded URI for a list that represents the canonical path and query.
+	GenerateListLink(req *http.Request) (uri string, err error)
+}
+
+type ContextBasedNaming struct {
+	SelfLinker    runtime.SelfLinker
+	ClusterScoped bool
+
+	SelfLinkPathPrefix string
+	SelfLinkPathSuffix string
+}
+
+// ContextBasedNaming implements ScopeNamer
+var _ ScopeNamer = ContextBasedNaming{}
+
+func (n ContextBasedNaming) SetSelfLink(obj runtime.Object, url string) error {
+	return n.SelfLinker.SetSelfLink(obj, url)
+}
+
+func (n ContextBasedNaming) Namespace(req *http.Request) (namespace string, err error) {
+	requestInfo, ok := request.RequestInfoFrom(req.Context())
+	if !ok {
+		return "", fmt.Errorf("missing requestInfo")
+	}
+	return requestInfo.Namespace, nil
+}
+
+func (n ContextBasedNaming) Name(req *http.Request) (namespace, name string, err error) {
+	requestInfo, ok := request.RequestInfoFrom(req.Context())
+	if !ok {
+		return "", "", fmt.Errorf("missing requestInfo")
+	}
+	ns, err := n.Namespace(req)
+	if err != nil {
+		return "", "", err
+	}
+
+	if len(requestInfo.Name) == 0 {
+		return "", "", errEmptyName
+	}
+	return ns, requestInfo.Name, nil
+}
+
+func (n ContextBasedNaming) GenerateLink(requestInfo *request.RequestInfo, obj runtime.Object) (uri string, err error) {
+	namespace, name, err := n.ObjectName(obj)
+	if err == errEmptyName && len(requestInfo.Name) > 0 {
+		name = requestInfo.Name
+	} else if err != nil {
+		return "", err
+	}
+	if len(namespace) == 0 && len(requestInfo.Namespace) > 0 {
+		namespace = requestInfo.Namespace
+	}
+
+	if n.ClusterScoped {
+		return n.SelfLinkPathPrefix + url.QueryEscape(name) + n.SelfLinkPathSuffix, nil
+	}
+
+	return n.SelfLinkPathPrefix +
+			url.QueryEscape(namespace) +
+			"/" + url.QueryEscape(requestInfo.Resource) + "/" +
+			url.QueryEscape(name) +
+			n.SelfLinkPathSuffix,
+		nil
+}
+
+func (n ContextBasedNaming) GenerateListLink(req *http.Request) (uri string, err error) {
+	if len(req.URL.RawPath) > 0 {
+		return req.URL.RawPath, nil
+	}
+	return req.URL.EscapedPath(), nil
+}
+
+func (n ContextBasedNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) {
+	name, err = n.SelfLinker.Name(obj)
+	if err != nil {
+		return "", "", err
+	}
+	if len(name) == 0 {
+		return "", "", errEmptyName
+	}
+	namespace, err = n.SelfLinker.Namespace(obj)
+	if err != nil {
+		return "", "", err
+	}
+	return namespace, name, err
+}
+
+// errEmptyName is returned when API requests do not fill the name section of the path.
+var errEmptyName = errors.NewBadRequest("name must be provided")
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/doc.go
new file mode 100644
index 0000000..80f4feb
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2016 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 negotiation contains media type negotiation logic.
+package negotiation // import "k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/errors.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/errors.go
new file mode 100644
index 0000000..93b17cf
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/errors.go
@@ -0,0 +1,69 @@
+/*
+Copyright 2016 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 negotiation
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// errNotAcceptable indicates Accept negotiation has failed
+type errNotAcceptable struct {
+	accepted []string
+}
+
+func NewNotAcceptableError(accepted []string) error {
+	return errNotAcceptable{accepted}
+}
+
+func (e errNotAcceptable) Error() string {
+	return fmt.Sprintf("only the following media types are accepted: %v", strings.Join(e.accepted, ", "))
+}
+
+func (e errNotAcceptable) Status() metav1.Status {
+	return metav1.Status{
+		Status:  metav1.StatusFailure,
+		Code:    http.StatusNotAcceptable,
+		Reason:  metav1.StatusReasonNotAcceptable,
+		Message: e.Error(),
+	}
+}
+
+// errUnsupportedMediaType indicates Content-Type is not recognized
+type errUnsupportedMediaType struct {
+	accepted []string
+}
+
+func NewUnsupportedMediaTypeError(accepted []string) error {
+	return errUnsupportedMediaType{accepted}
+}
+
+func (e errUnsupportedMediaType) Error() string {
+	return fmt.Sprintf("the body of the request was in an unknown format - accepted media types include: %v", strings.Join(e.accepted, ", "))
+}
+
+func (e errUnsupportedMediaType) Status() metav1.Status {
+	return metav1.Status{
+		Status:  metav1.StatusFailure,
+		Code:    http.StatusUnsupportedMediaType,
+		Reason:  metav1.StatusReasonUnsupportedMediaType,
+		Message: e.Error(),
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go
new file mode 100644
index 0000000..f9bb47b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go
@@ -0,0 +1,305 @@
+/*
+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 negotiation
+
+import (
+	"mime"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"bitbucket.org/ww/goautoneg"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// MediaTypesForSerializer returns a list of media and stream media types for the server.
+func MediaTypesForSerializer(ns runtime.NegotiatedSerializer) (mediaTypes, streamMediaTypes []string) {
+	for _, info := range ns.SupportedMediaTypes() {
+		mediaTypes = append(mediaTypes, info.MediaType)
+		if info.StreamSerializer != nil {
+			// stream=watch is the existing mime-type parameter for watch
+			streamMediaTypes = append(streamMediaTypes, info.MediaType+";stream=watch")
+		}
+	}
+	return mediaTypes, streamMediaTypes
+}
+
+// NegotiateOutputMediaType negotiates the output structured media type and a serializer, or
+// returns an error.
+func NegotiateOutputMediaType(req *http.Request, ns runtime.NegotiatedSerializer, restrictions EndpointRestrictions) (MediaTypeOptions, runtime.SerializerInfo, error) {
+	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), restrictions)
+	if !ok {
+		supported, _ := MediaTypesForSerializer(ns)
+		return mediaType, runtime.SerializerInfo{}, NewNotAcceptableError(supported)
+	}
+	// TODO: move into resthandler
+	info := mediaType.Accepted.Serializer
+	if (mediaType.Pretty || isPrettyPrint(req)) && info.PrettySerializer != nil {
+		info.Serializer = info.PrettySerializer
+	}
+	return mediaType, info, nil
+}
+
+// NegotiateOutputSerializer returns a serializer for the output.
+func NegotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
+	_, info, err := NegotiateOutputMediaType(req, ns, DefaultEndpointRestrictions)
+	return info, err
+}
+
+// NegotiateOutputStreamSerializer returns a stream serializer for the given request.
+func NegotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
+	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), DefaultEndpointRestrictions)
+	if !ok || mediaType.Accepted.Serializer.StreamSerializer == nil {
+		_, supported := MediaTypesForSerializer(ns)
+		return runtime.SerializerInfo{}, NewNotAcceptableError(supported)
+	}
+	return mediaType.Accepted.Serializer, nil
+}
+
+// NegotiateInputSerializer returns the input serializer for the provided request.
+func NegotiateInputSerializer(req *http.Request, streaming bool, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
+	mediaType := req.Header.Get("Content-Type")
+	return NegotiateInputSerializerForMediaType(mediaType, streaming, ns)
+}
+
+// NegotiateInputSerializerForMediaType returns the appropriate serializer for the given media type or an error.
+func NegotiateInputSerializerForMediaType(mediaType string, streaming bool, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
+	mediaTypes := ns.SupportedMediaTypes()
+	if len(mediaType) == 0 {
+		mediaType = mediaTypes[0].MediaType
+	}
+	if mediaType, _, err := mime.ParseMediaType(mediaType); err == nil {
+		for _, info := range mediaTypes {
+			if info.MediaType != mediaType {
+				continue
+			}
+			return info, nil
+		}
+	}
+
+	supported, streamingSupported := MediaTypesForSerializer(ns)
+	if streaming {
+		return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(streamingSupported)
+	}
+	return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(supported)
+}
+
+// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
+// matches known "human" clients.
+func isPrettyPrint(req *http.Request) bool {
+	// DEPRECATED: should be part of the content type
+	if req.URL != nil {
+		pp := req.URL.Query().Get("pretty")
+		if len(pp) > 0 {
+			pretty, _ := strconv.ParseBool(pp)
+			return pretty
+		}
+	}
+	userAgent := req.UserAgent()
+	// This covers basic all browsers and cli http tools
+	if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") {
+		return true
+	}
+	return false
+}
+
+// EndpointRestrictions is an interface that allows content-type negotiation
+// to verify server support for specific options
+type EndpointRestrictions interface {
+	// AllowsConversion should return true if the specified group version kind
+	// is an allowed target object.
+	AllowsConversion(schema.GroupVersionKind) bool
+	// AllowsServerVersion should return true if the specified version is valid
+	// for the server group.
+	AllowsServerVersion(version string) bool
+	// AllowsStreamSchema should return true if the specified stream schema is
+	// valid for the server group.
+	AllowsStreamSchema(schema string) bool
+}
+
+var DefaultEndpointRestrictions = emptyEndpointRestrictions{}
+
+type emptyEndpointRestrictions struct{}
+
+func (emptyEndpointRestrictions) AllowsConversion(schema.GroupVersionKind) bool { return false }
+func (emptyEndpointRestrictions) AllowsServerVersion(string) bool               { return false }
+func (emptyEndpointRestrictions) AllowsStreamSchema(s string) bool              { return s == "watch" }
+
+// AcceptedMediaType contains information about a valid media type that the
+// server can serialize.
+type AcceptedMediaType struct {
+	// Type is the first part of the media type ("application")
+	Type string
+	// SubType is the second part of the media type ("json")
+	SubType string
+	// Serializer is the serialization info this object accepts
+	Serializer runtime.SerializerInfo
+}
+
+// MediaTypeOptions describes information for a given media type that may alter
+// the server response
+type MediaTypeOptions struct {
+	// pretty is true if the requested representation should be formatted for human
+	// viewing
+	Pretty bool
+
+	// stream, if set, indicates that a streaming protocol variant of this encoding
+	// is desired. The only currently supported value is watch which returns versioned
+	// events. In the future, this may refer to other stream protocols.
+	Stream string
+
+	// convert is a request to alter the type of object returned by the server from the
+	// normal response
+	Convert *schema.GroupVersionKind
+	// useServerVersion is an optional version for the server group
+	UseServerVersion string
+
+	// export is true if the representation requested should exclude fields the server
+	// has set
+	Export bool
+
+	// unrecognized is a list of all unrecognized keys
+	Unrecognized []string
+
+	// the accepted media type from the client
+	Accepted *AcceptedMediaType
+}
+
+// acceptMediaTypeOptions returns an options object that matches the provided media type params. If
+// it returns false, the provided options are not allowed and the media type must be skipped.  These
+// parameters are unversioned and may not be changed.
+func acceptMediaTypeOptions(params map[string]string, accepts *AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
+	var options MediaTypeOptions
+
+	// extract all known parameters
+	for k, v := range params {
+		switch k {
+
+		// controls transformation of the object when returned
+		case "as":
+			if options.Convert == nil {
+				options.Convert = &schema.GroupVersionKind{}
+			}
+			options.Convert.Kind = v
+		case "g":
+			if options.Convert == nil {
+				options.Convert = &schema.GroupVersionKind{}
+			}
+			options.Convert.Group = v
+		case "v":
+			if options.Convert == nil {
+				options.Convert = &schema.GroupVersionKind{}
+			}
+			options.Convert.Version = v
+
+		// controls the streaming schema
+		case "stream":
+			if len(v) > 0 && (accepts.Serializer.StreamSerializer == nil || !endpoint.AllowsStreamSchema(v)) {
+				return MediaTypeOptions{}, false
+			}
+			options.Stream = v
+
+		// controls the version of the server API group used
+		// for generic output
+		case "sv":
+			if len(v) > 0 && !endpoint.AllowsServerVersion(v) {
+				return MediaTypeOptions{}, false
+			}
+			options.UseServerVersion = v
+
+		// if specified, the server should transform the returned
+		// output and remove fields that are always server specified,
+		// or which fit the default behavior.
+		case "export":
+			options.Export = v == "1"
+
+		// if specified, the pretty serializer will be used
+		case "pretty":
+			options.Pretty = v == "1"
+
+		default:
+			options.Unrecognized = append(options.Unrecognized, k)
+		}
+	}
+
+	if options.Convert != nil && !endpoint.AllowsConversion(*options.Convert) {
+		return MediaTypeOptions{}, false
+	}
+
+	options.Accepted = accepts
+	return options, true
+}
+
+type candidateMediaType struct {
+	accepted *AcceptedMediaType
+	clauses  goautoneg.Accept
+}
+
+type candidateMediaTypeSlice []candidateMediaType
+
+// NegotiateMediaTypeOptions returns the most appropriate content type given the accept header and
+// a list of alternatives along with the accepted media type parameters.
+func NegotiateMediaTypeOptions(header string, accepted []AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
+	if len(header) == 0 && len(accepted) > 0 {
+		return MediaTypeOptions{
+			Accepted: &accepted[0],
+		}, true
+	}
+
+	var candidates candidateMediaTypeSlice
+	clauses := goautoneg.ParseAccept(header)
+	for _, clause := range clauses {
+		for i := range accepted {
+			accepts := &accepted[i]
+			switch {
+			case clause.Type == accepts.Type && clause.SubType == accepts.SubType,
+				clause.Type == accepts.Type && clause.SubType == "*",
+				clause.Type == "*" && clause.SubType == "*":
+				candidates = append(candidates, candidateMediaType{accepted: accepts, clauses: clause})
+			}
+		}
+	}
+
+	for _, v := range candidates {
+		if retVal, ret := acceptMediaTypeOptions(v.clauses.Params, v.accepted, endpoint); ret {
+			return retVal, true
+		}
+	}
+
+	return MediaTypeOptions{}, false
+}
+
+// AcceptedMediaTypesForEndpoint returns an array of structs that are used to efficiently check which
+// allowed media types the server exposes.
+func AcceptedMediaTypesForEndpoint(ns runtime.NegotiatedSerializer) []AcceptedMediaType {
+	var acceptedMediaTypes []AcceptedMediaType
+	for _, info := range ns.SupportedMediaTypes() {
+		segments := strings.SplitN(info.MediaType, "/", 2)
+		if len(segments) == 1 {
+			segments = append(segments, "*")
+		}
+		t := AcceptedMediaType{
+			Type:       segments[0],
+			SubType:    segments[1],
+			Serializer: info,
+		}
+		acceptedMediaTypes = append(acceptedMediaTypes, t)
+	}
+	return acceptedMediaTypes
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
new file mode 100644
index 0000000..0801dce
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
@@ -0,0 +1,400 @@
+/*
+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 handlers
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/evanphx/json-patch"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/json"
+	"k8s.io/apimachinery/pkg/util/mergepatch"
+	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/apimachinery/pkg/util/strategicpatch"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/audit"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	utiltrace "k8s.io/apiserver/pkg/util/trace"
+)
+
+// PatchResource returns a function that will handle a resource patch.
+func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		// For performance tracking purposes.
+		trace := utiltrace.New("Patch " + req.URL.Path)
+		defer trace.LogIfLong(500 * time.Millisecond)
+
+		if isDryRun(req.URL) {
+			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
+			return
+		}
+
+		// Do this first, otherwise name extraction can fail for unrecognized content types
+		// TODO: handle this in negotiation
+		contentType := req.Header.Get("Content-Type")
+		// Remove "; charset=" if included in header.
+		if idx := strings.Index(contentType, ";"); idx > 0 {
+			contentType = contentType[:idx]
+		}
+		patchType := types.PatchType(contentType)
+
+		// Ensure the patchType is one we support
+		if !sets.NewString(patchTypes...).Has(contentType) {
+			scope.err(negotiation.NewUnsupportedMediaTypeError(patchTypes), w, req)
+			return
+		}
+
+		// TODO: we either want to remove timeout or document it (if we
+		// document, move timeout out of this function and declare it in
+		// api_installer)
+		timeout := parseTimeout(req.URL.Query().Get("timeout"))
+
+		namespace, name, err := scope.Namer.Name(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+
+		patchJS, err := readBody(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		ae := request.AuditEventFrom(ctx)
+		admit = admission.WithAudit(admit, ae)
+
+		audit.LogRequestPatch(ae, patchJS)
+		trace.Step("Recorded the audit event")
+
+		s, ok := runtime.SerializerInfoForMediaType(scope.Serializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
+		if !ok {
+			scope.err(fmt.Errorf("no serializer defined for JSON"), w, req)
+			return
+		}
+		gv := scope.Kind.GroupVersion()
+		codec := runtime.NewCodec(
+			scope.Serializer.EncoderForVersion(s.Serializer, gv),
+			scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}),
+		)
+
+		userInfo, _ := request.UserFrom(ctx)
+		staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)
+		admissionCheck := func(updatedObject runtime.Object, currentObject runtime.Object) error {
+			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && admit.Handles(admission.Update) {
+				return mutatingAdmission.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo))
+			}
+			return nil
+		}
+
+		p := patcher{
+			namer:           scope.Namer,
+			creater:         scope.Creater,
+			defaulter:       scope.Defaulter,
+			unsafeConvertor: scope.UnsafeConvertor,
+			kind:            scope.Kind,
+			resource:        scope.Resource,
+
+			createValidation: rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes),
+			updateValidation: rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes),
+			admissionCheck:   admissionCheck,
+
+			codec: codec,
+
+			timeout: timeout,
+
+			restPatcher: r,
+			name:        name,
+			patchType:   patchType,
+			patchJS:     patchJS,
+
+			trace: trace,
+		}
+
+		result, err := p.patchResource(ctx)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Object stored in database")
+
+		requestInfo, ok := request.RequestInfoFrom(ctx)
+		if !ok {
+			scope.err(fmt.Errorf("missing requestInfo"), w, req)
+			return
+		}
+		if err := setSelfLink(result, requestInfo, scope.Namer); err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Self-link added")
+
+		transformResponseObject(ctx, scope, req, w, http.StatusOK, result)
+	}
+}
+
+type mutateObjectUpdateFunc func(obj, old runtime.Object) error
+
+// patcher breaks the process of patch application and retries into smaller
+// pieces of functionality.
+// TODO: Use builder pattern to construct this object?
+// TODO: As part of that effort, some aspects of PatchResource above could be
+// moved into this type.
+type patcher struct {
+	// Pieces of RequestScope
+	namer           ScopeNamer
+	creater         runtime.ObjectCreater
+	defaulter       runtime.ObjectDefaulter
+	unsafeConvertor runtime.ObjectConvertor
+	resource        schema.GroupVersionResource
+	kind            schema.GroupVersionKind
+
+	// Validation functions
+	createValidation rest.ValidateObjectFunc
+	updateValidation rest.ValidateObjectUpdateFunc
+	admissionCheck   mutateObjectUpdateFunc
+
+	codec runtime.Codec
+
+	timeout time.Duration
+
+	// Operation information
+	restPatcher rest.Patcher
+	name        string
+	patchType   types.PatchType
+	patchJS     []byte
+
+	trace *utiltrace.Trace
+
+	// Set at invocation-time (by applyPatch) and immutable thereafter
+	namespace         string
+	updatedObjectInfo rest.UpdatedObjectInfo
+	mechanism         patchMechanism
+}
+
+func (p *patcher) toUnversioned(versionedObj runtime.Object) (runtime.Object, error) {
+	gvk := p.kind.GroupKind().WithVersion(runtime.APIVersionInternal)
+	return p.unsafeConvertor.ConvertToVersion(versionedObj, gvk.GroupVersion())
+}
+
+type patchMechanism interface {
+	applyPatchToCurrentObject(currentObject runtime.Object) (runtime.Object, error)
+}
+
+type jsonPatcher struct {
+	*patcher
+}
+
+func (p *jsonPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (runtime.Object, error) {
+	// Encode will convert & return a versioned object in JSON.
+	currentObjJS, err := runtime.Encode(p.codec, currentObject)
+	if err != nil {
+		return nil, err
+	}
+
+	// Apply the patch.
+	patchedObjJS, err := p.applyJSPatch(currentObjJS)
+	if err != nil {
+		return nil, interpretPatchError(err)
+	}
+
+	// Construct the resulting typed, unversioned object.
+	objToUpdate := p.restPatcher.New()
+	if err := runtime.DecodeInto(p.codec, patchedObjJS, objToUpdate); err != nil {
+		return nil, err
+	}
+
+	return objToUpdate, nil
+}
+
+// patchJS applies the patch. Input and output objects must both have
+// the external version, since that is what the patch must have been constructed against.
+func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr error) {
+	switch p.patchType {
+	case types.JSONPatchType:
+		patchObj, err := jsonpatch.DecodePatch(p.patchJS)
+		if err != nil {
+			return nil, err
+		}
+		return patchObj.Apply(versionedJS)
+	case types.MergePatchType:
+		return jsonpatch.MergePatch(versionedJS, p.patchJS)
+	default:
+		// only here as a safety net - go-restful filters content-type
+		return nil, fmt.Errorf("unknown Content-Type header for patch: %v", p.patchType)
+	}
+}
+
+type smpPatcher struct {
+	*patcher
+
+	// Schema
+	schemaReferenceObj runtime.Object
+}
+
+func (p *smpPatcher) applyPatchToCurrentObject(currentObject runtime.Object) (runtime.Object, error) {
+	// Since the patch is applied on versioned objects, we need to convert the
+	// current object to versioned representation first.
+	currentVersionedObject, err := p.unsafeConvertor.ConvertToVersion(currentObject, p.kind.GroupVersion())
+	if err != nil {
+		return nil, err
+	}
+	versionedObjToUpdate, err := p.creater.New(p.kind)
+	if err != nil {
+		return nil, err
+	}
+	if err := strategicPatchObject(p.codec, p.defaulter, currentVersionedObject, p.patchJS, versionedObjToUpdate, p.schemaReferenceObj); err != nil {
+		return nil, err
+	}
+	// Convert the object back to unversioned (aka internal version).
+	unversionedObjToUpdate, err := p.toUnversioned(versionedObjToUpdate)
+	if err != nil {
+		return nil, err
+	}
+
+	return unversionedObjToUpdate, nil
+}
+
+// strategicPatchObject applies a strategic merge patch of <patchJS> to
+// <originalObject> and stores the result in <objToUpdate>.
+// It additionally returns the map[string]interface{} representation of the
+// <originalObject> and <patchJS>.
+// NOTE: Both <originalObject> and <objToUpdate> are supposed to be versioned.
+func strategicPatchObject(
+	codec runtime.Codec,
+	defaulter runtime.ObjectDefaulter,
+	originalObject runtime.Object,
+	patchJS []byte,
+	objToUpdate runtime.Object,
+	schemaReferenceObj runtime.Object,
+) error {
+	originalObjMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(originalObject)
+	if err != nil {
+		return err
+	}
+
+	patchMap := make(map[string]interface{})
+	if err := json.Unmarshal(patchJS, &patchMap); err != nil {
+		return errors.NewBadRequest(err.Error())
+	}
+
+	if err := applyPatchToObject(codec, defaulter, originalObjMap, patchMap, objToUpdate, schemaReferenceObj); err != nil {
+		return err
+	}
+	return nil
+}
+
+// applyPatch is called every time GuaranteedUpdate asks for the updated object,
+// and is given the currently persisted object as input.
+func (p *patcher) applyPatch(_ context.Context, _, currentObject runtime.Object) (runtime.Object, error) {
+	// Make sure we actually have a persisted currentObject
+	p.trace.Step("About to apply patch")
+	if hasUID, err := hasUID(currentObject); err != nil {
+		return nil, err
+	} else if !hasUID {
+		return nil, errors.NewNotFound(p.resource.GroupResource(), p.name)
+	}
+
+	objToUpdate, err := p.mechanism.applyPatchToCurrentObject(currentObject)
+	if err != nil {
+		return nil, err
+	}
+	if err := checkName(objToUpdate, p.name, p.namespace, p.namer); err != nil {
+		return nil, err
+	}
+	return objToUpdate, nil
+}
+
+// applyAdmission is called every time GuaranteedUpdate asks for the updated object,
+// and is given the currently persisted object and the patched object as input.
+func (p *patcher) applyAdmission(ctx context.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) {
+	p.trace.Step("About to check admission control")
+	return patchedObject, p.admissionCheck(patchedObject, currentObject)
+}
+
+// patchResource divides PatchResource for easier unit testing
+func (p *patcher) patchResource(ctx context.Context) (runtime.Object, error) {
+	p.namespace = request.NamespaceValue(ctx)
+	switch p.patchType {
+	case types.JSONPatchType, types.MergePatchType:
+		p.mechanism = &jsonPatcher{patcher: p}
+	case types.StrategicMergePatchType:
+		schemaReferenceObj, err := p.unsafeConvertor.ConvertToVersion(p.restPatcher.New(), p.kind.GroupVersion())
+		if err != nil {
+			return nil, err
+		}
+		p.mechanism = &smpPatcher{patcher: p, schemaReferenceObj: schemaReferenceObj}
+	default:
+		return nil, fmt.Errorf("%v: unimplemented patch type", p.patchType)
+	}
+	p.updatedObjectInfo = rest.DefaultUpdatedObjectInfo(nil, p.applyPatch, p.applyAdmission)
+	return finishRequest(p.timeout, func() (runtime.Object, error) {
+		updateObject, _, updateErr := p.restPatcher.Update(ctx, p.name, p.updatedObjectInfo, p.createValidation, p.updateValidation)
+		return updateObject, updateErr
+	})
+}
+
+// applyPatchToObject applies a strategic merge patch of <patchMap> to
+// <originalMap> and stores the result in <objToUpdate>.
+// NOTE: <objToUpdate> must be a versioned object.
+func applyPatchToObject(
+	codec runtime.Codec,
+	defaulter runtime.ObjectDefaulter,
+	originalMap map[string]interface{},
+	patchMap map[string]interface{},
+	objToUpdate runtime.Object,
+	schemaReferenceObj runtime.Object,
+) error {
+	patchedObjMap, err := strategicpatch.StrategicMergeMapPatch(originalMap, patchMap, schemaReferenceObj)
+	if err != nil {
+		return interpretPatchError(err)
+	}
+
+	// Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object
+	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(patchedObjMap, objToUpdate); err != nil {
+		return err
+	}
+	// Decoding from JSON to a versioned object would apply defaults, so we do the same here
+	defaulter.Default(objToUpdate)
+
+	return nil
+}
+
+// interpretPatchError interprets the error type and returns an error with appropriate HTTP code.
+func interpretPatchError(err error) error {
+	switch err {
+	case mergepatch.ErrBadJSONDoc, mergepatch.ErrBadPatchFormatForPrimitiveList, mergepatch.ErrBadPatchFormatForRetainKeys, mergepatch.ErrBadPatchFormatForSetElementOrderList, mergepatch.ErrUnsupportedStrategicMergePatchFormat:
+		return errors.NewBadRequest(err.Error())
+	case mergepatch.ErrNoListOfLists, mergepatch.ErrPatchContentNotMatchRetainKeys:
+		return errors.NewGenericServerResponse(http.StatusUnprocessableEntity, "", schema.GroupResource{}, "", err.Error(), 0, false)
+	default:
+		return err
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
new file mode 100644
index 0000000..8cee470
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/response.go
@@ -0,0 +1,195 @@
+/*
+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 handlers
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
+)
+
+// transformResponseObject takes an object loaded from storage and performs any necessary transformations.
+// Will write the complete response object.
+func transformResponseObject(ctx context.Context, scope RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, result runtime.Object) {
+	// TODO: fetch the media type much earlier in request processing and pass it into this method.
+	mediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope)
+	if err != nil {
+		status := responsewriters.ErrorToAPIStatus(err)
+		responsewriters.WriteRawJSON(int(status.Code), status, w)
+		return
+	}
+
+	// If conversion was allowed by the scope, perform it before writing the response
+	if target := mediaType.Convert; target != nil {
+		switch {
+
+		case target.Kind == "PartialObjectMetadata" && target.GroupVersion() == metav1beta1.SchemeGroupVersion:
+			if meta.IsListType(result) {
+				// TODO: this should be calculated earlier
+				err = newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadata, but the requested object is a list (%T)", result))
+				scope.err(err, w, req)
+				return
+			}
+			m, err := meta.Accessor(result)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			partial := meta.AsPartialObjectMetadata(m)
+			partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
+
+			// renegotiate under the internal version
+			_, info, err := negotiation.NegotiateOutputMediaType(req, metainternalversion.Codecs, &scope)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			encoder := metainternalversion.Codecs.EncoderForVersion(info.Serializer, metav1beta1.SchemeGroupVersion)
+			responsewriters.SerializeObject(info.MediaType, encoder, w, req, statusCode, partial)
+			return
+
+		case target.Kind == "PartialObjectMetadataList" && target.GroupVersion() == metav1beta1.SchemeGroupVersion:
+			if !meta.IsListType(result) {
+				// TODO: this should be calculated earlier
+				err = newNotAcceptableError(fmt.Sprintf("you requested PartialObjectMetadataList, but the requested object is not a list (%T)", result))
+				scope.err(err, w, req)
+				return
+			}
+			list := &metav1beta1.PartialObjectMetadataList{}
+			err := meta.EachListItem(result, func(obj runtime.Object) error {
+				m, err := meta.Accessor(obj)
+				if err != nil {
+					return err
+				}
+				partial := meta.AsPartialObjectMetadata(m)
+				partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
+				list.Items = append(list.Items, partial)
+				return nil
+			})
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+
+			// renegotiate under the internal version
+			_, info, err := negotiation.NegotiateOutputMediaType(req, metainternalversion.Codecs, &scope)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			encoder := metainternalversion.Codecs.EncoderForVersion(info.Serializer, metav1beta1.SchemeGroupVersion)
+			responsewriters.SerializeObject(info.MediaType, encoder, w, req, statusCode, list)
+			return
+
+		case target.Kind == "Table" && target.GroupVersion() == metav1beta1.SchemeGroupVersion:
+			// TODO: relax the version abstraction
+			// TODO: skip if this is a status response (delete without body)?
+
+			opts := &metav1beta1.TableOptions{}
+			if err := metav1beta1.ParameterCodec.DecodeParameters(req.URL.Query(), metav1beta1.SchemeGroupVersion, opts); err != nil {
+				scope.err(err, w, req)
+				return
+			}
+
+			table, err := scope.TableConvertor.ConvertToTable(ctx, result, opts)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+
+			for i := range table.Rows {
+				item := &table.Rows[i]
+				switch opts.IncludeObject {
+				case metav1beta1.IncludeObject:
+					item.Object.Object, err = scope.Convertor.ConvertToVersion(item.Object.Object, scope.Kind.GroupVersion())
+					if err != nil {
+						scope.err(err, w, req)
+						return
+					}
+				// TODO: rely on defaulting for the value here?
+				case metav1beta1.IncludeMetadata, "":
+					m, err := meta.Accessor(item.Object.Object)
+					if err != nil {
+						scope.err(err, w, req)
+						return
+					}
+					// TODO: turn this into an internal type and do conversion in order to get object kind automatically set?
+					partial := meta.AsPartialObjectMetadata(m)
+					partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
+					item.Object.Object = partial
+				case metav1beta1.IncludeNone:
+					item.Object.Object = nil
+				default:
+					// TODO: move this to validation on the table options?
+					err = errors.NewBadRequest(fmt.Sprintf("unrecognized includeObject value: %q", opts.IncludeObject))
+					scope.err(err, w, req)
+				}
+			}
+
+			// renegotiate under the internal version
+			_, info, err := negotiation.NegotiateOutputMediaType(req, metainternalversion.Codecs, &scope)
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			encoder := metainternalversion.Codecs.EncoderForVersion(info.Serializer, metav1beta1.SchemeGroupVersion)
+			responsewriters.SerializeObject(info.MediaType, encoder, w, req, statusCode, table)
+			return
+
+		default:
+			// this block should only be hit if scope AllowsConversion is incorrect
+			accepted, _ := negotiation.MediaTypesForSerializer(metainternalversion.Codecs)
+			err := negotiation.NewNotAcceptableError(accepted)
+			status := responsewriters.ErrorToAPIStatus(err)
+			responsewriters.WriteRawJSON(int(status.Code), status, w)
+			return
+		}
+	}
+
+	responsewriters.WriteObject(statusCode, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
+}
+
+// errNotAcceptable indicates Accept negotiation has failed
+type errNotAcceptable struct {
+	message string
+}
+
+func newNotAcceptableError(message string) error {
+	return errNotAcceptable{message}
+}
+
+func (e errNotAcceptable) Error() string {
+	return e.message
+}
+
+func (e errNotAcceptable) Status() metav1.Status {
+	return metav1.Status{
+		Status:  metav1.StatusFailure,
+		Code:    http.StatusNotAcceptable,
+		Reason:  metav1.StatusReason("NotAcceptable"),
+		Message: e.Error(),
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/doc.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/doc.go
new file mode 100644
index 0000000..b76758b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2016 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 responsewriters containers helpers to write responses in HTTP handlers.
+package responsewriters // import "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/errors.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/errors.go
new file mode 100644
index 0000000..007efe9
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/errors.go
@@ -0,0 +1,97 @@
+/*
+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 responsewriters
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strings"
+
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apiserver/pkg/authorization/authorizer"
+)
+
+// Avoid emitting errors that look like valid HTML. Quotes are okay.
+var sanitizer = strings.NewReplacer(`&`, "&amp;", `<`, "&lt;", `>`, "&gt;")
+
+// BadGatewayError renders a simple bad gateway error.
+func BadGatewayError(w http.ResponseWriter, req *http.Request) {
+	w.Header().Set("Content-Type", "text/plain")
+	w.Header().Set("X-Content-Type-Options", "nosniff")
+	w.WriteHeader(http.StatusBadGateway)
+	fmt.Fprintf(w, "Bad Gateway: %q", sanitizer.Replace(req.RequestURI))
+}
+
+// Forbidden renders a simple forbidden error
+func Forbidden(ctx context.Context, attributes authorizer.Attributes, w http.ResponseWriter, req *http.Request, reason string, s runtime.NegotiatedSerializer) {
+	msg := sanitizer.Replace(forbiddenMessage(attributes))
+	w.Header().Set("X-Content-Type-Options", "nosniff")
+
+	var errMsg string
+	if len(reason) == 0 {
+		errMsg = fmt.Sprintf("%s", msg)
+	} else {
+		errMsg = fmt.Sprintf("%s: %s", msg, reason)
+	}
+	gv := schema.GroupVersion{Group: attributes.GetAPIGroup(), Version: attributes.GetAPIVersion()}
+	gr := schema.GroupResource{Group: attributes.GetAPIGroup(), Resource: attributes.GetResource()}
+	ErrorNegotiated(apierrors.NewForbidden(gr, attributes.GetName(), fmt.Errorf(errMsg)), s, gv, w, req)
+}
+
+func forbiddenMessage(attributes authorizer.Attributes) string {
+	username := ""
+	if user := attributes.GetUser(); user != nil {
+		username = user.GetName()
+	}
+
+	if !attributes.IsResourceRequest() {
+		return fmt.Sprintf("User %q cannot %s path %q", username, attributes.GetVerb(), attributes.GetPath())
+	}
+
+	resource := attributes.GetResource()
+	if group := attributes.GetAPIGroup(); len(group) > 0 {
+		resource = resource + "." + group
+	}
+	if subresource := attributes.GetSubresource(); len(subresource) > 0 {
+		resource = resource + "/" + subresource
+	}
+
+	if ns := attributes.GetNamespace(); len(ns) > 0 {
+		return fmt.Sprintf("User %q cannot %s %s in the namespace %q", username, attributes.GetVerb(), resource, ns)
+	}
+
+	return fmt.Sprintf("User %q cannot %s %s at the cluster scope", username, attributes.GetVerb(), resource)
+}
+
+// InternalError renders a simple internal error
+func InternalError(w http.ResponseWriter, req *http.Request, err error) {
+	w.Header().Set("Content-Type", "text/plain")
+	w.Header().Set("X-Content-Type-Options", "nosniff")
+	w.WriteHeader(http.StatusInternalServerError)
+	fmt.Fprintf(w, "Internal Server Error: %q: %v", sanitizer.Replace(req.RequestURI), err)
+	utilruntime.HandleError(err)
+}
+
+// NotFound renders a simple not found error.
+func NotFound(w http.ResponseWriter, req *http.Request) {
+	w.WriteHeader(http.StatusNotFound)
+	fmt.Fprintf(w, "Not Found: %q", sanitizer.Replace(req.RequestURI))
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go
new file mode 100755
index 0000000..9967307
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go
@@ -0,0 +1,76 @@
+/*
+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 responsewriters
+
+import (
+	"fmt"
+	"net/http"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apiserver/pkg/storage"
+)
+
+// statusError is an object that can be converted into an metav1.Status
+type statusError interface {
+	Status() metav1.Status
+}
+
+// ErrorToAPIStatus converts an error to an metav1.Status object.
+func ErrorToAPIStatus(err error) *metav1.Status {
+	switch t := err.(type) {
+	case statusError:
+		status := t.Status()
+		if len(status.Status) == 0 {
+			status.Status = metav1.StatusFailure
+		}
+		if status.Code == 0 {
+			switch status.Status {
+			case metav1.StatusSuccess:
+				status.Code = http.StatusOK
+			case metav1.StatusFailure:
+				status.Code = http.StatusInternalServerError
+			}
+		}
+		status.Kind = "Status"
+		status.APIVersion = "v1"
+		//TODO: check for invalid responses
+		return &status
+	default:
+		status := http.StatusInternalServerError
+		switch {
+		//TODO: replace me with NewConflictErr
+		case storage.IsConflict(err):
+			status = http.StatusConflict
+		}
+		// Log errors that were not converted to an error status
+		// by REST storage - these typically indicate programmer
+		// error by not using pkg/api/errors, or unexpected failure
+		// cases.
+		runtime.HandleError(fmt.Errorf("apiserver received an error that is not an metav1.Status: %#+v", err))
+		return &metav1.Status{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "Status",
+				APIVersion: "v1",
+			},
+			Status:  metav1.StatusFailure,
+			Code:    int32(status),
+			Reason:  metav1.StatusReasonUnknown,
+			Message: err.Error(),
+		}
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go
new file mode 100644
index 0000000..0add873
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go
@@ -0,0 +1,174 @@
+/*
+Copyright 2016 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 responsewriters
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"strconv"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apiserver/pkg/audit"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/metrics"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	"k8s.io/apiserver/pkg/util/flushwriter"
+	"k8s.io/apiserver/pkg/util/wsstream"
+)
+
+// WriteObject renders a returned runtime.Object to the response as a stream or an encoded object. If the object
+// returned by the response implements rest.ResourceStreamer that interface will be used to render the
+// response. The Accept header and current API version will be passed in, and the output will be copied
+// directly to the response body. If content type is returned it is used, otherwise the content type will
+// be "application/octet-stream". All other objects are sent to standard JSON serialization.
+func WriteObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSerializer, object runtime.Object, w http.ResponseWriter, req *http.Request) {
+	stream, ok := object.(rest.ResourceStreamer)
+	if ok {
+		requestInfo, _ := request.RequestInfoFrom(req.Context())
+		metrics.RecordLongRunning(req, requestInfo, func() {
+			StreamObject(statusCode, gv, s, stream, w, req)
+		})
+		return
+	}
+	WriteObjectNegotiated(s, gv, w, req, statusCode, object)
+}
+
+// StreamObject performs input stream negotiation from a ResourceStreamer and writes that to the response.
+// If the client requests a websocket upgrade, negotiate for a websocket reader protocol (because many
+// browser clients cannot easily handle binary streaming protocols).
+func StreamObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSerializer, stream rest.ResourceStreamer, w http.ResponseWriter, req *http.Request) {
+	out, flush, contentType, err := stream.InputStream(gv.String(), req.Header.Get("Accept"))
+	if err != nil {
+		ErrorNegotiated(err, s, gv, w, req)
+		return
+	}
+	if out == nil {
+		// No output provided - return StatusNoContent
+		w.WriteHeader(http.StatusNoContent)
+		return
+	}
+	defer out.Close()
+
+	if wsstream.IsWebSocketRequest(req) {
+		r := wsstream.NewReader(out, true, wsstream.NewDefaultReaderProtocols())
+		if err := r.Copy(w, req); err != nil {
+			utilruntime.HandleError(fmt.Errorf("error encountered while streaming results via websocket: %v", err))
+		}
+		return
+	}
+
+	if len(contentType) == 0 {
+		contentType = "application/octet-stream"
+	}
+	w.Header().Set("Content-Type", contentType)
+	w.WriteHeader(statusCode)
+	writer := w.(io.Writer)
+	if flush {
+		writer = flushwriter.Wrap(w)
+	}
+	io.Copy(writer, out)
+}
+
+// SerializeObject renders an object in the content type negotiated by the client using the provided encoder.
+// The context is optional and can be nil.
+func SerializeObject(mediaType string, encoder runtime.Encoder, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
+	w.Header().Set("Content-Type", mediaType)
+	w.WriteHeader(statusCode)
+
+	if err := encoder.Encode(object, w); err != nil {
+		errorJSONFatal(err, encoder, w)
+	}
+}
+
+// WriteObjectNegotiated renders an object in the content type negotiated by the client.
+// The context is optional and can be nil.
+func WriteObjectNegotiated(s runtime.NegotiatedSerializer, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) {
+	serializer, err := negotiation.NegotiateOutputSerializer(req, s)
+	if err != nil {
+		// if original statusCode was not successful we need to return the original error
+		// we cannot hide it behind negotiation problems
+		if statusCode < http.StatusOK || statusCode >= http.StatusBadRequest {
+			WriteRawJSON(int(statusCode), object, w)
+			return
+		}
+		status := ErrorToAPIStatus(err)
+		WriteRawJSON(int(status.Code), status, w)
+		return
+	}
+
+	if ae := request.AuditEventFrom(req.Context()); ae != nil {
+		audit.LogResponseObject(ae, object, gv, s)
+	}
+
+	encoder := s.EncoderForVersion(serializer.Serializer, gv)
+	SerializeObject(serializer.MediaType, encoder, w, req, statusCode, object)
+}
+
+// ErrorNegotiated renders an error to the response. Returns the HTTP status code of the error.
+// The context is optional and may be nil.
+func ErrorNegotiated(err error, s runtime.NegotiatedSerializer, gv schema.GroupVersion, w http.ResponseWriter, req *http.Request) int {
+	status := ErrorToAPIStatus(err)
+	code := int(status.Code)
+	// when writing an error, check to see if the status indicates a retry after period
+	if status.Details != nil && status.Details.RetryAfterSeconds > 0 {
+		delay := strconv.Itoa(int(status.Details.RetryAfterSeconds))
+		w.Header().Set("Retry-After", delay)
+	}
+
+	if code == http.StatusNoContent {
+		w.WriteHeader(code)
+		return code
+	}
+
+	WriteObjectNegotiated(s, gv, w, req, code, status)
+	return code
+}
+
+// errorJSONFatal renders an error to the response, and if codec fails will render plaintext.
+// Returns the HTTP status code of the error.
+func errorJSONFatal(err error, codec runtime.Encoder, w http.ResponseWriter) int {
+	utilruntime.HandleError(fmt.Errorf("apiserver was unable to write a JSON response: %v", err))
+	status := ErrorToAPIStatus(err)
+	code := int(status.Code)
+	output, err := runtime.Encode(codec, status)
+	if err != nil {
+		w.WriteHeader(code)
+		fmt.Fprintf(w, "%s: %s", status.Reason, status.Message)
+		return code
+	}
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(code)
+	w.Write(output)
+	return code
+}
+
+// WriteRawJSON writes a non-API object in JSON.
+func WriteRawJSON(statusCode int, object interface{}, w http.ResponseWriter) {
+	output, err := json.MarshalIndent(object, "", "  ")
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(statusCode)
+	w.Write(output)
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
new file mode 100644
index 0000000..d42c019
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
@@ -0,0 +1,330 @@
+/*
+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 handlers
+
+import (
+	"context"
+	"encoding/hex"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/golang/glog"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
+	"k8s.io/apiserver/pkg/endpoints/metrics"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	openapiproto "k8s.io/kube-openapi/pkg/util/proto"
+)
+
+// RequestScope encapsulates common fields across all RESTful handler methods.
+type RequestScope struct {
+	Namer ScopeNamer
+
+	Serializer runtime.NegotiatedSerializer
+	runtime.ParameterCodec
+
+	Creater         runtime.ObjectCreater
+	Convertor       runtime.ObjectConvertor
+	Defaulter       runtime.ObjectDefaulter
+	Typer           runtime.ObjectTyper
+	UnsafeConvertor runtime.ObjectConvertor
+
+	TableConvertor rest.TableConvertor
+	OpenAPISchema  openapiproto.Schema
+
+	Resource    schema.GroupVersionResource
+	Kind        schema.GroupVersionKind
+	Subresource string
+
+	MetaGroupVersion schema.GroupVersion
+}
+
+func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) {
+	responsewriters.ErrorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), w, req)
+}
+
+func (scope *RequestScope) AllowsConversion(gvk schema.GroupVersionKind) bool {
+	// TODO: this is temporary, replace with an abstraction calculated at endpoint installation time
+	if gvk.GroupVersion() == metav1beta1.SchemeGroupVersion {
+		switch gvk.Kind {
+		case "Table":
+			return scope.TableConvertor != nil
+		case "PartialObjectMetadata", "PartialObjectMetadataList":
+			// TODO: should delineate between lists and non-list endpoints
+			return true
+		default:
+			return false
+		}
+	}
+	return false
+}
+
+func (scope *RequestScope) AllowsServerVersion(version string) bool {
+	return version == scope.MetaGroupVersion.Version
+}
+
+func (scope *RequestScope) AllowsStreamSchema(s string) bool {
+	return s == "watch"
+}
+
+// ConnectResource returns a function that handles a connect request on a rest.Storage object.
+func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		namespace, name, err := scope.Namer.Name(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+		ae := request.AuditEventFrom(ctx)
+		admit = admission.WithAudit(admit, ae)
+
+		opts, subpath, subpathKey := connecter.NewConnectOptions()
+		if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
+			err = errors.NewBadRequest(err.Error())
+			scope.err(err, w, req)
+			return
+		}
+		if admit != nil && admit.Handles(admission.Connect) {
+			connectRequest := &rest.ConnectRequest{
+				Name:         name,
+				Options:      opts,
+				ResourcePath: restPath,
+			}
+			userInfo, _ := request.UserFrom(ctx)
+			// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
+			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
+				err = mutatingAdmission.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo))
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+			if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
+				err = validatingAdmission.Validate(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo))
+				if err != nil {
+					scope.err(err, w, req)
+					return
+				}
+			}
+		}
+		requestInfo, _ := request.RequestInfoFrom(ctx)
+		metrics.RecordLongRunning(req, requestInfo, func() {
+			handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, w: w})
+			if err != nil {
+				scope.err(err, w, req)
+				return
+			}
+			handler.ServeHTTP(w, req)
+		})
+	}
+}
+
+// responder implements rest.Responder for assisting a connector in writing objects or errors.
+type responder struct {
+	scope RequestScope
+	req   *http.Request
+	w     http.ResponseWriter
+}
+
+func (r *responder) Object(statusCode int, obj runtime.Object) {
+	responsewriters.WriteObject(statusCode, r.scope.Kind.GroupVersion(), r.scope.Serializer, obj, r.w, r.req)
+}
+
+func (r *responder) Error(err error) {
+	r.scope.err(err, r.w, r.req)
+}
+
+// resultFunc is a function that returns a rest result and can be run in a goroutine
+type resultFunc func() (runtime.Object, error)
+
+// finishRequest makes a given resultFunc asynchronous and handles errors returned by the response.
+// An api.Status object with status != success is considered an "error", which interrupts the normal response flow.
+func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, err error) {
+	// these channels need to be buffered to prevent the goroutine below from hanging indefinitely
+	// when the select statement reads something other than the one the goroutine sends on.
+	ch := make(chan runtime.Object, 1)
+	errCh := make(chan error, 1)
+	panicCh := make(chan interface{}, 1)
+	go func() {
+		// panics don't cross goroutine boundaries, so we have to handle ourselves
+		defer utilruntime.HandleCrash(func(panicReason interface{}) {
+			// Propagate to parent goroutine
+			panicCh <- panicReason
+		})
+
+		if result, err := fn(); err != nil {
+			errCh <- err
+		} else {
+			ch <- result
+		}
+	}()
+
+	select {
+	case result = <-ch:
+		if status, ok := result.(*metav1.Status); ok {
+			if status.Status != metav1.StatusSuccess {
+				return nil, errors.FromObject(status)
+			}
+		}
+		return result, nil
+	case err = <-errCh:
+		return nil, err
+	case p := <-panicCh:
+		panic(p)
+	case <-time.After(timeout):
+		return nil, errors.NewTimeoutError("request did not complete within allowed duration", 0)
+	}
+}
+
+// transformDecodeError adds additional information when a decode fails.
+func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *schema.GroupVersionKind, body []byte) error {
+	objGVKs, _, err := typer.ObjectKinds(into)
+	if err != nil {
+		return err
+	}
+	objGVK := objGVKs[0]
+	if gvk != nil && len(gvk.Kind) > 0 {
+		return errors.NewBadRequest(fmt.Sprintf("%s in version %q cannot be handled as a %s: %v", gvk.Kind, gvk.Version, objGVK.Kind, baseErr))
+	}
+	summary := summarizeData(body, 30)
+	return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v (%s)", objGVK.Kind, baseErr, summary))
+}
+
+// setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request
+// plus the path and query generated by the provided linkFunc
+func setSelfLink(obj runtime.Object, requestInfo *request.RequestInfo, namer ScopeNamer) error {
+	// TODO: SelfLink generation should return a full URL?
+	uri, err := namer.GenerateLink(requestInfo, obj)
+	if err != nil {
+		return nil
+	}
+
+	return namer.SetSelfLink(obj, uri)
+}
+
+func hasUID(obj runtime.Object) (bool, error) {
+	if obj == nil {
+		return false, nil
+	}
+	accessor, err := meta.Accessor(obj)
+	if err != nil {
+		return false, errors.NewInternalError(err)
+	}
+	if len(accessor.GetUID()) == 0 {
+		return false, nil
+	}
+	return true, nil
+}
+
+// checkName checks the provided name against the request
+func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) error {
+	objNamespace, objName, err := namer.ObjectName(obj)
+	if err != nil {
+		return errors.NewBadRequest(fmt.Sprintf(
+			"the name of the object (%s based on URL) was undeterminable: %v", name, err))
+	}
+	if objName != name {
+		return errors.NewBadRequest(fmt.Sprintf(
+			"the name of the object (%s) does not match the name on the URL (%s)", objName, name))
+	}
+	if len(namespace) > 0 {
+		if len(objNamespace) > 0 && objNamespace != namespace {
+			return errors.NewBadRequest(fmt.Sprintf(
+				"the namespace of the object (%s) does not match the namespace on the request (%s)", objNamespace, namespace))
+		}
+	}
+
+	return nil
+}
+
+// setListSelfLink sets the self link of a list to the base URL, then sets the self links
+// on all child objects returned. Returns the number of items in the list.
+func setListSelfLink(obj runtime.Object, ctx context.Context, req *http.Request, namer ScopeNamer) (int, error) {
+	if !meta.IsListType(obj) {
+		return 0, nil
+	}
+
+	uri, err := namer.GenerateListLink(req)
+	if err != nil {
+		return 0, err
+	}
+	if err := namer.SetSelfLink(obj, uri); err != nil {
+		glog.V(4).Infof("Unable to set self link on object: %v", err)
+	}
+	requestInfo, ok := request.RequestInfoFrom(ctx)
+	if !ok {
+		return 0, fmt.Errorf("missing requestInfo")
+	}
+
+	count := 0
+	err = meta.EachListItem(obj, func(obj runtime.Object) error {
+		count++
+		return setSelfLink(obj, requestInfo, namer)
+	})
+	return count, err
+}
+
+func summarizeData(data []byte, maxLength int) string {
+	switch {
+	case len(data) == 0:
+		return "<empty>"
+	case data[0] == '{':
+		if len(data) > maxLength {
+			return string(data[:maxLength]) + " ..."
+		}
+		return string(data)
+	default:
+		if len(data) > maxLength {
+			return hex.EncodeToString(data[:maxLength]) + " ..."
+		}
+		return hex.EncodeToString(data)
+	}
+}
+
+func readBody(req *http.Request) ([]byte, error) {
+	defer req.Body.Close()
+	return ioutil.ReadAll(req.Body)
+}
+
+func parseTimeout(str string) time.Duration {
+	if str != "" {
+		timeout, err := time.ParseDuration(str)
+		if err == nil {
+			return timeout
+		}
+		glog.Errorf("Failed to parse %q: %v", str, err)
+	}
+	return 30 * time.Second
+}
+
+func isDryRun(url *url.URL) bool {
+	return len(url.Query()["dryRun"]) != 0
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
new file mode 100644
index 0000000..de24277
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/update.go
@@ -0,0 +1,142 @@
+/*
+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 handlers
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"time"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/audit"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/registry/rest"
+	utiltrace "k8s.io/apiserver/pkg/util/trace"
+)
+
+// UpdateResource returns a function that will handle a resource update
+func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interface) http.HandlerFunc {
+	return func(w http.ResponseWriter, req *http.Request) {
+		// For performance tracking purposes.
+		trace := utiltrace.New("Update " + req.URL.Path)
+		defer trace.LogIfLong(500 * time.Millisecond)
+
+		if isDryRun(req.URL) {
+			scope.err(errors.NewBadRequest("dryRun is not supported yet"), w, req)
+			return
+		}
+
+		// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
+		timeout := parseTimeout(req.URL.Query().Get("timeout"))
+
+		namespace, name, err := scope.Namer.Name(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		ctx := req.Context()
+		ctx = request.WithNamespace(ctx, namespace)
+
+		body, err := readBody(req)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer)
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		defaultGVK := scope.Kind
+		original := r.New()
+		trace.Step("About to convert to expected version")
+		decoder := scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: defaultGVK.Group, Version: runtime.APIVersionInternal})
+		obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
+		if err != nil {
+			err = transformDecodeError(scope.Typer, err, original, gvk, body)
+			scope.err(err, w, req)
+			return
+		}
+		if gvk.GroupVersion() != defaultGVK.GroupVersion() {
+			err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion()))
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Conversion done")
+
+		ae := request.AuditEventFrom(ctx)
+		audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
+		admit = admission.WithAudit(admit, ae)
+
+		if err := checkName(obj, name, namespace, scope.Namer); err != nil {
+			scope.err(err, w, req)
+			return
+		}
+
+		userInfo, _ := request.UserFrom(ctx)
+		staticAdmissionAttributes := admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo)
+		var transformers []rest.TransformFunc
+		if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Update) {
+			transformers = append(transformers, func(ctx context.Context, newObj, oldObj runtime.Object) (runtime.Object, error) {
+				return newObj, mutatingAdmission.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo))
+			})
+		}
+
+		trace.Step("About to store object in database")
+		wasCreated := false
+		result, err := finishRequest(timeout, func() (runtime.Object, error) {
+			obj, created, err := r.Update(
+				ctx,
+				name,
+				rest.DefaultUpdatedObjectInfo(obj, transformers...),
+				rest.AdmissionToValidateObjectFunc(admit, staticAdmissionAttributes),
+				rest.AdmissionToValidateObjectUpdateFunc(admit, staticAdmissionAttributes),
+			)
+			wasCreated = created
+			return obj, err
+		})
+		if err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Object stored in database")
+
+		requestInfo, ok := request.RequestInfoFrom(ctx)
+		if !ok {
+			scope.err(fmt.Errorf("missing requestInfo"), w, req)
+			return
+		}
+		if err := setSelfLink(result, requestInfo, scope.Namer); err != nil {
+			scope.err(err, w, req)
+			return
+		}
+		trace.Step("Self-link added")
+
+		status := http.StatusOK
+		if wasCreated {
+			status = http.StatusCreated
+		}
+
+		transformResponseObject(ctx, scope, req, w, status, result)
+	}
+}
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/watch.go b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/watch.go
new file mode 100755
index 0000000..c1bc984
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/endpoints/handlers/watch.go
@@ -0,0 +1,324 @@
+/*
+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 handlers
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"reflect"
+	"time"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/apimachinery/pkg/watch"
+	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
+	"k8s.io/apiserver/pkg/endpoints/metrics"
+	"k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/server/httplog"
+	"k8s.io/apiserver/pkg/util/wsstream"
+
+	"golang.org/x/net/websocket"
+)
+
+// nothing will ever be sent down this channel
+var neverExitWatch <-chan time.Time = make(chan time.Time)
+
+// timeoutFactory abstracts watch timeout logic for testing
+type TimeoutFactory interface {
+	TimeoutCh() (<-chan time.Time, func() bool)
+}
+
+// realTimeoutFactory implements timeoutFactory
+type realTimeoutFactory struct {
+	timeout time.Duration
+}
+
+// TimeoutCh returns a channel which will receive something when the watch times out,
+// and a cleanup function to call when this happens.
+func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
+	if w.timeout == 0 {
+		return neverExitWatch, func() bool { return false }
+	}
+	t := time.NewTimer(w.timeout)
+	return t.C, t.Stop
+}
+
+// serveWatch handles serving requests to the server
+// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
+func serveWatch(watcher watch.Interface, scope RequestScope, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
+	// negotiate for the stream serializer
+	serializer, err := negotiation.NegotiateOutputStreamSerializer(req, scope.Serializer)
+	if err != nil {
+		scope.err(err, w, req)
+		return
+	}
+	framer := serializer.StreamSerializer.Framer
+	streamSerializer := serializer.StreamSerializer.Serializer
+	embedded := serializer.Serializer
+	if framer == nil {
+		scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), w, req)
+		return
+	}
+	encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
+
+	useTextFraming := serializer.EncodesAsText
+
+	// find the embedded serializer matching the media type
+	embeddedEncoder := scope.Serializer.EncoderForVersion(embedded, scope.Kind.GroupVersion())
+
+	// TODO: next step, get back mediaTypeOptions from negotiate and return the exact value here
+	mediaType := serializer.MediaType
+	if mediaType != runtime.ContentTypeJSON {
+		mediaType += ";stream=watch"
+	}
+
+	ctx := req.Context()
+	requestInfo, ok := request.RequestInfoFrom(ctx)
+	if !ok {
+		scope.err(fmt.Errorf("missing requestInfo"), w, req)
+		return
+	}
+
+	server := &WatchServer{
+		Watching: watcher,
+		Scope:    scope,
+
+		UseTextFraming:  useTextFraming,
+		MediaType:       mediaType,
+		Framer:          framer,
+		Encoder:         encoder,
+		EmbeddedEncoder: embeddedEncoder,
+		Fixup: func(obj runtime.Object) {
+			if err := setSelfLink(obj, requestInfo, scope.Namer); err != nil {
+				utilruntime.HandleError(fmt.Errorf("failed to set link for object %v: %v", reflect.TypeOf(obj), err))
+			}
+		},
+
+		TimeoutFactory: &realTimeoutFactory{timeout},
+	}
+
+	server.ServeHTTP(w, req)
+}
+
+// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
+type WatchServer struct {
+	Watching watch.Interface
+	Scope    RequestScope
+
+	// true if websocket messages should use text framing (as opposed to binary framing)
+	UseTextFraming bool
+	// the media type this watch is being served with
+	MediaType string
+	// used to frame the watch stream
+	Framer runtime.Framer
+	// used to encode the watch stream event itself
+	Encoder runtime.Encoder
+	// used to encode the nested object in the watch stream
+	EmbeddedEncoder runtime.Encoder
+	Fixup           func(runtime.Object)
+
+	TimeoutFactory TimeoutFactory
+}
+
+// ServeHTTP serves a series of encoded events via HTTP with Transfer-Encoding: chunked
+// or over a websocket connection.
+func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	kind := s.Scope.Kind
+	metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Inc()
+	defer metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Dec()
+
+	w = httplog.Unlogged(w)
+
+	if wsstream.IsWebSocketRequest(req) {
+		w.Header().Set("Content-Type", s.MediaType)
+		websocket.Handler(s.HandleWS).ServeHTTP(w, req)
+		return
+	}
+
+	cn, ok := w.(http.CloseNotifier)
+	if !ok {
+		err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
+		utilruntime.HandleError(err)
+		s.Scope.err(errors.NewInternalError(err), w, req)
+		return
+	}
+	flusher, ok := w.(http.Flusher)
+	if !ok {
+		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
+		utilruntime.HandleError(err)
+		s.Scope.err(errors.NewInternalError(err), w, req)
+		return
+	}
+
+	framer := s.Framer.NewFrameWriter(w)
+	if framer == nil {
+		// programmer error
+		err := fmt.Errorf("no stream framing support is available for media type %q", s.MediaType)
+		utilruntime.HandleError(err)
+		s.Scope.err(errors.NewBadRequest(err.Error()), w, req)
+		return
+	}
+	e := streaming.NewEncoder(framer, s.Encoder)
+
+	// ensure the connection times out
+	timeoutCh, cleanup := s.TimeoutFactory.TimeoutCh()
+	defer cleanup()
+	defer s.Watching.Stop()
+
+	// begin the stream
+	w.Header().Set("Content-Type", s.MediaType)
+	w.Header().Set("Transfer-Encoding", "chunked")
+	w.WriteHeader(http.StatusOK)
+	flusher.Flush()
+
+	var unknown runtime.Unknown
+	internalEvent := &metav1.InternalEvent{}
+	buf := &bytes.Buffer{}
+	ch := s.Watching.ResultChan()
+	for {
+		select {
+		case <-cn.CloseNotify():
+			return
+		case <-timeoutCh:
+			return
+		case event, ok := <-ch:
+			if !ok {
+				// End of results.
+				return
+			}
+
+			obj := event.Object
+			s.Fixup(obj)
+			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
+				// unexpected error
+				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
+				return
+			}
+
+			// ContentType is not required here because we are defaulting to the serializer
+			// type
+			unknown.Raw = buf.Bytes()
+			event.Object = &unknown
+
+			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
+			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
+			// and we get the benefit of using conversion functions which already have to stay in sync
+			outEvent := &metav1.WatchEvent{}
+			*internalEvent = metav1.InternalEvent(event)
+			err := metav1.Convert_versioned_InternalEvent_to_versioned_Event(internalEvent, outEvent, nil)
+			if err != nil {
+				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
+				// client disconnect.
+				return
+			}
+			if err := e.Encode(outEvent); err != nil {
+				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v (%#v)", err, e))
+				// client disconnect.
+				return
+			}
+			if len(ch) == 0 {
+				flusher.Flush()
+			}
+
+			buf.Reset()
+		}
+	}
+}
+
+// HandleWS implements a websocket handler.
+func (s *WatchServer) HandleWS(ws *websocket.Conn) {
+	defer ws.Close()
+	done := make(chan struct{})
+
+	go func() {
+		defer utilruntime.HandleCrash()
+		// This blocks until the connection is closed.
+		// Client should not send anything.
+		wsstream.IgnoreReceives(ws, 0)
+		// Once the client closes, we should also close
+		close(done)
+	}()
+
+	var unknown runtime.Unknown
+	internalEvent := &metav1.InternalEvent{}
+	buf := &bytes.Buffer{}
+	streamBuf := &bytes.Buffer{}
+	ch := s.Watching.ResultChan()
+	for {
+		select {
+		case <-done:
+			s.Watching.Stop()
+			return
+		case event, ok := <-ch:
+			if !ok {
+				// End of results.
+				return
+			}
+			obj := event.Object
+			s.Fixup(obj)
+			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
+				// unexpected error
+				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
+				return
+			}
+
+			// ContentType is not required here because we are defaulting to the serializer
+			// type
+			unknown.Raw = buf.Bytes()
+			event.Object = &unknown
+
+			// the internal event will be versioned by the encoder
+			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
+			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
+			// and we get the benefit of using conversion functions which already have to stay in sync
+			outEvent := &metav1.WatchEvent{}
+			*internalEvent = metav1.InternalEvent(event)
+			err := metav1.Convert_versioned_InternalEvent_to_versioned_Event(internalEvent, outEvent, nil)
+			if err != nil {
+				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
+				// client disconnect.
+				s.Watching.Stop()
+				return
+			}
+			if err := s.Encoder.Encode(outEvent, streamBuf); err != nil {
+				// encoding error
+				utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err))
+				s.Watching.Stop()
+				return
+			}
+			if s.UseTextFraming {
+				if err := websocket.Message.Send(ws, streamBuf.String()); err != nil {
+					// Client disconnect.
+					s.Watching.Stop()
+					return
+				}
+			} else {
+				if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil {
+					// Client disconnect.
+					s.Watching.Stop()
+					return
+				}
+			}
+			buf.Reset()
+			streamBuf.Reset()
+		}
+	}
+}