mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
Merge pull request #30420 from janetkuo/sj-job-determi
Automatic merge from submit-queue Name jobs created by sj deterministically ```release-note Name the job created by scheduledjob (sj) deterministically with sj's name and a hash of job's scheduled time. ``` @erictune @soltysh <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.kubernetes.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.kubernetes.io/reviews/kubernetes/kubernetes/30420) <!-- Reviewable:end -->
This commit is contained in:
commit
4a5c852697
@ -209,7 +209,7 @@ func SyncOne(sj batch.ScheduledJob, js []batch.Job, now time.Time, jc jobControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobReq, err := getJobFromTemplate(&sj)
|
jobReq, err := getJobFromTemplate(&sj, scheduledTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Unable to make Job from template in %s: %v", nameForLog, err)
|
glog.Errorf("Unable to make Job from template in %s: %v", nameForLog, err)
|
||||||
return
|
return
|
||||||
@ -228,8 +228,8 @@ func SyncOne(sj batch.ScheduledJob, js []batch.Job, now time.Time, jc jobControl
|
|||||||
// the next time. Actually, if we relist the SJs and Jobs on the next
|
// the next time. Actually, if we relist the SJs and Jobs on the next
|
||||||
// iteration of SyncAll, we might not see our own status update, and
|
// iteration of SyncAll, we might not see our own status update, and
|
||||||
// then post one again. So, we need to use the job name as a lock to
|
// then post one again. So, we need to use the job name as a lock to
|
||||||
// prevent us from making the job twice. TODO: name the job
|
// prevent us from making the job twice (name the job with hash of its
|
||||||
// deterministically (via hash of its scheduled time).
|
// scheduled time).
|
||||||
|
|
||||||
// Add the just-started job to the status list.
|
// Add the just-started job to the status list.
|
||||||
ref, err := getRef(jobResp)
|
ref, err := getRef(jobResp)
|
||||||
|
@ -19,6 +19,7 @@ package scheduledjob
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/adler32"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@ -29,6 +30,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
hashutil "k8s.io/kubernetes/pkg/util/hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Utilities for dealing with Jobs and ScheduledJobs and time.
|
// Utilities for dealing with Jobs and ScheduledJobs and time.
|
||||||
@ -186,7 +188,7 @@ func addSeconds(schedule string) string {
|
|||||||
// XXX unit test this
|
// XXX unit test this
|
||||||
|
|
||||||
// getJobFromTemplate makes a Job from a ScheduledJob
|
// getJobFromTemplate makes a Job from a ScheduledJob
|
||||||
func getJobFromTemplate(sj *batch.ScheduledJob) (*batch.Job, error) {
|
func getJobFromTemplate(sj *batch.ScheduledJob, scheduledTime time.Time) (*batch.Job, error) {
|
||||||
// TODO: consider adding the following labels:
|
// TODO: consider adding the following labels:
|
||||||
// nominal-start-time=$RFC_3339_DATE_OF_INTENDED_START -- for user convenience
|
// nominal-start-time=$RFC_3339_DATE_OF_INTENDED_START -- for user convenience
|
||||||
// scheduled-job-name=$SJ_NAME -- for user convenience
|
// scheduled-job-name=$SJ_NAME -- for user convenience
|
||||||
@ -197,16 +199,14 @@ func getJobFromTemplate(sj *batch.ScheduledJob) (*batch.Job, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
annotations[CreatedByAnnotation] = string(createdByRefJson)
|
annotations[CreatedByAnnotation] = string(createdByRefJson)
|
||||||
// TODO: instead of using generateName, use a deterministic hash of the nominal
|
// We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice
|
||||||
// start time, to prevent same job being created twice.
|
name := fmt.Sprintf("%s-%d", sj.Name, getTimeHash(scheduledTime))
|
||||||
// We want job names for a given nominal start time to have a predictable name to avoid
|
|
||||||
prefix := fmt.Sprintf("%s-", sj.Name)
|
|
||||||
|
|
||||||
job := &batch.Job{
|
job := &batch.Job{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
GenerateName: prefix,
|
Name: name,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := api.Scheme.Convert(&sj.Spec.JobTemplate.Spec, &job.Spec); err != nil {
|
if err := api.Scheme.Convert(&sj.Spec.JobTemplate.Spec, &job.Spec); err != nil {
|
||||||
@ -215,6 +215,12 @@ func getJobFromTemplate(sj *batch.ScheduledJob) (*batch.Job, error) {
|
|||||||
return job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTimeHash(scheduledTime time.Time) uint32 {
|
||||||
|
timeHasher := adler32.New()
|
||||||
|
hashutil.DeepHashObject(timeHasher, scheduledTime)
|
||||||
|
return timeHasher.Sum32()
|
||||||
|
}
|
||||||
|
|
||||||
// makeCreatedByRefJson makes a json string with an object reference for use in "created-by" annotation value
|
// makeCreatedByRefJson makes a json string with an object reference for use in "created-by" annotation value
|
||||||
func makeCreatedByRefJson(object runtime.Object) (string, error) {
|
func makeCreatedByRefJson(object runtime.Object) (string, error) {
|
||||||
createdByRef, err := api.GetReference(object)
|
createdByRef, err := api.GetReference(object)
|
||||||
|
@ -18,6 +18,7 @@ package scheduledjob
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
//"fmt"
|
//"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -72,12 +73,12 @@ func TestGetJobFromTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var job *batch.Job
|
var job *batch.Job
|
||||||
job, err := getJobFromTemplate(&sj)
|
job, err := getJobFromTemplate(&sj, time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Did not expect error: %s", err)
|
t.Errorf("Did not expect error: %s", err)
|
||||||
}
|
}
|
||||||
if job.ObjectMeta.GenerateName != "myscheduledjob-" {
|
if !strings.HasPrefix(job.ObjectMeta.Name, "myscheduledjob-") {
|
||||||
t.Errorf("Wrong GenerateName")
|
t.Errorf("Wrong Name")
|
||||||
}
|
}
|
||||||
if len(job.ObjectMeta.Labels) != 1 {
|
if len(job.ObjectMeta.Labels) != 1 {
|
||||||
t.Errorf("Wrong number of labels")
|
t.Errorf("Wrong number of labels")
|
||||||
|
Loading…
Reference in New Issue
Block a user