ScheduledJob kubectl changes

This commit is contained in:
Maciej Szulik 2016-05-18 10:11:35 +02:00
parent e6c327048e
commit b5c68a9015
5 changed files with 252 additions and 9 deletions

View File

@ -69,7 +69,10 @@ var (
kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN> kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
# Start the perl container to compute π to 2000 places and print it out. # Start the perl container to compute π to 2000 places and print it out.
kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'`) kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'
# Start the scheduled job to compute π to 2000 places and print it out every 5 minutes.
kubectl run pi --schedule="* 0/5 * * * ?" --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'`)
) )
func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
@ -118,6 +121,7 @@ func addRunFlags(cmd *cobra.Command) {
cmd.Flags().String("service-generator", "service/v2", "The name of the generator to use for creating a service. Only used if --expose is true") cmd.Flags().String("service-generator", "service/v2", "The name of the generator to use for creating a service. Only used if --expose is true")
cmd.Flags().String("service-overrides", "", "An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true.") cmd.Flags().String("service-overrides", "", "An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true.")
cmd.Flags().Bool("quiet", false, "If true, suppress prompt messages.") cmd.Flags().Bool("quiet", false, "If true, suppress prompt messages.")
cmd.Flags().String("schedule", "", "A schedule in the Cron format the job should be run with.")
} }
func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error { func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error {
@ -154,6 +158,10 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
} }
generatorName := cmdutil.GetFlagString(cmd, "generator") generatorName := cmdutil.GetFlagString(cmd, "generator")
schedule := cmdutil.GetFlagString(cmd, "schedule")
if len(schedule) != 0 && len(generatorName) == 0 {
generatorName = "scheduledjob/v2alpha1"
}
if len(generatorName) == 0 { if len(generatorName) == 0 {
client, err := f.Client() client, err := f.Client()
if err != nil { if err != nil {

View File

@ -164,6 +164,7 @@ const (
DeploymentV1Beta1GeneratorName = "deployment/v1beta1" DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
JobV1Beta1GeneratorName = "job/v1beta1" JobV1Beta1GeneratorName = "job/v1beta1"
JobV1GeneratorName = "job/v1" JobV1GeneratorName = "job/v1"
ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
NamespaceV1GeneratorName = "namespace/v1" NamespaceV1GeneratorName = "namespace/v1"
ResourceQuotaV1GeneratorName = "resourcequotas/v1" ResourceQuotaV1GeneratorName = "resourcequotas/v1"
SecretV1GeneratorName = "secret/v1" SecretV1GeneratorName = "secret/v1"
@ -180,11 +181,12 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{}, ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
} }
generators["run"] = map[string]kubectl.Generator{ generators["run"] = map[string]kubectl.Generator{
RunV1GeneratorName: kubectl.BasicReplicationController{}, RunV1GeneratorName: kubectl.BasicReplicationController{},
RunPodV1GeneratorName: kubectl.BasicPod{}, RunPodV1GeneratorName: kubectl.BasicPod{},
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{}, DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{}, JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
JobV1GeneratorName: kubectl.JobV1{}, JobV1GeneratorName: kubectl.JobV1{},
ScheduledJobV2Alpha1GeneratorName: kubectl.ScheduledJobV2Alpha1{},
} }
generators["autoscale"] = map[string]kubectl.Generator{ generators["autoscale"] = map[string]kubectl.Generator{
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{}, HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},

View File

@ -109,6 +109,7 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)}, extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)},
extensions.Kind("Job"): &JobDescriber{c}, extensions.Kind("Job"): &JobDescriber{c},
batch.Kind("Job"): &JobDescriber{c}, batch.Kind("Job"): &JobDescriber{c},
batch.Kind("ScheduledJob"): &ScheduledJobDescriber{adapter.FromUnversionedClient(c)},
apps.Kind("PetSet"): &PetSetDescriber{c}, apps.Kind("PetSet"): &PetSetDescriber{c},
extensions.Kind("Ingress"): &IngressDescriber{c}, extensions.Kind("Ingress"): &IngressDescriber{c},
} }
@ -1019,6 +1020,14 @@ func describeStatus(stateName string, state api.ContainerState, out io.Writer) {
} }
} }
func printBoolPtr(value *bool) string {
if value != nil {
return printBool(*value)
}
return "<unset>"
}
func printBool(value bool) string { func printBool(value bool) string {
if value { if value {
return "True" return "True"
@ -1148,18 +1157,18 @@ func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, runnin
// JobDescriber generates information about a job and the pods it has created. // JobDescriber generates information about a job and the pods it has created.
type JobDescriber struct { type JobDescriber struct {
client *client.Client client.Interface
} }
func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
job, err := d.client.Extensions().Jobs(namespace).Get(name) job, err := d.Batch().Jobs(namespace).Get(name)
if err != nil { if err != nil {
return "", err return "", err
} }
var events *api.EventList var events *api.EventList
if describerSettings.ShowEvents { if describerSettings.ShowEvents {
events, _ = d.client.Events(namespace).Search(job) events, _ = d.Events(namespace).Search(job)
} }
return describeJob(job, events) return describeJob(job, events)
@ -1194,6 +1203,92 @@ func describeJob(job *batch.Job, events *api.EventList) (string, error) {
}) })
} }
// ScheduledJobDescriber generates information about a scheduled job and the jobs it has created.
type ScheduledJobDescriber struct {
clientset.Interface
}
func (d *ScheduledJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
scheduledJob, err := d.Batch().ScheduledJobs(namespace).Get(name)
if err != nil {
return "", err
}
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Core().Events(namespace).Search(scheduledJob)
}
return describeScheduledJob(scheduledJob, events)
}
func describeScheduledJob(scheduledJob *batch.ScheduledJob, events *api.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", scheduledJob.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", scheduledJob.Namespace)
fmt.Fprintf(out, "Schedule:\t%s\n", scheduledJob.Spec.Schedule)
fmt.Fprintf(out, "Concurrency Policy:\t%s\n", scheduledJob.Spec.ConcurrencyPolicy)
fmt.Fprintf(out, "Suspend:\t%s\n", printBoolPtr(scheduledJob.Spec.Suspend))
if scheduledJob.Spec.StartingDeadlineSeconds != nil {
fmt.Fprintf(out, "Starting Deadline Seconds:\t%ds\n", *scheduledJob.Spec.StartingDeadlineSeconds)
} else {
fmt.Fprintf(out, "Starting Deadline Seconds:\t<unset>\n")
}
describeJobTemplate(scheduledJob.Spec.JobTemplate, out)
printLabelsMultiline(out, "Labels", scheduledJob.Labels)
if scheduledJob.Status.LastScheduleTime != nil {
fmt.Fprintf(out, "Last Schedule Time:\t%s\n", scheduledJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z))
} else {
fmt.Fprintf(out, "Last Schedule Time:\t<unset>\n")
}
printActiveJobs(out, "Active Jobs", scheduledJob.Status.Active)
if events != nil {
DescribeEvents(events, out)
}
return nil
})
}
func describeJobTemplate(jobTemplate batch.JobTemplateSpec, out io.Writer) {
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&jobTemplate.Spec.Template.Spec))
if jobTemplate.Spec.Selector != nil {
selector, _ := unversioned.LabelSelectorAsSelector(jobTemplate.Spec.Selector)
fmt.Fprintf(out, "Selector:\t%s\n", selector)
} else {
fmt.Fprintf(out, "Selector:\t<unset>\n")
}
if jobTemplate.Spec.Parallelism != nil {
fmt.Fprintf(out, "Parallelism:\t%d\n", *jobTemplate.Spec.Parallelism)
} else {
fmt.Fprintf(out, "Parallelism:\t<unset>\n")
}
if jobTemplate.Spec.Completions != nil {
fmt.Fprintf(out, "Completions:\t%d\n", *jobTemplate.Spec.Completions)
} else {
fmt.Fprintf(out, "Completions:\t<unset>\n")
}
if jobTemplate.Spec.ActiveDeadlineSeconds != nil {
fmt.Fprintf(out, "Active Deadline Seconds:\t%ds\n", *jobTemplate.Spec.ActiveDeadlineSeconds)
}
describeVolumes(jobTemplate.Spec.Template.Spec.Volumes, out, "")
}
func printActiveJobs(out io.Writer, title string, jobs []api.ObjectReference) {
fmt.Fprintf(out, "%s:\t", title)
if len(jobs) == 0 {
fmt.Fprintln(out, "<none>")
return
}
for i, job := range jobs {
if i != 0 {
fmt.Fprint(out, ", ")
}
fmt.Fprintf(out, "%s", job.Name)
}
fmt.Fprintln(out, "")
}
// DaemonSetDescriber generates information about a daemon set and the pods it has created. // DaemonSetDescriber generates information about a daemon set and the pods it has created.
type DaemonSetDescriber struct { type DaemonSetDescriber struct {
client.Interface client.Interface

View File

@ -414,6 +414,7 @@ var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLA
var replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"} var replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"} var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"} var jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
var scheduledJobColumns = []string{"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST-SCHEDULE"}
var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"} var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"}
var ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"} var ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"}
var petSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"} var petSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
@ -459,6 +460,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(daemonSetColumns, printDaemonSetList) h.Handler(daemonSetColumns, printDaemonSetList)
h.Handler(jobColumns, printJob) h.Handler(jobColumns, printJob)
h.Handler(jobColumns, printJobList) h.Handler(jobColumns, printJobList)
h.Handler(scheduledJobColumns, printScheduledJob)
h.Handler(scheduledJobColumns, printScheduledJobList)
h.Handler(serviceColumns, printService) h.Handler(serviceColumns, printService)
h.Handler(serviceColumns, printServiceList) h.Handler(serviceColumns, printServiceList)
h.Handler(ingressColumns, printIngress) h.Handler(ingressColumns, printIngress)
@ -970,6 +973,42 @@ func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error
return nil return nil
} }
func printScheduledJob(scheduledJob *batch.ScheduledJob, w io.Writer, options PrintOptions) error {
name := scheduledJob.Name
namespace := scheduledJob.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
}
}
lastScheduleTime := "<none>"
if scheduledJob.Status.LastScheduleTime != nil {
lastScheduleTime = scheduledJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z)
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\n",
name,
scheduledJob.Spec.Schedule,
printBoolPtr(scheduledJob.Spec.Suspend),
len(scheduledJob.Status.Active),
lastScheduleTime,
); err != nil {
return err
}
return nil
}
func printScheduledJobList(list *batch.ScheduledJobList, w io.Writer, options PrintOptions) error {
for _, scheduledJob := range list.Items {
if err := printScheduledJob(&scheduledJob, w, options); err != nil {
return err
}
}
return nil
}
// loadBalancerStatusStringer behaves mostly like a string interface and converts the given status to a string. // loadBalancerStatusStringer behaves mostly like a string interface and converts the given status to a string.
// `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes. // `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
func loadBalancerStatusStringer(s api.LoadBalancerStatus, wide bool) string { func loadBalancerStatusStringer(s api.LoadBalancerStatus, wide bool) string {

View File

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1" batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation" "k8s.io/kubernetes/pkg/util/validation"
@ -394,6 +395,104 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return &job, nil return &job, nil
} }
type ScheduledJobV2Alpha1 struct{}
func (ScheduledJobV2Alpha1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"labels", false},
{"default-name", false},
{"name", true},
{"image", true},
{"port", false},
{"hostport", false},
{"stdin", false},
{"leave-stdin-open", false},
{"tty", false},
{"command", false},
{"args", false},
{"env", false},
{"requests", false},
{"limits", false},
{"restart", false},
{"schedule", true},
}
}
func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args, err := getArgs(genericParams)
if err != nil {
return nil, err
}
envs, err := getV1Envs(genericParams)
if err != nil {
return nil, err
}
params, err := getParams(genericParams)
if err != nil {
return nil, err
}
name, err := getName(params)
if err != nil {
return nil, err
}
labels, err := getLabels(params, true, name)
if err != nil {
return nil, err
}
podSpec, err := makeV1PodSpec(params, name)
if err != nil {
return nil, err
}
if err = updateV1PodContainers(params, args, envs, podSpec); err != nil {
return nil, err
}
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
if err != nil {
return nil, err
}
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
if err := updateV1PodPorts(params, podSpec); err != nil {
return nil, err
}
restartPolicy := v1.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = v1.RestartPolicyNever
}
podSpec.RestartPolicy = restartPolicy
scheduledJob := batchv2alpha1.ScheduledJob{
ObjectMeta: v1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: batchv2alpha1.ScheduledJobSpec{
Schedule: params["schedule"],
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
JobTemplate: batchv2alpha1.JobTemplateSpec{
Spec: batchv2alpha1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Labels: labels,
},
Spec: *podSpec,
},
},
},
},
}
return &scheduledJob, nil
}
type BasicReplicationController struct{} type BasicReplicationController struct{}
func (BasicReplicationController) ParamNames() []GeneratorParam { func (BasicReplicationController) ParamNames() []GeneratorParam {