forked from kubernetes/kubernetes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[client-go] Polymorphic Scale Client
This introduces a polymorphic scale client capable of operating against scale subresources which return different group-versions of Scale. The scale subresources may be in group-versions different than the scale itself, so that we no longer need a copy of every scalable resource in the extensions API group. To discovery which Scale group-versions go to which subresources, discovery is used. The scale client maintains its own internal versions and conversions to several external versions, with a "hub" version that's a copy of the autoscaling internal version. It currently supports the following group-versions for Scale subresources: - extensions/v1beta1.Scale - autoscaling/v1.Scale
- Loading branch information
1 parent
f83a196
commit d61a2d9
Showing
29 changed files
with
1,780 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = [ | ||
"client.go", | ||
"doc.go", | ||
"interfaces.go", | ||
"util.go", | ||
], | ||
importpath = "k8s.io/client-go/scale", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", | ||
"//vendor/k8s.io/client-go/discovery:go_default_library", | ||
"//vendor/k8s.io/client-go/dynamic:go_default_library", | ||
"//vendor/k8s.io/client-go/rest:go_default_library", | ||
"//vendor/k8s.io/client-go/scale/scheme:go_default_library", | ||
"//vendor/k8s.io/client-go/scale/scheme/autoscalingv1:go_default_library", | ||
"//vendor/k8s.io/client-go/scale/scheme/extensionsint:go_default_library", | ||
"//vendor/k8s.io/client-go/scale/scheme/extensionsv1beta1:go_default_library", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "go_default_test", | ||
srcs = [ | ||
"client_test.go", | ||
"roundtrip_test.go", | ||
], | ||
importpath = "k8s.io/client-go/scale", | ||
library = ":go_default_library", | ||
deps = [ | ||
"//vendor/github.com/stretchr/testify/assert:go_default_library", | ||
"//vendor/k8s.io/api/apps/v1beta2:go_default_library", | ||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library", | ||
"//vendor/k8s.io/api/core/v1:go_default_library", | ||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/api/testing/roundtrip:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", | ||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", | ||
"//vendor/k8s.io/client-go/discovery:go_default_library", | ||
"//vendor/k8s.io/client-go/discovery/fake:go_default_library", | ||
"//vendor/k8s.io/client-go/dynamic:go_default_library", | ||
"//vendor/k8s.io/client-go/rest/fake:go_default_library", | ||
"//vendor/k8s.io/client-go/testing:go_default_library", | ||
], | ||
) | ||
|
||
filegroup( | ||
name = "package-srcs", | ||
srcs = glob(["**"]), | ||
tags = ["automanaged"], | ||
visibility = ["//visibility:private"], | ||
) | ||
|
||
filegroup( | ||
name = "all-srcs", | ||
srcs = [ | ||
":package-srcs", | ||
"//staging/src/k8s.io/client-go/scale/scheme:all-srcs", | ||
], | ||
tags = ["automanaged"], | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/* | ||
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 scale | ||
|
||
import ( | ||
"fmt" | ||
|
||
autoscaling "k8s.io/api/autoscaling/v1" | ||
"k8s.io/apimachinery/pkg/api/meta" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
serializer "k8s.io/apimachinery/pkg/runtime/serializer" | ||
"k8s.io/client-go/dynamic" | ||
restclient "k8s.io/client-go/rest" | ||
) | ||
|
||
var scaleConverter = NewScaleConverter() | ||
var codecs = serializer.NewCodecFactory(scaleConverter.Scheme()) | ||
|
||
// restInterfaceProvider turns a restclient.Config into a restclient.Interface. | ||
// It's overridable for the purposes of testing. | ||
type restInterfaceProvider func(*restclient.Config) (restclient.Interface, error) | ||
|
||
// scaleClient is an implementation of ScalesGetter | ||
// which makes use of a RESTMapper and a generic REST | ||
// client to support an discoverable resource. | ||
// It behaves somewhat similarly to the dynamic ClientPool, | ||
// but is more specifically scoped to Scale. | ||
type scaleClient struct { | ||
mapper meta.RESTMapper | ||
|
||
apiPathResolverFunc dynamic.APIPathResolverFunc | ||
scaleKindResolver ScaleKindResolver | ||
clientBase restclient.Interface | ||
} | ||
|
||
// NewForConfig creates a new ScalesGetter which resolves kinds | ||
// to resources using the given RESTMapper, and API paths using | ||
// the given dynamic.APIPathResolverFunc. | ||
func NewForConfig(cfg *restclient.Config, mapper meta.RESTMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) (ScalesGetter, error) { | ||
// so that the RESTClientFor doesn't complain | ||
cfg.GroupVersion = &schema.GroupVersion{} | ||
|
||
cfg.NegotiatedSerializer = serializer.DirectCodecFactory{ | ||
CodecFactory: codecs, | ||
} | ||
if len(cfg.UserAgent) == 0 { | ||
cfg.UserAgent = restclient.DefaultKubernetesUserAgent() | ||
} | ||
|
||
client, err := restclient.RESTClientFor(cfg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return New(client, mapper, resolver, scaleKindResolver), nil | ||
} | ||
|
||
// New creates a new ScalesGetter using the given client to make requests. | ||
// The GroupVersion on the client is ignored. | ||
func New(baseClient restclient.Interface, mapper meta.RESTMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) ScalesGetter { | ||
return &scaleClient{ | ||
mapper: mapper, | ||
|
||
apiPathResolverFunc: resolver, | ||
scaleKindResolver: scaleKindResolver, | ||
clientBase: baseClient, | ||
} | ||
} | ||
|
||
// pathAndVersionFor returns the appropriate base path and the associated full GroupVersionResource | ||
// for the given GroupResource | ||
func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) { | ||
gvr, err := c.mapper.ResourceFor(resource.WithVersion("")) | ||
if err != nil { | ||
return "", gvr, fmt.Errorf("unable to get full preferred group-version-resource for %s: %v", resource.String(), err) | ||
} | ||
|
||
groupVer := gvr.GroupVersion() | ||
|
||
// we need to set the API path based on GroupVersion (defaulting to the legacy path if none is set) | ||
// TODO: we "cheat" here since the API path really only depends on group ATM, but this should | ||
// *probably* take GroupVersionResource and not GroupVersionKind. | ||
apiPath := c.apiPathResolverFunc(groupVer.WithKind("")) | ||
if apiPath == "" { | ||
apiPath = "/api" | ||
} | ||
|
||
path := restclient.DefaultVersionedAPIPath(apiPath, groupVer) | ||
|
||
return path, gvr, nil | ||
} | ||
|
||
// namespacedScaleClient is an ScaleInterface for fetching | ||
// Scales in a given namespace. | ||
type namespacedScaleClient struct { | ||
client *scaleClient | ||
namespace string | ||
} | ||
|
||
func (c *scaleClient) Scales(namespace string) ScaleInterface { | ||
return &namespacedScaleClient{ | ||
client: c, | ||
namespace: namespace, | ||
} | ||
} | ||
|
||
func (c *namespacedScaleClient) Get(resource schema.GroupResource, name string) (*autoscaling.Scale, error) { | ||
// Currently, a /scale endpoint can return different scale types. | ||
// Until we have support for the alternative API representations proposal, | ||
// we need to deal with accepting different API versions. | ||
// In practice, this is autoscaling/v1.Scale and extensions/v1beta1.Scale | ||
|
||
path, gvr, err := c.client.pathAndVersionFor(resource) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) | ||
} | ||
|
||
rawObj, err := c.client.clientBase.Get(). | ||
AbsPath(path). | ||
Namespace(c.namespace). | ||
Resource(gvr.Resource). | ||
Name(name). | ||
SubResource("scale"). | ||
Do(). | ||
Get() | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// convert whatever this is to autoscaling/v1.Scale | ||
scaleObj, err := scaleConverter.ConvertToVersion(rawObj, autoscaling.SchemeGroupVersion) | ||
if err != nil { | ||
return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err) | ||
} | ||
|
||
return scaleObj.(*autoscaling.Scale), nil | ||
} | ||
|
||
func (c *namespacedScaleClient) Update(resource schema.GroupResource, scale *autoscaling.Scale) (*autoscaling.Scale, error) { | ||
path, gvr, err := c.client.pathAndVersionFor(resource) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) | ||
} | ||
|
||
// Currently, a /scale endpoint can receive and return different scale types. | ||
// Until we hvae support for the alternative API representations proposal, | ||
// we need to deal with sending and accepting differnet API versions. | ||
|
||
// figure out what scale we actually need here | ||
desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not find proper group-version for scale subresource of %s: %v", gvr.String(), err) | ||
} | ||
|
||
// convert this to whatever this endpoint wants | ||
scaleUpdate, err := scaleConverter.ConvertToVersion(scale, desiredGVK.GroupVersion()) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not convert scale update to internal Scale: %v", err) | ||
} | ||
|
||
rawObj, err := c.client.clientBase.Put(). | ||
AbsPath(path). | ||
Namespace(c.namespace). | ||
Resource(gvr.Resource). | ||
Name(scale.Name). | ||
SubResource("scale"). | ||
Body(scaleUpdate). | ||
Do(). | ||
Get() | ||
|
||
if err != nil { | ||
return nil, fmt.Errorf("could not fetch the scale for %s %s: %v", resource.String(), scale.Name, err) | ||
} | ||
|
||
// convert whatever this is back to autoscaling/v1.Scale | ||
scaleObj, err := scaleConverter.ConvertToVersion(rawObj, autoscaling.SchemeGroupVersion) | ||
if err != nil { | ||
return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err) | ||
} | ||
|
||
return scaleObj.(*autoscaling.Scale), err | ||
} |
Oops, something went wrong.