From 175addf2a37a720e08a27ae7a07d24bd91b17c7e Mon Sep 17 00:00:00 2001 From: Eric Tune Date: Wed, 17 Feb 2016 15:07:38 -0800 Subject: [PATCH] Implemented Batch client --- pkg/api/testapi/testapi.go | 1 + pkg/client/unversioned/batch.go | 82 +++++ pkg/client/unversioned/client.go | 6 + pkg/client/unversioned/helper.go | 12 +- .../horizontalpodautoscaler_test.go | 16 +- pkg/client/unversioned/jobs.go | 64 ++++ pkg/client/unversioned/jobs_test.go | 296 ++++-------------- .../unversioned/testclient/fake_jobs.go | 63 ++++ .../testclient/simple/simple_testclient.go | 4 + .../unversioned/testclient/testclient.go | 17 + pkg/kubectl/cmd/drain.go | 2 +- pkg/kubectl/cmd/util/factory.go | 9 + 12 files changed, 334 insertions(+), 238 deletions(-) create mode 100644 pkg/client/unversioned/batch.go diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index 965e6f3dee3..93138bef156 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -33,6 +33,7 @@ import ( _ "k8s.io/kubernetes/pkg/api/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" + _ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install" diff --git a/pkg/client/unversioned/batch.go b/pkg/client/unversioned/batch.go new file mode 100644 index 00000000000..d25678c0660 --- /dev/null +++ b/pkg/client/unversioned/batch.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/batch" +) + +type BatchInterface interface { + JobsNamespacer +} + +// BatchClient is used to interact with Kubernetes batch features. +type BatchClient struct { + *RESTClient +} + +func (c *BatchClient) Jobs(namespace string) JobInterface { + return newJobsV1(c, namespace) +} + +func NewBatch(c *Config) (*BatchClient, error) { + config := *c + if err := setBatchDefaults(&config); err != nil { + return nil, err + } + client, err := RESTClientFor(&config) + if err != nil { + return nil, err + } + return &BatchClient{client}, nil +} + +func NewBatchOrDie(c *Config) *BatchClient { + client, err := NewBatch(c) + if err != nil { + panic(err) + } + return client +} + +func setBatchDefaults(config *Config) error { + // if batch group is not registered, return an error + g, err := registered.Group(batch.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 8a891c44a9a..d4a9692a5eb 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -42,6 +42,7 @@ type Interface interface { ComponentStatusesInterface ConfigMapsNamespacer Autoscaling() AutoscalingInterface + Batch() BatchInterface Extensions() ExtensionsInterface Discovery() DiscoveryInterface } @@ -113,6 +114,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface { type Client struct { *RESTClient *AutoscalingClient + *BatchClient *ExtensionsClient *DiscoveryClient } @@ -152,6 +154,10 @@ func (c *Client) Autoscaling() AutoscalingInterface { return c.AutoscalingClient } +func (c *Client) Batch() BatchInterface { + return c.BatchClient +} + func (c *Client) Extensions() ExtensionsInterface { return c.ExtensionsClient } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index 346b3fdc478..069de64890b 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" @@ -165,6 +166,15 @@ func New(c *Config) (*Client, error) { } } + var batchClient *BatchClient + if registered.IsRegistered(batch.GroupName) { + batchConfig := *c + batchClient, err = NewBatch(&batchConfig) + if err != nil { + return nil, err + } + } + var extensionsClient *ExtensionsClient if registered.IsRegistered(extensions.GroupName) { extensionsConfig := *c @@ -174,7 +184,7 @@ func New(c *Config) (*Client, error) { } } - return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil + return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil } // MatchesServerVersion queries the server to compares the build version diff --git a/pkg/client/unversioned/horizontalpodautoscaler_test.go b/pkg/client/unversioned/horizontalpodautoscaler_test.go index b35d258af3e..b4552be12c9 100644 --- a/pkg/client/unversioned/horizontalpodautoscaler_test.go +++ b/pkg/client/unversioned/horizontalpodautoscaler_test.go @@ -35,7 +35,7 @@ func getHorizontalPodAutoscalersResoureName() string { return "horizontalpodautoscalers" } -func getClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface { +func getHPAClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface { switch resourceGroup { case autoscaling.GroupName: return c.Setup(t).Autoscaling().HorizontalPodAutoscalers(ns) @@ -66,7 +66,7 @@ func testHorizontalPodAutoscalerCreate(t *testing.T, group testapi.TestGroup, re ResourceGroup: resourceGroup, } - response, err := getClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler) + response, err := getHPAClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler) defer c.Close() if err != nil { t.Fatalf("unexpected error: %v", err) @@ -98,7 +98,7 @@ func testHorizontalPodAutoscalerGet(t *testing.T, group testapi.TestGroup, resou ResourceGroup: resourceGroup, } - response, err := getClient(t, c, ns, resourceGroup).Get("abc") + response, err := getHPAClient(t, c, ns, resourceGroup).Get("abc") defer c.Close() c.Validate(t, response, err) } @@ -130,7 +130,7 @@ func testHorizontalPodAutoscalerList(t *testing.T, group testapi.TestGroup, reso Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList}, ResourceGroup: resourceGroup, } - response, err := getClient(t, c, ns, resourceGroup).List(api.ListOptions{}) + response, err := getHPAClient(t, c, ns, resourceGroup).List(api.ListOptions{}) defer c.Close() c.Validate(t, response, err) } @@ -154,7 +154,7 @@ func testHorizontalPodAutoscalerUpdate(t *testing.T, group testapi.TestGroup, re Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, ResourceGroup: resourceGroup, } - response, err := getClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler) + response, err := getHPAClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } @@ -178,7 +178,7 @@ func testHorizontalPodAutoscalerUpdateStatus(t *testing.T, group testapi.TestGro Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, ResourceGroup: resourceGroup, } - response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler) + response, err := getHPAClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler) defer c.Close() c.Validate(t, response, err) } @@ -195,7 +195,7 @@ func testHorizontalPodAutoscalerDelete(t *testing.T, group testapi.TestGroup, re Response: simple.Response{StatusCode: 200}, ResourceGroup: resourceGroup, } - err := getClient(t, c, ns, resourceGroup).Delete("foo", nil) + err := getHPAClient(t, c, ns, resourceGroup).Delete("foo", nil) defer c.Close() c.Validate(t, nil, err) } @@ -214,7 +214,7 @@ func testHorizontalPodAutoscalerWatch(t *testing.T, group testapi.TestGroup, res Response: simple.Response{StatusCode: 200}, ResourceGroup: resourceGroup, } - _, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{}) + _, err := getHPAClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{}) defer c.Close() c.Validate(t, nil, err) } diff --git a/pkg/client/unversioned/jobs.go b/pkg/client/unversioned/jobs.go index 0d10997c7a3..f965a087493 100644 --- a/pkg/client/unversioned/jobs.go +++ b/pkg/client/unversioned/jobs.go @@ -101,3 +101,67 @@ func (c *jobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err er err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result) return } + +// jobsV1 implements JobsNamespacer interface using BatchClient internally +type jobsV1 struct { + r *BatchClient + ns string +} + +// newJobsV1 returns a jobsV1 +func newJobsV1(c *BatchClient, namespace string) *jobsV1 { + return &jobsV1{c, namespace} +} + +// Ensure statically that jobsV1 implements JobInterface. +var _ JobInterface = &jobsV1{} + +// List returns a list of jobs that match the label and field selectors. +func (c *jobsV1) List(opts api.ListOptions) (result *extensions.JobList, err error) { + result = &extensions.JobList{} + err = c.r.Get().Namespace(c.ns).Resource("jobs").VersionedParams(&opts, api.ParameterCodec).Do().Into(result) + return +} + +// Get returns information about a particular job. +func (c *jobsV1) Get(name string) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result) + return +} + +// Create creates a new job. +func (c *jobsV1) Create(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result) + return +} + +// Update updates an existing job. +func (c *jobsV1) Update(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).Body(job).Do().Into(result) + return +} + +// Delete deletes a job, returns error if one occurs. +func (c *jobsV1) Delete(name string, options *api.DeleteOptions) (err error) { + return c.r.Delete().Namespace(c.ns).Resource("jobs").Name(name).Body(options).Do().Error() +} + +// Watch returns a watch.Interface that watches the requested jobs. +func (c *jobsV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.r.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("jobs"). + VersionedParams(&opts, api.ParameterCodec). + Watch() +} + +// UpdateStatus takes the name of the job and the new status. Returns the server's representation of the job, and an error, if it occurs. +func (c *jobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result) + return +} diff --git a/pkg/client/unversioned/jobs_test.go b/pkg/client/unversioned/jobs_test.go index b65f5339c9b..1541648ed15 100644 --- a/pkg/client/unversioned/jobs_test.go +++ b/pkg/client/unversioned/jobs_test.go @@ -26,19 +26,32 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" ) -func getJobResourceName() string { +func getJobsResourceName() string { return "jobs" } -func TestListJobsViaExtensions(t *testing.T) { +func getJobClient(t *testing.T, c *simple.Client, ns, resourceGroup string) JobInterface { + switch resourceGroup { + case batch.GroupName: + return c.Setup(t).Batch().Jobs(ns) + case extensions.GroupName: + return c.Setup(t).Extensions().Jobs(ns) + default: + t.Fatalf("Unknown group %v", resourceGroup) + } + return nil +} + +func testListJob(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceAll c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""), + Path: group.ResourcePath(getJobsResourceName(), ns, ""), }, Response: simple.Response{StatusCode: 200, Body: &extensions.JobList{ @@ -58,18 +71,24 @@ func TestListJobsViaExtensions(t *testing.T) { }, }, }, + ResourceGroup: resourceGroup, } - receivedJobList, err := c.Setup(t).Extensions().Jobs(ns).List(api.ListOptions{}) + receivedJobList, err := getJobClient(t, c, ns, resourceGroup).List(api.ListOptions{}) defer c.Close() c.Validate(t, receivedJobList, err) } -func TestGetJobViaExtensions(t *testing.T) { +func TestListJob(t *testing.T) { + testListJob(t, testapi.Extensions, extensions.GroupName) + testListJob(t, testapi.Batch, batch.GroupName) +} + +func testGetJob(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), + Path: group.ResourcePath(getJobsResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil), }, Response: simple.Response{ @@ -87,25 +106,19 @@ func TestGetJobViaExtensions(t *testing.T) { }, }, }, + ResourceGroup: resourceGroup, } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("foo") + receivedJob, err := getJobClient(t, c, ns, resourceGroup).Get("foo") defer c.Close() c.Validate(t, receivedJob, err) } -func TestGetJobWithNoNameViaExtensions(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedJob, err) +func TestGetJob(t *testing.T) { + testGetJob(t, testapi.Extensions, extensions.GroupName) + testGetJob(t, testapi.Batch, batch.GroupName) } -func TestUpdateJobViaExtensions(t *testing.T) { +func testUpdateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault requestJob := &extensions.Job{ ObjectMeta: api.ObjectMeta{ @@ -117,7 +130,7 @@ func TestUpdateJobViaExtensions(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "PUT", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), + Path: group.ResourcePath(getJobsResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil), }, Response: simple.Response{ @@ -135,13 +148,19 @@ func TestUpdateJobViaExtensions(t *testing.T) { }, }, }, + ResourceGroup: resourceGroup, } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Update(requestJob) + receivedJob, err := getJobClient(t, c, ns, resourceGroup).Update(requestJob) defer c.Close() c.Validate(t, receivedJob, err) } -func TestUpdateJobStatusViaExtensions(t *testing.T) { +func TestUpdateJob(t *testing.T) { + testUpdateJob(t, testapi.Extensions, extensions.GroupName) + testUpdateJob(t, testapi.Batch, batch.GroupName) +} + +func testUpdateJobStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault requestJob := &extensions.Job{ ObjectMeta: api.ObjectMeta{ @@ -153,7 +172,7 @@ func TestUpdateJobStatusViaExtensions(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "PUT", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo") + "/status", + Path: group.ResourcePath(getJobsResourceName(), ns, "foo") + "/status", Query: simple.BuildQueryValues(nil), }, Response: simple.Response{ @@ -174,28 +193,40 @@ func TestUpdateJobStatusViaExtensions(t *testing.T) { }, }, }, + ResourceGroup: resourceGroup, } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).UpdateStatus(requestJob) + receivedJob, err := getJobClient(t, c, ns, resourceGroup).UpdateStatus(requestJob) defer c.Close() c.Validate(t, receivedJob, err) } -func TestDeleteJobViaExtensions(t *testing.T) { +func TestUpdateJobStatus(t *testing.T) { + testUpdateJobStatus(t, testapi.Extensions, extensions.GroupName) + testUpdateJobStatus(t, testapi.Batch, batch.GroupName) +} + +func testDeleteJob(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault c := &simple.Client{ Request: simple.Request{ Method: "DELETE", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), + Path: group.ResourcePath(getJobsResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil), }, - Response: simple.Response{StatusCode: 200}, + Response: simple.Response{StatusCode: 200}, + ResourceGroup: resourceGroup, } - err := c.Setup(t).Extensions().Jobs(ns).Delete("foo", nil) + err := getJobClient(t, c, ns, resourceGroup).Delete("foo", nil) defer c.Close() c.Validate(t, nil, err) } -func TestCreateJobViaExtensions(t *testing.T) { +func TestDeleteJob(t *testing.T) { + testDeleteJob(t, testapi.Extensions, extensions.GroupName) + testDeleteJob(t, testapi.Batch, batch.GroupName) +} + +func testCreateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) { ns := api.NamespaceDefault requestJob := &extensions.Job{ ObjectMeta: api.ObjectMeta{ @@ -206,7 +237,7 @@ func TestCreateJobViaExtensions(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "POST", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""), + Path: group.ResourcePath(getJobsResourceName(), ns, ""), Body: requestJob, Query: simple.BuildQueryValues(nil), }, @@ -225,208 +256,17 @@ func TestCreateJobViaExtensions(t *testing.T) { }, }, }, + ResourceGroup: resourceGroup, } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Create(requestJob) + receivedJob, err := getJobClient(t, c, ns, resourceGroup).Create(requestJob) defer c.Close() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } c.Validate(t, receivedJob, err) } -// Tests below are a copy of the above tests. Once job is removed from extensions, above test cases can be deleted. - -func TestListJobsViaBatch(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.JobList{ - Items: []extensions.Job{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - }, - }, - } - receivedJobList, err := c.Setup(t).Batch().Jobs(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedJobList, err) -} - -func TestGetJobViaBatch(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Batch().Jobs(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestGetJobWithNoNameViaBatch(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedJob, err := c.Setup(t).Batch().Jobs(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedJob, err) -} - -func TestUpdateJobViaBatch(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Batch().Jobs(ns).Update(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestUpdateJobStatusViaBatch(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, "foo") + "/status", - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - Status: extensions.JobStatus{ - Active: 1, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Batch().Jobs(ns).UpdateStatus(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestDeleteJobViaBatch(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Batch().Jobs(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateJobViaBatch(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Batch.ResourcePath(getJobResourceName(), ns, ""), - Body: requestJob, - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Batch().Jobs(ns).Create(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) +func TestCreateJob(t *testing.T) { + testCreateJob(t, testapi.Extensions, extensions.GroupName) + testCreateJob(t, testapi.Batch, batch.GroupName) } diff --git a/pkg/client/unversioned/testclient/fake_jobs.go b/pkg/client/unversioned/testclient/fake_jobs.go index d6fb79fa1c2..71ac8dfd65a 100644 --- a/pkg/client/unversioned/testclient/fake_jobs.go +++ b/pkg/client/unversioned/testclient/fake_jobs.go @@ -82,3 +82,66 @@ func (c *FakeJobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, er return obj.(*extensions.Job), err } + +// FakeJobs implements JobInterface. 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 JobsV1 +// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely +type FakeJobsV1 struct { + Fake *FakeBatch + Namespace string +} + +func (c *FakeJobsV1) Get(name string) (*extensions.Job, error) { + obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &extensions.Job{}) + if obj == nil { + return nil, err + } + + return obj.(*extensions.Job), err +} + +func (c *FakeJobsV1) List(opts api.ListOptions) (*extensions.JobList, error) { + obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &extensions.JobList{}) + if obj == nil { + return nil, err + } + + return obj.(*extensions.JobList), err +} + +func (c *FakeJobsV1) Create(job *extensions.Job) (*extensions.Job, error) { + obj, err := c.Fake.Invokes(NewCreateAction("jobs", c.Namespace, job), job) + if obj == nil { + return nil, err + } + + return obj.(*extensions.Job), err +} + +func (c *FakeJobsV1) Update(job *extensions.Job) (*extensions.Job, error) { + obj, err := c.Fake.Invokes(NewUpdateAction("jobs", c.Namespace, job), job) + if obj == nil { + return nil, err + } + + return obj.(*extensions.Job), err +} + +func (c *FakeJobsV1) Delete(name string, options *api.DeleteOptions) error { + _, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &extensions.Job{}) + return err +} + +func (c *FakeJobsV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.Fake.InvokesWatch(NewWatchAction("jobs", c.Namespace, opts)) +} + +func (c *FakeJobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) { + obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("jobs", "status", c.Namespace, job), job) + if obj == nil { + return nil, err + } + + return obj.(*extensions.Job), err +} diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index ab32f5295ee..187f4e21e81 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -92,6 +92,10 @@ func (c *Client) Setup(t *testing.T) *Client { Host: c.server.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Autoscaling.GroupVersion()}, }) + c.BatchClient = client.NewBatchOrDie(&client.Config{ + Host: c.server.URL, + ContentConfig: client.ContentConfig{GroupVersion: testapi.Batch.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 ef962e41743..c23f9bbacdf 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -278,6 +278,10 @@ func (c *Fake) Autoscaling() client.AutoscalingInterface { return &FakeAutoscaling{c} } +func (c *Fake) Batch() client.BatchInterface { + return &FakeBatch{c} +} + func (c *Fake) Extensions() client.ExtensionsInterface { return &FakeExperimental{c} } @@ -321,6 +325,19 @@ func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.Hori return &FakeHorizontalPodAutoscalersV1{Fake: c, Namespace: namespace} } +// NewSimpleFakeBatch returns a client that will respond with the provided objects +func NewSimpleFakeBatch(objects ...runtime.Object) *FakeBatch { + return &FakeBatch{Fake: NewSimpleFake(objects...)} +} + +type FakeBatch struct { + *Fake +} + +func (c *FakeBatch) Jobs(namespace string) client.JobInterface { + return &FakeJobsV1{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/drain.go b/pkg/kubectl/cmd/drain.go index 89cba686db3..1e955b5eb47 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -242,7 +242,7 @@ func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) { daemonset_pod = true } } else if sr.Reference.Kind == "Job" { - job, err := o.client.Jobs(sr.Reference.Namespace).Get(sr.Reference.Name) + job, err := o.client.ExtensionsClient.Jobs(sr.Reference.Namespace).Get(sr.Reference.Name) // Assume the only reason for an error is because the Job is // gone/missing, not for any other cause. TODO(mml): something more diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index ca6413b75d8..98f1ce7c976 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -221,6 +222,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return client.RESTClient, nil case autoscaling.GroupName: return client.AutoscalingClient.RESTClient, nil + case batch.GroupName: + return client.BatchClient.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } @@ -710,6 +713,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { } return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) } + if gvk.Group == batch.GroupName { + if c.c.BatchClient == nil { + return errors.New("unable to validate: no batch client") + } + return getSchemaAndValidate(c.c.BatchClient.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")