From 53814de2e21acb92c2bf8243df56398d9d366a82 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Wed, 7 Jun 2017 17:52:51 -0400 Subject: [PATCH] Update dependencies This commit updates the dependencies to the latest versions. This removes the special "resource" and "subresource" context information, in favor of the new RequestInfo context information, which includes resource and subresource. --- .gitignore | 1 + glide.lock | 121 +++++++-- pkg/apiserver/apiserver.go | 2 +- pkg/apiserver/cmapis.go | 9 +- pkg/apiserver/installer/context/context.go | 48 ---- pkg/apiserver/installer/installer.go | 275 +++++---------------- pkg/provider/resource_lister.go | 4 +- pkg/registry/custom_metrics/reststorage.go | 7 +- 8 files changed, 174 insertions(+), 293 deletions(-) delete mode 100644 pkg/apiserver/installer/context/context.go diff --git a/.gitignore b/.gitignore index f9d5cc391..6cee6c60e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *~ sample-main +vendor diff --git a/glide.lock b/glide.lock index 2b782546d..e701df5ce 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 807f261d8170be02e0c3802e4d005c334a04419a8b2e5f75d68498385202f5ff -updated: 2017-04-05T18:21:53.389585483-04:00 +updated: 2017-06-07T14:38:39.211756323-04:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 @@ -8,7 +8,7 @@ imports: subpackages: - quantile - name: github.com/coreos/etcd - version: cc198e22d3b8fd7ec98304c95e68ee375be54589 + version: 20490caaf0dcd96bb4a95e40625559def8ef5b04 subpackages: - alarm - auth @@ -37,12 +37,14 @@ imports: - mvcc/mvccpb - pkg/adt - pkg/contention + - pkg/cpuutil - pkg/crc - pkg/fileutil - pkg/httputil - pkg/idutil - pkg/ioutil - pkg/logutil + - pkg/monotime - pkg/netutil - pkg/pathutil - pkg/pbutil @@ -53,6 +55,8 @@ imports: - pkg/transport - pkg/types - pkg/wait + - proxy/grpcproxy + - proxy/grpcproxy/cache - raft - raft/raftpb - rafthttp @@ -86,46 +90,63 @@ imports: - name: github.com/elazarl/go-bindata-assetfs version: 3dcc96556217539f50599357fb481ac0dc7439b9 - name: github.com/emicklei/go-restful - version: 09691a3b6378b740595c1002f40c34dd5f218a22 + version: ff4f55a206334ef123e4f79bbf348980da81ca46 subpackages: - log - - swagger +- name: github.com/emicklei/go-restful-swagger12 + version: dcef7f55730566d41eae5db10e7d6981829720f6 - name: github.com/evanphx/json-patch version: ba18e35c5c1b36ef6334cad706eb681153d2d379 - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-openapi/analysis + version: b44dc874b601d9e4e2f6e19140e794ba24bead3b - name: github.com/go-openapi/jsonpointer version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 - name: github.com/go-openapi/jsonreference version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 +- name: github.com/go-openapi/loads + version: 18441dfa706d924a39a030ee2c3b1d8d81917b38 - name: github.com/go-openapi/spec version: 6aced65f8501fe1217321abf0749d354824ba2ff - name: github.com/go-openapi/swag version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 - name: github.com/gogo/protobuf - version: e18d7aa8f8c624c915db340349aad4c49b10d173 + version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 subpackages: - proto - sortkeys - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed -- name: github.com/golang/groupcache - version: 02826c3e79038b59d737d3b1c0a1d937f71a4433 - subpackages: - - lru - name: github.com/golang/protobuf - version: 8616e8ee5e20a1704615e6c8d7afcdac06087a67 + version: 4bd1920723d7b7c925de087aa32e2187708897f7 subpackages: - jsonpb - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp - name: github.com/google/gofuzz version: 44d81051d367757e1c7c6a5a86423ece9afcf63c +- name: github.com/googleapis/gnostic + version: 68f4ded48ba9414dab2ae69b3f0d69971da73aa5 + subpackages: + - OpenAPIv2 + - compiler + - extensions +- name: github.com/grpc-ecosystem/go-grpc-prometheus + version: 2500245aa6110c562d17020fb31a2c133d737799 - name: github.com/grpc-ecosystem/grpc-gateway - version: f52d055dc48aec25854ed7d31862f78913cf17d1 + version: 84398b94e188ee336f307779b57b3aa91af7063c subpackages: - runtime - runtime/internal - utilities +- name: github.com/hashicorp/golang-lru + version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 + subpackages: + - simplelru - name: github.com/howeyc/gopass version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d - name: github.com/imdario/mergo @@ -182,7 +203,7 @@ imports: - blowfish - ssh/terminal - name: golang.org/x/net - version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 + version: f2499483f923065a842d38eb4c7f1927e6fc6e6d subpackages: - context - html @@ -212,7 +233,7 @@ imports: - unicode/norm - width - name: google.golang.org/grpc - version: 231b4cfea0e79843053a33f5fe90bd4d84b23cd3 + version: 777daa17ff9b5daef1cfdf915088a2ada3332bf0 subpackages: - codes - credentials @@ -229,7 +250,7 @@ imports: - name: gopkg.in/yaml.v2 version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 - name: k8s.io/apimachinery - version: 20e10d54608f05c3059443a6c0afb9979641e88d + version: 2de00c78cb6d6127fb51b9531c1b3def1cbcac8c subpackages: - pkg/api/equality - pkg/api/errors @@ -244,6 +265,7 @@ imports: - pkg/apis/meta/v1 - pkg/apis/meta/v1/unstructured - pkg/apis/meta/v1/validation + - pkg/apis/meta/v1alpha1 - pkg/conversion - pkg/conversion/queryparams - pkg/conversion/unstructured @@ -260,6 +282,8 @@ imports: - pkg/runtime/serializer/versioning - pkg/selection - pkg/types + - pkg/util/cache + - pkg/util/clock - pkg/util/diff - pkg/util/errors - pkg/util/framer @@ -283,9 +307,10 @@ imports: - third_party/forked/golang/netutil - third_party/forked/golang/reflect - name: k8s.io/apiserver - version: dcf548fbe26dacc3a78d18e1135adf17006552e9 + version: c809cf8581e1e44c6174bf5ab4415e6ee39965ca subpackages: - pkg/admission + - pkg/admission/initializer - pkg/apis/apiserver - pkg/apis/apiserver/install - pkg/apis/apiserver/v1alpha1 @@ -304,6 +329,7 @@ imports: - pkg/authorization/authorizerfactory - pkg/authorization/union - pkg/endpoints + - pkg/endpoints/discovery - pkg/endpoints/filters - pkg/endpoints/handlers - pkg/endpoints/handlers/negotiation @@ -334,7 +360,7 @@ imports: - pkg/storage/names - pkg/storage/storagebackend - pkg/storage/storagebackend/factory - - pkg/util/cache + - pkg/storage/value - pkg/util/feature - pkg/util/flag - pkg/util/flushwriter @@ -347,23 +373,79 @@ imports: - plugin/pkg/authenticator/token/webhook - plugin/pkg/authorizer/webhook - name: k8s.io/client-go - version: dabf37f5df16a224729883d9f616ce4a2c282e95 + version: 450baa5d60f8d6a251c7682cb6f86e939b750b2d subpackages: + - discovery + - informers + - informers/apps + - informers/apps/v1beta1 + - informers/autoscaling + - informers/autoscaling/v1 + - informers/autoscaling/v2alpha1 + - informers/batch + - informers/batch/v1 + - informers/batch/v2alpha1 + - informers/certificates + - informers/certificates/v1beta1 + - informers/core + - informers/core/v1 + - informers/extensions + - informers/extensions/v1beta1 + - informers/internalinterfaces + - informers/policy + - informers/policy/v1beta1 + - informers/rbac + - informers/rbac/v1alpha1 + - informers/rbac/v1beta1 + - informers/settings + - informers/settings/v1alpha1 + - informers/storage + - informers/storage/v1 + - informers/storage/v1beta1 + - kubernetes - kubernetes/scheme + - kubernetes/typed/apps/v1beta1 + - kubernetes/typed/authentication/v1 - kubernetes/typed/authentication/v1beta1 + - kubernetes/typed/authorization/v1 - kubernetes/typed/authorization/v1beta1 + - kubernetes/typed/autoscaling/v1 + - kubernetes/typed/autoscaling/v2alpha1 + - kubernetes/typed/batch/v1 + - kubernetes/typed/batch/v2alpha1 + - kubernetes/typed/certificates/v1beta1 - kubernetes/typed/core/v1 + - kubernetes/typed/extensions/v1beta1 + - kubernetes/typed/policy/v1beta1 + - kubernetes/typed/rbac/v1alpha1 + - kubernetes/typed/rbac/v1beta1 + - kubernetes/typed/settings/v1alpha1 + - kubernetes/typed/storage/v1 + - kubernetes/typed/storage/v1beta1 + - listers/apps/v1beta1 + - listers/autoscaling/v1 + - listers/autoscaling/v2alpha1 + - listers/batch/v1 + - listers/batch/v2alpha1 + - listers/certificates/v1beta1 + - listers/core/v1 + - listers/extensions/v1beta1 + - listers/policy/v1beta1 + - listers/rbac/v1alpha1 + - listers/rbac/v1beta1 + - listers/settings/v1alpha1 + - listers/storage/v1 + - listers/storage/v1beta1 - pkg/api - pkg/api/install - pkg/api/v1 + - pkg/api/v1/ref - pkg/apis/apps - pkg/apis/apps/v1beta1 - pkg/apis/authentication - - pkg/apis/authentication/install - pkg/apis/authentication/v1 - pkg/apis/authentication/v1beta1 - pkg/apis/authorization - - pkg/apis/authorization/install - pkg/apis/authorization/v1 - pkg/apis/authorization/v1beta1 - pkg/apis/autoscaling @@ -400,7 +482,6 @@ imports: - tools/metrics - transport - util/cert - - util/clock - util/flowcontrol - util/homedir - util/integer diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index b9e090f1f..e6c58f593 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -88,7 +88,7 @@ func (c *Config) SkipComplete() completedConfig { // New returns a new instance of CustomMetricsAdapterServer from the given config. func (c completedConfig) New(cmProvider provider.CustomMetricsProvider) (*CustomMetricsAdapterServer, error) { - genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time + genericServer, err := c.Config.GenericConfig.SkipComplete().New(genericapiserver.EmptyDelegate) // completion is done in Complete, no need for a second time if err != nil { return nil, err } diff --git a/pkg/apiserver/cmapis.go b/pkg/apiserver/cmapis.go index 2c234bc2c..d0c0bd5a2 100644 --- a/pkg/apiserver/cmapis.go +++ b/pkg/apiserver/cmapis.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" genericapi "k8s.io/apiserver/pkg/endpoints" genericapiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/endpoints/discovery" specificapi "k8s.io/custom-metrics-boilerplate/pkg/apiserver/installer" "k8s.io/custom-metrics-boilerplate/pkg/provider" @@ -43,19 +44,19 @@ func (s *CustomMetricsAdapterServer) InstallCustomMetricsAPI() error { Version: groupMeta.GroupVersion.Version, } apiGroup := metav1.APIGroup{ - Name: groupMeta.GroupVersion.String(), + Name: groupMeta.GroupVersion.Group, Versions: []metav1.GroupVersionForDiscovery{groupVersion}, PreferredVersion: preferredVersionForDiscovery, } cmAPI := s.cmAPI(groupMeta, &groupMeta.GroupVersion) - if err := cmAPI.InstallREST(s.GenericAPIServer.HandlerContainer.Container); err != nil { + if err := cmAPI.InstallREST(s.GenericAPIServer.Handler.GoRestfulContainer); err != nil { return err } - path := genericapiserver.APIGroupPrefix + "/" + groupMeta.GroupVersion.Group - s.GenericAPIServer.HandlerContainer.Add(genericapi.NewGroupWebService(s.GenericAPIServer.Serializer, path, apiGroup)) + s.GenericAPIServer.DiscoveryGroupManager.AddGroup(apiGroup) + s.GenericAPIServer.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.GenericAPIServer.Serializer, apiGroup).WebService()) return nil } diff --git a/pkg/apiserver/installer/context/context.go b/pkg/apiserver/installer/context/context.go deleted file mode 100644 index 042995565..000000000 --- a/pkg/apiserver/installer/context/context.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -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 context - -import ( - "k8s.io/apiserver/pkg/endpoints/request" -) - -// resourceInformation holds the resource and subresource for a request in the context. -type resourceInformation struct { - resource string - subresource string -} - -// contextKey is the type of the keys for the context in this file. -// It's private to avoid conflicts across packages. -type contextKey int - -const resourceKey contextKey = iota - -// WithResourceInformation returns a copy of parent in which the resource and subresource values are set -func WithResourceInformation(parent request.Context, resource, subresource string) request.Context { - return request.WithValue(parent, resourceKey, resourceInformation{resource, subresource}) -} - -// ResourceInformationFrom returns resource and subresource on the ctx -func ResourceInformationFrom(ctx request.Context) (resource string, subresource string, ok bool) { - resourceInfo, ok := ctx.Value(resourceKey).(resourceInformation) - if !ok { - return "", "", ok - } - - return resourceInfo.resource, resourceInfo.subresource, ok -} diff --git a/pkg/apiserver/installer/installer.go b/pkg/apiserver/installer/installer.go index a56d93f53..369ac6d5a 100644 --- a/pkg/apiserver/installer/installer.go +++ b/pkg/apiserver/installer/installer.go @@ -17,32 +17,28 @@ limitations under the License. package installer import ( - "bytes" "fmt" "net/http" - "net/url" gpath "path" "reflect" "strings" "time" - "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + utilerrors "k8s.io/apimachinery/pkg/util/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apiserver/pkg/endpoints" "k8s.io/apiserver/pkg/endpoints/handlers" "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" "k8s.io/apiserver/pkg/endpoints/metrics" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/endpoints/discovery" "github.com/emicklei/go-restful" - - specificcontext "k8s.io/custom-metrics-boilerplate/pkg/apiserver/installer/context" ) // NB: the contents of this file should mostly be a subset of the functionality @@ -74,7 +70,8 @@ func (g *MetricsAPIGroupVersion) InstallREST(container *restful.Container) error if lister == nil { return fmt.Errorf("must provide a dynamic lister for dynamic API groups") } - endpoints.AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, lister) + versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, lister) + versionDiscoveryHandler.AddToWebService(ws) container.Add(ws) return utilerrors.NewAggregate(registrationErrors) } @@ -174,26 +171,11 @@ func (a *MetricsAPIInstaller) registerResourceHandlers(storage rest.Storage, ws return err } - ctxFn := func(req *restful.Request) request.Context { - var ctx request.Context - if ctx != nil { - if existingCtx, ok := context.Get(req.Request); ok { - ctx = existingCtx - } + ctxFn := func(req *http.Request) request.Context { + if ctx, ok := context.Get(req); ok { + return request.WithUserAgent(ctx, req.Header.Get("User-Agent")) } - if ctx == nil { - ctx = request.NewContext() - } - - ctx = request.WithUserAgent(ctx, req.HeaderParameter("User-Agent")) - - // inject the resource, subresource, and name here so that - // we don't have to write custom handler logic - resource := req.PathParameter("resource") - subresource := req.PathParameter("subresource") - ctx = specificcontext.WithResourceInformation(ctx, resource, subresource) - - return ctx + return request.WithUserAgent(request.NewContext(), req.Header.Get("User-Agent")) } scope := mapping.Scope @@ -218,52 +200,12 @@ func (a *MetricsAPIInstaller) registerResourceHandlers(storage rest.Storage, ws subresourceParam, } namespacedPath := scope.ParamName() + "/{" + scope.ArgumentName() + "}/{resource}/{name}/{subresource}" - namespacedPathPrefix := gpath.Join(a.prefix, scope.ParamName()) + "/" - itemPathFn := func(name, namespace, resource, subresource string) bytes.Buffer { - var buf bytes.Buffer - buf.WriteString(namespacedPathPrefix) - buf.WriteString(url.QueryEscape(namespace)) - buf.WriteString("/") - buf.WriteString(url.QueryEscape(resource)) - buf.WriteString("/") - buf.WriteString(url.QueryEscape(name)) - buf.WriteString("/") - buf.WriteString(url.QueryEscape(subresource)) - return buf - } namespaceSpecificPath := scope.ParamName() + "/{" + scope.ArgumentName() + "}/metrics/{name}" namespaceSpecificParams := []*restful.Parameter{ namespaceParam, nameParam, } - namespaceSpecificItemPathFn := func(name, namespace, resource, subresource string) bytes.Buffer { - var buf bytes.Buffer - buf.WriteString(namespacedPathPrefix) - buf.WriteString(url.QueryEscape(namespace)) - buf.WriteString("/metrics/") - buf.WriteString(url.QueryEscape(name)) - return buf - } - namespaceSpecificCtxFn := func(req *restful.Request) request.Context { - var ctx request.Context - if ctx != nil { - if existingCtx, ok := context.Get(req.Request); ok { - ctx = existingCtx - } - } - if ctx == nil { - ctx = request.NewContext() - } - - ctx = request.WithUserAgent(ctx, req.HeaderParameter("User-Agent")) - - // inject the resource, subresource, and name here so that - // we don't have to write custom handler logic - ctx = specificcontext.WithResourceInformation(ctx, "metrics", "") - - return ctx - } mediaTypes, streamMediaTypes := negotiation.MediaTypesForSerializer(a.group.Serializer) allMediaTypes := append(mediaTypes, streamMediaTypes...) @@ -292,8 +234,16 @@ func (a *MetricsAPIInstaller) registerResourceHandlers(storage rest.Storage, ws // we need one path for namespaced resources, one for non-namespaced resources doc := "list custom metrics describing an object or objects" - reqScope.Namer = rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, rootScopedPath, "/"), "/{subresource}"} - rootScopedHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics", handlers.ListResource(lister, nil, reqScope, false, a.minRequestTimeout)) + reqScope.Namer = MetricsNaming{ + handlers.ContextBasedNaming{ + GetContext: ctxFn, + SelfLinker: a.group.Linker, + ClusterScoped: true, + SelfLinkPathPrefix: a.prefix + "/", + }, + } + + rootScopedHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics", restfulListResource(lister, nil, reqScope, false, a.minRequestTimeout)) // install the root-scoped route rootScopedRoute := ws.GET(rootScopedPath).To(rootScopedHandler). @@ -310,8 +260,15 @@ func (a *MetricsAPIInstaller) registerResourceHandlers(storage rest.Storage, ws ws.Route(rootScopedRoute) // install the namespace-scoped route - reqScope.Namer = scopeNaming{scope, a.group.Linker, itemPathFn, false} - namespacedHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics-namespaced", handlers.ListResource(lister, nil, reqScope, false, a.minRequestTimeout)) + reqScope.Namer = MetricsNaming{ + handlers.ContextBasedNaming{ + GetContext: ctxFn, + SelfLinker: a.group.Linker, + ClusterScoped: false, + SelfLinkPathPrefix: gpath.Join(a.prefix, scope.ParamName()) + "/", + }, + } + namespacedHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics-namespaced", restfulListResource(lister, nil, reqScope, false, a.minRequestTimeout)) namespacedRoute := ws.GET(namespacedPath).To(namespacedHandler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). @@ -326,9 +283,16 @@ func (a *MetricsAPIInstaller) registerResourceHandlers(storage rest.Storage, ws ws.Route(namespacedRoute) // install the special route for metrics describing namespaces (last b/c we modify the context func) - reqScope.ContextFunc = namespaceSpecificCtxFn - reqScope.Namer = scopeNaming{scope, a.group.Linker, namespaceSpecificItemPathFn, false} - namespaceSpecificHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics-for-namespace", handlers.ListResource(lister, nil, reqScope, false, a.minRequestTimeout)) + reqScope.ContextFunc = ctxFn + reqScope.Namer = MetricsNaming{ + handlers.ContextBasedNaming{ + GetContext: ctxFn, + SelfLinker: a.group.Linker, + ClusterScoped: false, + SelfLinkPathPrefix: gpath.Join(a.prefix, scope.ParamName()) + "/", + }, + } + namespaceSpecificHandler := metrics.InstrumentRouteFunc("LIST", "custom-metrics-for-namespace", restfulListResource(lister, nil, reqScope, false, a.minRequestTimeout)) namespaceSpecificRoute := ws.GET(namespaceSpecificPath).To(namespaceSpecificHandler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). @@ -474,160 +438,39 @@ func typeToJSON(typeName string) string { } } -// rootScopeNaming reads only names from a request and ignores namespaces. It implements ScopeNamer -// for root scoped resources. -type rootScopeNaming struct { - scope meta.RESTScope - runtime.SelfLinker - pathPrefix string - pathSuffix string -} - -// rootScopeNaming implements ScopeNamer -var _ handlers.ScopeNamer = rootScopeNaming{} - -// Namespace returns an empty string because root scoped objects have no namespace. -func (n rootScopeNaming) Namespace(req *restful.Request) (namespace string, err error) { - return "", nil -} - -// Name returns the name from the path and an empty string for namespace, or an error if the -// name is empty. -func (n rootScopeNaming) Name(req *restful.Request) (namespace, name string, err error) { - name = req.PathParameter("name") - if len(name) == 0 { - return "", "", errEmptyName - } - return "", name, nil -} - -// GenerateLink returns the appropriate path and query to locate an object by its canonical path. -func (n rootScopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) { - _, name, err := n.ObjectName(obj) - if err != nil { - return "", err - } - if len(name) == 0 { - _, name, err = n.Name(req) - if err != nil { - return "", err - } - } - return n.pathPrefix + url.QueryEscape(name) + n.pathSuffix, nil -} - -// GenerateListLink returns the appropriate path and query to locate a list by its canonical path. -func (n rootScopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) { - if len(req.Request.URL.RawPath) > 0 { - return req.Request.URL.RawPath, nil - } - return req.Request.URL.EscapedPath(), nil -} - -// ObjectName returns the name set on the object, or an error if the -// name cannot be returned. Namespace is empty -// TODO: distinguish between objects with name/namespace and without via a specific error. -func (n rootScopeNaming) 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 - } - return "", name, nil +// An interface to see if an object supports swagger documentation as a method +type documentable interface { + SwaggerDoc() map[string]string } -// scopeNaming returns naming information from a request. It implements ScopeNamer for -// namespace scoped resources. -type scopeNaming struct { - scope meta.RESTScope - runtime.SelfLinker - itemPathFn func(name, namespace, resource, subresource string) bytes.Buffer - allNamespaces bool +// MetricsNaming is similar to handlers.ContextBasedNaming, except that it handles +// polymorphism over subresources. +type MetricsNaming struct { + handlers.ContextBasedNaming } -// scopeNaming implements ScopeNamer -var _ handlers.ScopeNamer = scopeNaming{} - -// Namespace returns the namespace from the path or the default. -func (n scopeNaming) Namespace(req *restful.Request) (namespace string, err error) { - if n.allNamespaces { - return "", nil +func (n MetricsNaming) GenerateLink(req *http.Request, obj runtime.Object) (uri string, err error) { + requestInfo, ok := request.RequestInfoFrom(n.GetContext(req)) + if !ok { + return "", fmt.Errorf("missing requestInfo") } - namespace = req.PathParameter(n.scope.ArgumentName()) - if len(namespace) == 0 { - // a URL was constructed without the namespace, or this method was invoked - // on an object without a namespace path parameter. - return "", fmt.Errorf("no namespace parameter found on request") - } - return namespace, nil -} -// Name returns the name from the path, the namespace (or default), or an error if the -// name is empty. -func (n scopeNaming) Name(req *restful.Request) (namespace, name string, err error) { - namespace, _ = n.Namespace(req) - name = req.PathParameter("name") - if len(name) == 0 { - return "", "", errEmptyName + if requestInfo.Resource != "metrics" { + n.SelfLinkPathSuffix += "/" + requestInfo.Subresource } - return -} - -func (n scopeNaming) reqResource(req *restful.Request) (resource, subresource string) { - return req.PathParameter("resource"), req.PathParameter("subresource") -} -// GenerateLink returns the appropriate path and query to locate an object by its canonical path. -func (n scopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (uri string, err error) { - namespace, name, err := n.ObjectName(obj) - if err != nil { - return "", err + // since this is not a pointer receiver, it's ok to modify it here + // (since we copy around every method call) + if n.ClusterScoped { + n.SelfLinkPathPrefix += requestInfo.Resource + "/" + return n.ContextBasedNaming.GenerateLink(req, obj) } - if len(namespace) == 0 && len(name) == 0 { - namespace, name, err = n.Name(req) - if err != nil { - return "", err - } - } - if len(name) == 0 { - return "", errEmptyName - } - - resource, subresource := n.reqResource(req) - result := n.itemPathFn(name, namespace, resource, subresource) - return result.String(), nil + return n.ContextBasedNaming.GenerateLink(req, obj) } -// GenerateListLink returns the appropriate path and query to locate a list by its canonical path. -func (n scopeNaming) GenerateListLink(req *restful.Request) (uri string, err error) { - if len(req.Request.URL.RawPath) > 0 { - return req.Request.URL.RawPath, nil - } - return req.Request.URL.EscapedPath(), nil -} - -// ObjectName returns the name and namespace set on the object, or an error if the -// name cannot be returned. -// TODO: distinguish between objects with name/namespace and without via a specific error. -func (n scopeNaming) ObjectName(obj runtime.Object) (namespace, name string, err error) { - name, err = n.SelfLinker.Name(obj) - if err != nil { - return "", "", err +func restfulListResource(r rest.Lister, rw rest.Watcher, scope handlers.RequestScope, forceWatch bool, minRequestTimeout time.Duration) restful.RouteFunction { + return func(req *restful.Request, res *restful.Response) { + handlers.ListResource(r, rw, scope, forceWatch, minRequestTimeout)(res.ResponseWriter, req.Request) } - namespace, err = n.SelfLinker.Namespace(obj) - if err != nil { - return "", "", err - } - return namespace, name, err -} - -// An interface to see if an object supports swagger documentation as a method -type documentable interface { - SwaggerDoc() map[string]string } - -// 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/pkg/provider/resource_lister.go b/pkg/provider/resource_lister.go index dab46e233..783f57f34 100644 --- a/pkg/provider/resource_lister.go +++ b/pkg/provider/resource_lister.go @@ -17,7 +17,7 @@ limitations under the License. package provider import ( - "k8s.io/apiserver/pkg/endpoints/handlers" + "k8s.io/apiserver/pkg/endpoints/discovery" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -25,7 +25,7 @@ type customMetricsResourceLister struct { provider CustomMetricsProvider } -func NewResourceLister(provider CustomMetricsProvider) handlers.APIResourceLister { +func NewResourceLister(provider CustomMetricsProvider) discovery.APIResourceLister { return &customMetricsResourceLister{ provider: provider, } diff --git a/pkg/registry/custom_metrics/reststorage.go b/pkg/registry/custom_metrics/reststorage.go index afcc2e427..8e7d74766 100644 --- a/pkg/registry/custom_metrics/reststorage.go +++ b/pkg/registry/custom_metrics/reststorage.go @@ -25,9 +25,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - specificinstaller "k8s.io/custom-metrics-boilerplate/pkg/apiserver/installer/context" "k8s.io/custom-metrics-boilerplate/pkg/provider" "k8s.io/metrics/pkg/apis/custom_metrics" + "k8s.io/apiserver/pkg/endpoints/request" ) type REST struct { @@ -74,11 +74,14 @@ func (r *REST) List(ctx genericapirequest.Context, options *metainternalversion. namespace := genericapirequest.NamespaceValue(ctx) - resourceRaw, metricName, ok := specificinstaller.ResourceInformationFrom(ctx) + requestInfo, ok := request.RequestInfoFrom(ctx) if !ok { return nil, fmt.Errorf("unable to get resource and metric name from request") } + resourceRaw := requestInfo.Resource + metricName := requestInfo.Subresource + groupResource := schema.ParseGroupResource(resourceRaw) // handle metrics describing namespaces