diff --git a/pkg/client/unversioned/autoscaling.go b/pkg/client/unversioned/autoscaling.go new file mode 100644 index 00000000000..9445c0c6c93 --- /dev/null +++ b/pkg/client/unversioned/autoscaling.go @@ -0,0 +1,82 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 unversioned + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" +) + +type AutoscalingInterface interface { + HorizontalPodAutoscalersNamespacer +} + +// AutoscalingClient is used to interact with Kubernetes autoscaling features. +type AutoscalingClient struct { + *RESTClient +} + +func (c *AutoscalingClient) HorizontalPodAutoscalers(namespace string) HorizontalPodAutoscalerInterface { + return newHorizontalPodAutoscalersV1(c, namespace) +} + +func NewAutoscaling(c *Config) (*AutoscalingClient, error) { + config := *c + if err := setAutoscalingDefaults(&config); err != nil { + return nil, err + } + client, err := RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AutoscalingClient{client}, nil +} + +func NewAutoscalingOrDie(c *Config) *AutoscalingClient { + client, err := NewAutoscaling(c) + if err != nil { + panic(err) + } + return client +} + +func setAutoscalingDefaults(config *Config) error { + // if autoscaling group is not registered, return an error + g, err := registered.Group(autoscaling.GroupName) + if err != nil { + return err + } + config.APIPath = defaultAPIPath + if config.UserAgent == "" { + config.UserAgent = DefaultKubernetesUserAgent() + } + // TODO: Unconditionally set the config.Version, until we fix the config. + //if config.Version == "" { + copyGroupVersion := g.GroupVersion + config.GroupVersion = ©GroupVersion + //} + + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) + if config.QPS == 0 { + config.QPS = 5 + } + if config.Burst == 0 { + config.Burst = 10 + } + return nil +} diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index 24bc54c9376..8a891c44a9a 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -41,6 +41,7 @@ type Interface interface { PersistentVolumeClaimsNamespacer ComponentStatusesInterface ConfigMapsNamespacer + Autoscaling() AutoscalingInterface Extensions() ExtensionsInterface Discovery() DiscoveryInterface } @@ -111,6 +112,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface { // Client is the implementation of a Kubernetes client. type Client struct { *RESTClient + *AutoscalingClient *ExtensionsClient *DiscoveryClient } @@ -146,6 +148,10 @@ func IsTimeout(err error) bool { return false } +func (c *Client) Autoscaling() AutoscalingInterface { + return c.AutoscalingClient +} + func (c *Client) Extensions() ExtensionsInterface { return c.ExtensionsClient } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 507f9a30a10..346b3fdc478 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" @@ -155,16 +156,25 @@ func New(c *Config) (*Client, error) { return nil, err } - if _, err := registered.Group(extensions.GroupName); err != nil { - return &Client{RESTClient: client, ExtensionsClient: nil, DiscoveryClient: discoveryClient}, nil - } - experimentalConfig := *c - experimentalClient, err := NewExtensions(&experimentalConfig) - if err != nil { - return nil, err + var autoscalingClient *AutoscalingClient + if registered.IsRegistered(autoscaling.GroupName) { + autoscalingConfig := *c + autoscalingClient, err = NewAutoscaling(&autoscalingConfig) + if err != nil { + return nil, err + } } - return &Client{RESTClient: client, ExtensionsClient: experimentalClient, DiscoveryClient: discoveryClient}, nil + var extensionsClient *ExtensionsClient + if registered.IsRegistered(extensions.GroupName) { + extensionsConfig := *c + extensionsClient, err = NewExtensions(&extensionsConfig) + if err != nil { + return nil, err + } + } + + return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil } // MatchesServerVersion queries the server to compares the build version diff --git a/pkg/client/unversioned/horizontalpodautoscaler.go b/pkg/client/unversioned/horizontalpodautoscaler.go index efdf82b70ed..a4efc232a41 100644 --- a/pkg/client/unversioned/horizontalpodautoscaler.go +++ b/pkg/client/unversioned/horizontalpodautoscaler.go @@ -101,3 +101,68 @@ func (c *horizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, VersionedParams(&opts, api.ParameterCodec). Watch() } + +// horizontalPodAutoscalersV1 implements HorizontalPodAutoscalersNamespacer interface using AutoscalingClient internally +// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely +type horizontalPodAutoscalersV1 struct { + client *AutoscalingClient + ns string +} + +// newHorizontalPodAutoscalers returns a horizontalPodAutoscalers +func newHorizontalPodAutoscalersV1(c *AutoscalingClient, namespace string) *horizontalPodAutoscalersV1 { + return &horizontalPodAutoscalersV1{ + client: c, + ns: namespace, + } +} + +// List takes label and field selectors, and returns the list of horizontalPodAutoscalers that match those selectors. +func (c *horizontalPodAutoscalersV1) List(opts api.ListOptions) (result *extensions.HorizontalPodAutoscalerList, err error) { + result = &extensions.HorizontalPodAutoscalerList{} + err = c.client.Get().Namespace(c.ns).Resource("horizontalPodAutoscalers").VersionedParams(&opts, api.ParameterCodec).Do().Into(result) + return +} + +// Get takes the name of the horizontalPodAutoscaler, and returns the corresponding HorizontalPodAutoscaler object, and an error if it occurs +func (c *horizontalPodAutoscalersV1) Get(name string) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Get().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(name).Do().Into(result) + return +} + +// Delete takes the name of the horizontalPodAutoscaler and deletes it. Returns an error if one occurs. +func (c *horizontalPodAutoscalersV1) Delete(name string, options *api.DeleteOptions) error { + return c.client.Delete().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(name).Body(options).Do().Error() +} + +// Create takes the representation of a horizontalPodAutoscaler and creates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) Create(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Post().Namespace(c.ns).Resource("horizontalPodAutoscalers").Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// Update takes the representation of a horizontalPodAutoscaler and updates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) Update(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Put().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(horizontalPodAutoscaler.Name).Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// UpdateStatus takes the representation of a horizontalPodAutoscaler and updates it. Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs. +func (c *horizontalPodAutoscalersV1) UpdateStatus(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { + result = &extensions.HorizontalPodAutoscaler{} + err = c.client.Put().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(horizontalPodAutoscaler.Name).SubResource("status").Body(horizontalPodAutoscaler).Do().Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested horizontalPodAutoscalers. +func (c *horizontalPodAutoscalersV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.client.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("horizontalPodAutoscalers"). + VersionedParams(&opts, api.ParameterCodec). + Watch() +} diff --git a/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go b/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go index a70a1af99db..e50b326d914 100644 --- a/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go +++ b/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go @@ -91,3 +91,74 @@ func (c *FakeHorizontalPodAutoscalers) Delete(name string, options *api.DeleteOp func (c *FakeHorizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, error) { return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) } + +// FakeHorizontalPodAutoscalers implements HorizontalPodAutoscalerInterface. Meant to be embedded into a struct to get a default +// implementation. This makes faking out just the methods you want to test easier. +// This is a test implementation of HorizontalPodAutoscalersV1 +// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely +type FakeHorizontalPodAutoscalersV1 struct { + Fake *FakeAutoscaling + Namespace string +} + +func (c *FakeHorizontalPodAutoscalersV1) Get(name string) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewGetAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) List(opts api.ListOptions) (*extensions.HorizontalPodAutoscalerList, error) { + obj, err := c.Fake.Invokes(NewListAction("horizontalpodautoscalers", c.Namespace, opts), &extensions.HorizontalPodAutoscalerList{}) + if obj == nil { + return nil, err + } + label := opts.LabelSelector + if label == nil { + label = labels.Everything() + } + list := &extensions.HorizontalPodAutoscalerList{} + for _, a := range obj.(*extensions.HorizontalPodAutoscalerList).Items { + if label.Matches(labels.Set(a.Labels)) { + list.Items = append(list.Items, a) + } + } + return list, err +} + +func (c *FakeHorizontalPodAutoscalersV1) Create(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewCreateAction("horizontalpodautoscalers", c.Namespace, a), a) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) Update(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewUpdateAction("horizontalpodautoscalers", c.Namespace, a), a) + if obj == nil { + return nil, err + } + + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) UpdateStatus(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { + obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.Namespace, a), &extensions.HorizontalPodAutoscaler{}) + if obj == nil { + return nil, err + } + return obj.(*extensions.HorizontalPodAutoscaler), err +} + +func (c *FakeHorizontalPodAutoscalersV1) Delete(name string, options *api.DeleteOptions) error { + _, err := c.Fake.Invokes(NewDeleteAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) + return err +} + +func (c *FakeHorizontalPodAutoscalersV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) +} diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index 7325f16aed1..08b979501d2 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -84,6 +84,10 @@ func (c *Client) Setup(t *testing.T) *Client { // TODO: caesarxuchao: hacky way to specify version of Experimental client. // We will fix this by supporting multiple group versions in Config + c.AutoscalingClient = client.NewAutoscalingOrDie(&client.Config{ + Host: c.server.URL, + ContentConfig: client.ContentConfig{GroupVersion: testapi.Autoscaling.GroupVersion()}, + }) c.ExtensionsClient = client.NewExtensionsOrDie(&client.Config{ Host: c.server.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()}, diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 0da88798418..ef962e41743 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -274,6 +274,10 @@ func (c *Fake) Namespaces() client.NamespaceInterface { return &FakeNamespaces{Fake: c} } +func (c *Fake) Autoscaling() client.AutoscalingInterface { + return &FakeAutoscaling{c} +} + func (c *Fake) Extensions() client.ExtensionsInterface { return &FakeExperimental{c} } @@ -304,6 +308,19 @@ func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDecl return &swagger.ApiDeclaration{}, nil } +// NewSimpleFakeAutoscaling returns a client that will respond with the provided objects +func NewSimpleFakeAutoscaling(objects ...runtime.Object) *FakeAutoscaling { + return &FakeAutoscaling{Fake: NewSimpleFake(objects...)} +} + +type FakeAutoscaling struct { + *Fake +} + +func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.HorizontalPodAutoscalerInterface { + return &FakeHorizontalPodAutoscalersV1{Fake: c, Namespace: namespace} +} + // NewSimpleFakeExp returns a client that will respond with the provided objects func NewSimpleFakeExp(objects ...runtime.Object) *FakeExperimental { return &FakeExperimental{Fake: NewSimpleFake(objects...)} diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index ce75c476bdc..9fbd31a4a3f 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -217,6 +218,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { switch mapping.GroupVersionKind.Group { case api.GroupName: return client.RESTClient, nil + case autoscaling.GroupName: + return client.AutoscalingClient.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } @@ -700,6 +703,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok { return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.EnabledVersions()) } + if gvk.Group == autoscaling.GroupName { + if c.c.AutoscalingClient == nil { + return errors.New("unable to validate: no autoscaling client") + } + return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) + } if gvk.Group == extensions.GroupName { if c.c.ExtensionsClient == nil { return errors.New("unable to validate: no experimental client")