Merge pull request #13254 from soltysh/job_controller_kubectl
Job resource - kubectl part
This commit is contained in:
		| @@ -291,6 +291,7 @@ _kubectl_get() | |||||||
|     must_have_one_noun+=("endpoints") |     must_have_one_noun+=("endpoints") | ||||||
|     must_have_one_noun+=("event") |     must_have_one_noun+=("event") | ||||||
|     must_have_one_noun+=("horizontalpodautoscaler") |     must_have_one_noun+=("horizontalpodautoscaler") | ||||||
|  |     must_have_one_noun+=("job") | ||||||
|     must_have_one_noun+=("limitrange") |     must_have_one_noun+=("limitrange") | ||||||
|     must_have_one_noun+=("namespace") |     must_have_one_noun+=("namespace") | ||||||
|     must_have_one_noun+=("node") |     must_have_one_noun+=("node") | ||||||
| @@ -459,6 +460,7 @@ _kubectl_delete() | |||||||
|     must_have_one_noun+=("endpoints") |     must_have_one_noun+=("endpoints") | ||||||
|     must_have_one_noun+=("event") |     must_have_one_noun+=("event") | ||||||
|     must_have_one_noun+=("horizontalpodautoscaler") |     must_have_one_noun+=("horizontalpodautoscaler") | ||||||
|  |     must_have_one_noun+=("job") | ||||||
|     must_have_one_noun+=("limitrange") |     must_have_one_noun+=("limitrange") | ||||||
|     must_have_one_noun+=("namespace") |     must_have_one_noun+=("namespace") | ||||||
|     must_have_one_noun+=("node") |     must_have_one_noun+=("node") | ||||||
| @@ -829,6 +831,7 @@ _kubectl_label() | |||||||
|     must_have_one_noun+=("endpoints") |     must_have_one_noun+=("endpoints") | ||||||
|     must_have_one_noun+=("event") |     must_have_one_noun+=("event") | ||||||
|     must_have_one_noun+=("horizontalpodautoscaler") |     must_have_one_noun+=("horizontalpodautoscaler") | ||||||
|  |     must_have_one_noun+=("job") | ||||||
|     must_have_one_noun+=("limitrange") |     must_have_one_noun+=("limitrange") | ||||||
|     must_have_one_noun+=("namespace") |     must_have_one_noun+=("namespace") | ||||||
|     must_have_one_noun+=("node") |     must_have_one_noun+=("node") | ||||||
|   | |||||||
| @@ -36,6 +36,7 @@ type ExperimentalInterface interface { | |||||||
| 	ScaleNamespacer | 	ScaleNamespacer | ||||||
| 	DaemonSetsNamespacer | 	DaemonSetsNamespacer | ||||||
| 	DeploymentsNamespacer | 	DeploymentsNamespacer | ||||||
|  | 	JobsNamespacer | ||||||
| } | } | ||||||
|  |  | ||||||
| // ExperimentalClient is used to interact with experimental Kubernetes features. | // ExperimentalClient is used to interact with experimental Kubernetes features. | ||||||
| @@ -90,6 +91,10 @@ func (c *ExperimentalClient) Deployments(namespace string) DeploymentInterface { | |||||||
| 	return newDeployments(c, namespace) | 	return newDeployments(c, namespace) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *ExperimentalClient) Jobs(namespace string) JobInterface { | ||||||
|  | 	return newJobs(c, namespace) | ||||||
|  | } | ||||||
|  |  | ||||||
| // NewExperimental creates a new ExperimentalClient for the given config. This client | // NewExperimental creates a new ExperimentalClient for the given config. This client | ||||||
| // provides access to experimental Kubernetes features. | // provides access to experimental Kubernetes features. | ||||||
| // Experimental features are not supported and may be changed or removed in | // Experimental features are not supported and may be changed or removed in | ||||||
|   | |||||||
							
								
								
									
										112
									
								
								pkg/client/unversioned/jobs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								pkg/client/unversioned/jobs.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2015 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/apis/experimental" | ||||||
|  | 	"k8s.io/kubernetes/pkg/fields" | ||||||
|  | 	"k8s.io/kubernetes/pkg/labels" | ||||||
|  | 	"k8s.io/kubernetes/pkg/watch" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // JobsNamespacer has methods to work with Job resources in a namespace | ||||||
|  | type JobsNamespacer interface { | ||||||
|  | 	Jobs(namespace string) JobInterface | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // JobInterface exposes methods to work on Job resources. | ||||||
|  | type JobInterface interface { | ||||||
|  | 	List(label labels.Selector, field fields.Selector) (*experimental.JobList, error) | ||||||
|  | 	Get(name string) (*experimental.Job, error) | ||||||
|  | 	Create(job *experimental.Job) (*experimental.Job, error) | ||||||
|  | 	Update(job *experimental.Job) (*experimental.Job, error) | ||||||
|  | 	Delete(name string, options *api.DeleteOptions) error | ||||||
|  | 	Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) | ||||||
|  | 	UpdateStatus(job *experimental.Job) (*experimental.Job, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // jobs implements JobsNamespacer interface | ||||||
|  | type jobs struct { | ||||||
|  | 	r  *ExperimentalClient | ||||||
|  | 	ns string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // newJobs returns a jobs | ||||||
|  | func newJobs(c *ExperimentalClient, namespace string) *jobs { | ||||||
|  | 	return &jobs{c, namespace} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List returns a list of jobs that match the label and field selectors. | ||||||
|  | func (c *jobs) List(label labels.Selector, field fields.Selector) (result *experimental.JobList, err error) { | ||||||
|  | 	result = &experimental.JobList{} | ||||||
|  | 	err = c.r.Get().Namespace(c.ns).Resource("jobs").LabelsSelectorParam(label).FieldsSelectorParam(field).Do().Into(result) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get returns information about a particular job. | ||||||
|  | func (c *jobs) Get(name string) (result *experimental.Job, err error) { | ||||||
|  | 	result = &experimental.Job{} | ||||||
|  | 	err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create creates a new job. | ||||||
|  | func (c *jobs) Create(job *experimental.Job) (result *experimental.Job, err error) { | ||||||
|  | 	result = &experimental.Job{} | ||||||
|  | 	err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Update updates an existing job. | ||||||
|  | func (c *jobs) Update(job *experimental.Job) (result *experimental.Job, err error) { | ||||||
|  | 	result = &experimental.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 *jobs) Delete(name string, options *api.DeleteOptions) (err error) { | ||||||
|  | 	if options == nil { | ||||||
|  | 		return c.r.Delete().Namespace(c.ns).Resource("jobs").Name(name).Do().Error() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	body, err := api.Scheme.EncodeToVersion(options, c.r.APIVersion()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return c.r.Delete().Namespace(c.ns).Resource("jobs").Name(name).Body(body).Do().Error() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Watch returns a watch.Interface that watches the requested jobs. | ||||||
|  | func (c *jobs) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) { | ||||||
|  | 	return c.r.Get(). | ||||||
|  | 		Prefix("watch"). | ||||||
|  | 		Namespace(c.ns). | ||||||
|  | 		Resource("jobs"). | ||||||
|  | 		Param("resourceVersion", resourceVersion). | ||||||
|  | 		LabelsSelectorParam(label). | ||||||
|  | 		FieldsSelectorParam(field). | ||||||
|  | 		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 *jobs) UpdateStatus(job *experimental.Job) (result *experimental.Job, err error) { | ||||||
|  | 	result = &experimental.Job{} | ||||||
|  | 	err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result) | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										222
									
								
								pkg/client/unversioned/jobs_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								pkg/client/unversioned/jobs_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2015 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 ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/testapi" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/experimental" | ||||||
|  | 	"k8s.io/kubernetes/pkg/fields" | ||||||
|  | 	"k8s.io/kubernetes/pkg/labels" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func getJobResourceName() string { | ||||||
|  | 	return "jobs" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestListJobs(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceAll | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "GET", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, ""), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{StatusCode: 200, | ||||||
|  | 			Body: &experimental.JobList{ | ||||||
|  | 				Items: []experimental.Job{ | ||||||
|  | 					{ | ||||||
|  | 						ObjectMeta: api.ObjectMeta{ | ||||||
|  | 							Name: "foo", | ||||||
|  | 							Labels: map[string]string{ | ||||||
|  | 								"foo":  "bar", | ||||||
|  | 								"name": "baz", | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						Spec: experimental.JobSpec{ | ||||||
|  | 							Template: &api.PodTemplateSpec{}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	receivedJobList, err := c.Setup(t).Experimental().Jobs(ns).List(labels.Everything(), fields.Everything()) | ||||||
|  | 	c.Validate(t, receivedJobList, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetJob(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "GET", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, "foo"), | ||||||
|  | 			Query:  buildQueryValues(nil), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{ | ||||||
|  | 			StatusCode: 200, | ||||||
|  | 			Body: &experimental.Job{ | ||||||
|  | 				ObjectMeta: api.ObjectMeta{ | ||||||
|  | 					Name: "foo", | ||||||
|  | 					Labels: map[string]string{ | ||||||
|  | 						"foo":  "bar", | ||||||
|  | 						"name": "baz", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Spec: experimental.JobSpec{ | ||||||
|  | 					Template: &api.PodTemplateSpec{}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	receivedJob, err := c.Setup(t).Experimental().Jobs(ns).Get("foo") | ||||||
|  | 	c.Validate(t, receivedJob, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetJobWithNoName(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	c := &testClient{Error: true} | ||||||
|  | 	receivedJob, err := c.Setup(t).Experimental().Jobs(ns).Get("") | ||||||
|  | 	if (err != nil) && (err.Error() != nameRequiredError) { | ||||||
|  | 		t.Errorf("Expected error: %v, but got %v", nameRequiredError, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.Validate(t, receivedJob, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateJob(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	requestJob := &experimental.Job{ | ||||||
|  | 		ObjectMeta: api.ObjectMeta{ | ||||||
|  | 			Name:            "foo", | ||||||
|  | 			Namespace:       ns, | ||||||
|  | 			ResourceVersion: "1", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "PUT", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, "foo"), | ||||||
|  | 			Query:  buildQueryValues(nil), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{ | ||||||
|  | 			StatusCode: 200, | ||||||
|  | 			Body: &experimental.Job{ | ||||||
|  | 				ObjectMeta: api.ObjectMeta{ | ||||||
|  | 					Name: "foo", | ||||||
|  | 					Labels: map[string]string{ | ||||||
|  | 						"foo":  "bar", | ||||||
|  | 						"name": "baz", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Spec: experimental.JobSpec{ | ||||||
|  | 					Template: &api.PodTemplateSpec{}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	receivedJob, err := c.Setup(t).Experimental().Jobs(ns).Update(requestJob) | ||||||
|  | 	c.Validate(t, receivedJob, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateJobStatus(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	requestJob := &experimental.Job{ | ||||||
|  | 		ObjectMeta: api.ObjectMeta{ | ||||||
|  | 			Name:            "foo", | ||||||
|  | 			Namespace:       ns, | ||||||
|  | 			ResourceVersion: "1", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "PUT", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, "foo") + "/status", | ||||||
|  | 			Query:  buildQueryValues(nil), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{ | ||||||
|  | 			StatusCode: 200, | ||||||
|  | 			Body: &experimental.Job{ | ||||||
|  | 				ObjectMeta: api.ObjectMeta{ | ||||||
|  | 					Name: "foo", | ||||||
|  | 					Labels: map[string]string{ | ||||||
|  | 						"foo":  "bar", | ||||||
|  | 						"name": "baz", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Spec: experimental.JobSpec{ | ||||||
|  | 					Template: &api.PodTemplateSpec{}, | ||||||
|  | 				}, | ||||||
|  | 				Status: experimental.JobStatus{ | ||||||
|  | 					Active: 1, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	receivedJob, err := c.Setup(t).Experimental().Jobs(ns).UpdateStatus(requestJob) | ||||||
|  | 	c.Validate(t, receivedJob, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDeleteJob(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "DELETE", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, "foo"), | ||||||
|  | 			Query:  buildQueryValues(nil), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{StatusCode: 200}, | ||||||
|  | 	} | ||||||
|  | 	err := c.Setup(t).Experimental().Jobs(ns).Delete("foo", nil) | ||||||
|  | 	c.Validate(t, nil, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestCreateJob(t *testing.T) { | ||||||
|  | 	ns := api.NamespaceDefault | ||||||
|  | 	requestJob := &experimental.Job{ | ||||||
|  | 		ObjectMeta: api.ObjectMeta{ | ||||||
|  | 			Name:      "foo", | ||||||
|  | 			Namespace: ns, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	c := &testClient{ | ||||||
|  | 		Request: testRequest{ | ||||||
|  | 			Method: "POST", | ||||||
|  | 			Path:   testapi.Experimental.ResourcePath(getJobResourceName(), ns, ""), | ||||||
|  | 			Body:   requestJob, | ||||||
|  | 			Query:  buildQueryValues(nil), | ||||||
|  | 		}, | ||||||
|  | 		Response: Response{ | ||||||
|  | 			StatusCode: 200, | ||||||
|  | 			Body: &experimental.Job{ | ||||||
|  | 				ObjectMeta: api.ObjectMeta{ | ||||||
|  | 					Name: "foo", | ||||||
|  | 					Labels: map[string]string{ | ||||||
|  | 						"foo":  "bar", | ||||||
|  | 						"name": "baz", | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Spec: experimental.JobSpec{ | ||||||
|  | 					Template: &api.PodTemplateSpec{}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	receivedJob, err := c.Setup(t).Experimental().Jobs(ns).Create(requestJob) | ||||||
|  | 	c.Validate(t, receivedJob, err) | ||||||
|  | } | ||||||
| @@ -261,3 +261,7 @@ func (c *FakeExperimental) Deployments(namespace string) client.DeploymentInterf | |||||||
| func (c *FakeExperimental) Scales(namespace string) client.ScaleInterface { | func (c *FakeExperimental) Scales(namespace string) client.ScaleInterface { | ||||||
| 	return &FakeScales{Fake: c, Namespace: namespace} | 	return &FakeScales{Fake: c, Namespace: namespace} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *FakeExperimental) Jobs(namespace string) client.JobInterface { | ||||||
|  | 	panic("unimplemented") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ import ( | |||||||
| 	"github.com/golang/glog" | 	"github.com/golang/glog" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/resource" | 	"k8s.io/kubernetes/pkg/api/resource" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/experimental" | ||||||
| 	client "k8s.io/kubernetes/pkg/client/unversioned" | 	client "k8s.io/kubernetes/pkg/client/unversioned" | ||||||
| 	"k8s.io/kubernetes/pkg/fieldpath" | 	"k8s.io/kubernetes/pkg/fieldpath" | ||||||
| 	"k8s.io/kubernetes/pkg/fields" | 	"k8s.io/kubernetes/pkg/fields" | ||||||
| @@ -85,6 +86,7 @@ func describerMap(c *client.Client) map[string]Describer { | |||||||
| func expDescriberMap(c *client.Client) map[string]Describer { | func expDescriberMap(c *client.Client) map[string]Describer { | ||||||
| 	return map[string]Describer{ | 	return map[string]Describer{ | ||||||
| 		"HorizontalPodAutoscaler": &HorizontalPodAutoscalerDescriber{c}, | 		"HorizontalPodAutoscaler": &HorizontalPodAutoscalerDescriber{c}, | ||||||
|  | 		"Job": &JobDescriber{c}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -854,6 +856,46 @@ func describeReplicationController(controller *api.ReplicationController, events | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // JobDescriber generates information about a job and the pods it has created. | ||||||
|  | type JobDescriber struct { | ||||||
|  | 	client *client.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *JobDescriber) Describe(namespace, name string) (string, error) { | ||||||
|  | 	job, err := d.client.Experimental().Jobs(namespace).Get(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	events, _ := d.client.Events(namespace).Search(job) | ||||||
|  |  | ||||||
|  | 	return describeJob(job, events) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func describeJob(job *experimental.Job, events *api.EventList) (string, error) { | ||||||
|  | 	return tabbedString(func(out io.Writer) error { | ||||||
|  | 		fmt.Fprintf(out, "Name:\t%s\n", job.Name) | ||||||
|  | 		fmt.Fprintf(out, "Namespace:\t%s\n", job.Namespace) | ||||||
|  | 		if job.Spec.Template != nil { | ||||||
|  | 			fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&job.Spec.Template.Spec)) | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Fprintf(out, "Image(s):\t%s\n", "<no template>") | ||||||
|  | 		} | ||||||
|  | 		fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(job.Spec.Selector)) | ||||||
|  | 		fmt.Fprintf(out, "Parallelism:\t%d\n", job.Spec.Parallelism) | ||||||
|  | 		fmt.Fprintf(out, "Completions:\t%d\n", job.Spec.Completions) | ||||||
|  | 		fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(job.Labels)) | ||||||
|  | 		fmt.Fprintf(out, "Pods Statuses:\t%d Running / %d Succeeded / %d Failed\n", job.Status.Active, job.Status.Successful, job.Status.Unsuccessful) | ||||||
|  | 		if job.Spec.Template != nil { | ||||||
|  | 			describeVolumes(job.Spec.Template.Spec.Volumes, out) | ||||||
|  | 		} | ||||||
|  | 		if events != nil { | ||||||
|  | 			DescribeEvents(events, out) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| // SecretDescriber generates information about a secret | // SecretDescriber generates information about a secret | ||||||
| type SecretDescriber struct { | type SecretDescriber struct { | ||||||
| 	client.Interface | 	client.Interface | ||||||
|   | |||||||
| @@ -380,6 +380,7 @@ func (h *HumanReadablePrinter) HandledResources() []string { | |||||||
| var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"} | var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"} | ||||||
| var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"} | var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"} | ||||||
| var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS", "AGE"} | var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS", "AGE"} | ||||||
|  | var jobColumns = []string{"JOB", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "SUCCESSFUL"} | ||||||
| var serviceColumns = []string{"NAME", "CLUSTER_IP", "EXTERNAL_IP", "PORT(S)", "SELECTOR", "AGE"} | var serviceColumns = []string{"NAME", "CLUSTER_IP", "EXTERNAL_IP", "PORT(S)", "SELECTOR", "AGE"} | ||||||
| var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"} | var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"} | ||||||
| var nodeColumns = []string{"NAME", "LABELS", "STATUS", "AGE"} | var nodeColumns = []string{"NAME", "LABELS", "STATUS", "AGE"} | ||||||
| @@ -405,6 +406,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { | |||||||
| 	h.Handler(podTemplateColumns, printPodTemplateList) | 	h.Handler(podTemplateColumns, printPodTemplateList) | ||||||
| 	h.Handler(replicationControllerColumns, printReplicationController) | 	h.Handler(replicationControllerColumns, printReplicationController) | ||||||
| 	h.Handler(replicationControllerColumns, printReplicationControllerList) | 	h.Handler(replicationControllerColumns, printReplicationControllerList) | ||||||
|  | 	h.Handler(jobColumns, printJob) | ||||||
|  | 	h.Handler(jobColumns, printJobList) | ||||||
| 	h.Handler(serviceColumns, printService) | 	h.Handler(serviceColumns, printService) | ||||||
| 	h.Handler(serviceColumns, printServiceList) | 	h.Handler(serviceColumns, printServiceList) | ||||||
| 	h.Handler(endpointColumns, printEndpoints) | 	h.Handler(endpointColumns, printEndpoints) | ||||||
| @@ -655,7 +658,6 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, withNamespa | |||||||
| func printReplicationController(controller *api.ReplicationController, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { | func printReplicationController(controller *api.ReplicationController, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { | ||||||
| 	name := controller.Name | 	name := controller.Name | ||||||
| 	namespace := controller.Namespace | 	namespace := controller.Namespace | ||||||
|  |  | ||||||
| 	containers := controller.Spec.Template.Spec.Containers | 	containers := controller.Spec.Template.Spec.Containers | ||||||
| 	var firstContainer api.Container | 	var firstContainer api.Container | ||||||
| 	if len(containers) > 0 { | 	if len(containers) > 0 { | ||||||
| @@ -707,6 +709,33 @@ func printReplicationControllerList(list *api.ReplicationControllerList, w io.Wr | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func printJob(job *experimental.Job, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { | ||||||
|  | 	containers := job.Spec.Template.Spec.Containers | ||||||
|  | 	var firstContainer api.Container | ||||||
|  | 	if len(containers) > 0 { | ||||||
|  | 		firstContainer = containers[0] | ||||||
|  | 	} | ||||||
|  | 	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n", | ||||||
|  | 		job.Name, | ||||||
|  | 		firstContainer.Name, | ||||||
|  | 		firstContainer.Image, | ||||||
|  | 		labels.FormatLabels(job.Spec.Selector), | ||||||
|  | 		job.Status.Successful) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func printJobList(list *experimental.JobList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error { | ||||||
|  | 	for _, job := range list.Items { | ||||||
|  | 		if err := printJob(&job, w, withNamespace, wide, showAll, columnLabels); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func getServiceExternalIP(svc *api.Service) string { | func getServiceExternalIP(svc *api.Service) string { | ||||||
| 	switch svc.Spec.Type { | 	switch svc.Spec.Type { | ||||||
| 	case api.ServiceTypeClusterIP: | 	case api.ServiceTypeClusterIP: | ||||||
|   | |||||||
| @@ -14,6 +14,6 @@ See the License for the specific language governing permissions and | |||||||
| limitations under the License. | limitations under the License. | ||||||
| */ | */ | ||||||
|  |  | ||||||
| // Package controller provides Registry interface and it's RESTStorage | // Package job provides Registry interface and it's RESTStorage | ||||||
| // implementation for storing Job api objects. | // implementation for storing Job api objects. | ||||||
| package job | package job | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/storage" | 	"k8s.io/kubernetes/pkg/storage" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // rest implements a RESTStorage for jobs against etcd | // REST implements a RESTStorage for jobs against etcd | ||||||
| type REST struct { | type REST struct { | ||||||
| 	*etcdgeneric.Etcd | 	*etcdgeneric.Etcd | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jeff Lowdermilk
					Jeff Lowdermilk