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(`&`, "&", `<`, "<", `>`, ">")
+
+// 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()
+ }
+ }
+}