Browse Source

1

pull/6/head
Vladimir Smagin 1 month ago
parent
commit
78a0ea9a8d
Signed by: Vladimir Smagin <21h@blindage.org> GPG Key ID: 13BCE12E5618F071

+ 72
- 40
README.md View File

@@ -2,27 +2,16 @@

Manages multiple CronJobs with same image, but different commands and workdirs. Supports mounting of persistent volume to save results somewhere, ENV variables configmaps, concurrency and restart policy.

## Planning

- global and local SecurityContext
- global SubPath for volume mounts
- global node selector
- global resources limits
- global tolerations
- global affinity
- global imagePullPolicy

## Build sources

- You definitly need a Golang installed ([easy install for Ubuntu](http://deb.blindage.org/readme.txt))
- You definitly need a Golang installed
- Clone repository https://git.blindage.org/21h/cron-operator.git to your computer
- Open `Makefile` and change images registry to yours, then run `make && make push`
Or run `docker build -t cron-operator .` to make image and push to your repo later
- Upload image to your registry
- Open `Makefile` and change images registry to yours, then run `make && make push`
- Or run `docker build -t cron-operator .` to make image and push to your repo later

## Prebuilt images

You can find images here https://hub.docker.com/r/iam21h/cron-operator. Pin version number in `operator.yaml` if you do not want automaticaly get new features and bugs.
You can find images here https://hub.docker.com/r/iam21h/cron-operator

## Deploy operator

@@ -40,34 +29,13 @@ Change `operator.yaml` if you want operator watch only specified namespace. Chec

## Settings

In your `CronOp` resource you can set image, parameters and tasks. CronJobs is a result of reconciling tasks section, some parameters can be global and overrided in each task. Remember, CronJob, Pod and Job is a different things.

This parameters can be set in global section or each task:

- Set Configmap with ENV variables
`envConfigmap`: _default empty_

- How to restart Pod
`restartPolicy`: **OnFailure** | *Never* | *Always*

- Limit Job restarts
`backoffLimit`: **0** | integer

- Allow Cronjob to run concurrent Jobs
`concurrencyPolicy`: **Allow** | *Forbid* | *Replace*

Global history limits:

- successfulJobsHistoryLimit: **3** | integer
- failedJobsHistoryLimit: **3** | integer

Also you can save intermediate results in mounted persistent volume. You can set PVC name with `PersistentVolumeName` and mount point with `PersistentVolumePath`. It's easy.

Look into `pkg/apis/blindage/v1alpha1/cronop_types.go` to find more interesting parameters!

## Your first CronOp deployment

Good sample you can find in `deploy/test-cron.yaml`, see it in case of questions.
Good samples you can find in `deploy/test-cron.yaml`.

This resource can help you run your first tasks:

@@ -79,13 +47,77 @@ metadata:
name: mycron
spec:
image: "python:3"
persistentVolumeName: app-cache
persistentVolumePath: /persistent
tasks:
- name: print-test
commands: ['python','-c', 'print("doing nothing")']
- name: print-test-in-pvc-dir
workdir: /persistent
commands: ['python','-c', 'print("doing nothing")', '>', '/persistent/message.txt']
schedule: "* * * * *"
- name: print-another-test
commands: ['cat', '/persistent/message.txt']
schedule: "* * * * *"

---
apiVersion: batch/v1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: app-cache
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: my-efs
```

If you delete this resource operator will delete CronJobs and Jobs.
If you delete this resource operator will delete CronJobs.

## Full Spec

```
type CronOpSpec struct {
// default busybox, override it with our image
Image string `json:"image,omitempty"`
// if you need some persistence set here to your PersistentVolumeClaim name
PersistentVolumeName string `json:"persistentVolumeName,omitempty"`

// mount path
PersistentVolumePath string `json:"persistentVolumePath,omitempty"`

// global ENVs configmap, default empty
EnvConfigmap string `json:"envConfigmap,omitempty"`

// default is empty, but empty is useless ;)
Tasks []CronTask `json:"tasks,omitempty"`
}

type CronTask struct {
// use naming same as containers name, its required
Name string `json:"name"`

// default "0 * * * *", hourly
Schedule string `json:"schedule,omitempty"`

// command, can't be empty
Commands []string `json:"commands"`

// set command working directory, default empty
Workdir string `json:"workdir,omitempty"`

// default OnFailure
RestartPolicy *corev1.RestartPolicy `json:"restartPolicy,omitempty"`

// default Allow
ConcurrencyPolicy *batchv1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`

// local ENVs configmap that overrides global, default empty
EnvConfigmap string `json:"envConfigmap,omitempty"`
}
```

---
Copyright by Vladimir Smagin (21h) 2019

+ 1
- 1
VERSION View File

@@ -1 +1 @@
0.0.4
0.0.5

+ 38
- 18
deploy/test-cron.yaml View File

@@ -4,37 +4,57 @@ metadata:
name: mycron
spec:
image: "python:3"

# global ENVs
envConfigmap: cron-global

# do not run same job again
concurrencyPolicy: Forbid
# persistentVolumeName: my-pvc-name
# persistentVolumePath: /persistent
# persistentVolumeSubPath: data

# podSecurityContext:
# runAsUser: 1000

# nodeSelector:
# disktype: ssd

# affinity:
# podAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# - labelSelector:
# matchExpressions:
# - key: security
# operator: In
# values:
# - S1
# topologyKey: failure-domain.beta.kubernetes.io/zone

# do not restart job pods
restartPolicy: Never
# tolerations:
# - key: "key"
# operator: "Exists"
# effect: "NoSchedule"

# podRequests:
# memory: "64Mi"
# cpu: "250m"
# ephemeral-storage: "10Ki"

# podLimits:
# memory: "128Mi"
# cpu: "500m"
# ephemeral-storage: "10Ki"

# do not restart job
backoffLimit: 0
# history limits
failedJobsHistoryLimit: 2
successfulJobsHistoryLimit: 2
tasks:
# special task with failed command
# override global ENV vars with local and print
- name: var-local
# override global ENV vars with local and print
envConfigmap: cron-local
commands:
- cat
- eating
- turd
- printenv
schedule: "* * * * *"
# simple task without overrides, print only global ENV vars
# print only global ENV vars
- name: var-global
commands:
- printenv
schedule: "* * * * *"
# just print something on python, but shorter command syntax
# just print something on python, shorter command syntax
- name: python-print-test
commands: ['python', '-c', 'print("test")']
schedule: "* * * * *"

+ 1
- 0
go.mod View File

@@ -1,6 +1,7 @@
module git.blindage.org/21h/cron-operator

require (
github.com/NYTimes/gziphandler v1.0.1 // indirect
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713 // indirect
github.com/getsentry/raven-go v0.2.0
github.com/go-logr/logr v0.1.0

+ 24
- 9
pkg/apis/blindage/v1alpha1/cronop_types.go View File

@@ -10,14 +10,14 @@ import (
// +k8s:openapi-gen=true

type CronTask struct {
Name string `json:"name"`
Schedule string `json:"schedule,omitempty"` // default "0 * * * *", hourly
Workdir string `json:"workdir,omitempty"` // default empty
Commands []string `json:"commands"`
RestartPolicy *corev1.RestartPolicy `json:"restartPolicy,omitempty"` // default OnFailure, overrides global policy
ConcurrencyPolicy *batchv1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` // default Allow, overrides global policy
EnvConfigmap string `json:"envConfigmap,omitempty"` // default empty, overrides global configmap
BackoffLimit *int32 `json:"backoffLimit,omitempty"` // default 0, overrides global limit
Name string `json:"name"`
Schedule string `json:"schedule,omitempty"` // default "0 * * * *", hourly
Workdir string `json:"workdir,omitempty"` // default empty
Commands []string `json:"commands"`
RestartPolicy *corev1.RestartPolicy `json:"restartPolicy,omitempty"` // default OnFailure, overrides global policy
ConcurrencyPolicy *batchv1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` // default Allow, overrides global policy
EnvConfigmap string `json:"envConfigmap,omitempty"` // default empty, overrides global configmap
PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`
}

type CronOpSpec struct {
@@ -25,12 +25,27 @@ type CronOpSpec struct {
ConcurrencyPolicy *batchv1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` // default Allow, global
SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"` // default 3
FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"` // default 3
BackoffLimit *int32 `json:"backoffLimit,omitempty"` // default 0, global
Image string `json:"image,omitempty"` // default busybox
PersistentVolumeName string `json:"persistentVolumeName,omitempty"` // default emtpy, must be created before use
PersistentVolumePath string `json:"persistentVolumePath,omitempty"` // default emtpy
PersistentVolumeSubPath string `json:"persistentVolumeSubPath,omitempty"` // default emtpy
EnvConfigmap string `json:"envConfigmap,omitempty"` // default empty, global
Tasks []CronTask `json:"tasks,omitempty"` // default empty
PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"`

// standart kubernetes resource limits for pods
// https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
PodRequests corev1.ResourceList `json:"podRequests,omitempty"`
PodLimits corev1.ResourceList `json:"podLimits,omitempty"`

// node selection
// https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
Affinity *corev1.Affinity `json:"affinity,omitempty"`

// tolerations
// https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"`
}

// CronOpStatus defines the observed state of CronOp

+ 46
- 9
pkg/apis/blindage/v1alpha1/zz_generated.deepcopy.go View File

@@ -94,11 +94,6 @@ func (in *CronOpSpec) DeepCopyInto(out *CronOpSpec) {
*out = new(int32)
**out = **in
}
if in.BackoffLimit != nil {
in, out := &in.BackoffLimit, &out.BackoffLimit
*out = new(int32)
**out = **in
}
if in.Tasks != nil {
in, out := &in.Tasks, &out.Tasks
*out = make([]CronTask, len(*in))
@@ -106,6 +101,48 @@ func (in *CronOpSpec) DeepCopyInto(out *CronOpSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.PodSecurityContext != nil {
in, out := &in.PodSecurityContext, &out.PodSecurityContext
*out = new(v1.PodSecurityContext)
(*in).DeepCopyInto(*out)
}
if in.PodRequests != nil {
in, out := &in.PodRequests, &out.PodRequests
*out = make(v1.ResourceList, len(*in))
for key, val := range *in {
(*out)[key] = val.DeepCopy()
}
}
if in.PodLimits != nil {
in, out := &in.PodLimits, &out.PodLimits
*out = make(v1.ResourceList, len(*in))
for key, val := range *in {
(*out)[key] = val.DeepCopy()
}
}
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Affinity != nil {
in, out := &in.Affinity, &out.Affinity
*out = new(v1.Affinity)
(*in).DeepCopyInto(*out)
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = new([]v1.Toleration)
if **in != nil {
in, out := *in, *out
*out = make([]v1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
return
}

@@ -153,10 +190,10 @@ func (in *CronTask) DeepCopyInto(out *CronTask) {
*out = new(v1beta1.ConcurrencyPolicy)
**out = **in
}
if in.BackoffLimit != nil {
in, out := &in.BackoffLimit, &out.BackoffLimit
*out = new(int32)
**out = **in
if in.PodSecurityContext != nil {
in, out := &in.PodSecurityContext, &out.PodSecurityContext
*out = new(v1.PodSecurityContext)
(*in).DeepCopyInto(*out)
}
return
}

+ 0
- 174
pkg/controller/cronop/cronjob.go View File

@@ -1,174 +0,0 @@
package cronop

import (
"github.com/go-logr/logr"
batchv1beta1 "k8s.io/api/batch/v1beta1"

blindagev1alpha1 "git.blindage.org/21h/cron-operator/pkg/apis/blindage/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// generate one cronjob for specific task
func generateCronjob(reqLogger logr.Logger, cr *blindagev1alpha1.CronOp, jobSpec blindagev1alpha1.CronTask) batchv1beta1.CronJob {
labels := map[string]string{
"operator": "cron-operator",
"cronop": cr.Name,
}

imageName := "busybox"
if cr.Spec.Image != "" {
reqLogger.Info("Found Image override", "Namespace", cr.Namespace, "Name", cr.Name, "Image", cr.Spec.Image)
imageName = cr.Spec.Image
}

cronjob := batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name + "-" + jobSpec.Name,
Namespace: cr.Namespace,
Labels: labels,
},
Spec: batchv1beta1.CronJobSpec{},
}

cronjob.Spec.Schedule = "0 * * * *"
if jobSpec.Schedule != "" {
cronjob.Spec.Schedule = jobSpec.Schedule
}

workdir := ""
if jobSpec.Workdir != "" {
workdir = jobSpec.Workdir
}

//
// set backoff limit
//

// default backoff limit
backoffLimit := int32(0)

// override default limit with global
if cr.Spec.BackoffLimit != nil {
backoffLimit = *cr.Spec.BackoffLimit
}

// override default limit with global
if jobSpec.BackoffLimit != nil {
backoffLimit = *jobSpec.BackoffLimit
}

cronjob.Spec.JobTemplate.Spec.BackoffLimit = &backoffLimit

//
// restart policy
//

// default restart policy
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure

// override default policy from global spec
if cr.Spec.RestartPolicy != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = *cr.Spec.RestartPolicy
}

// override global policy from task spec
if jobSpec.RestartPolicy != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = *jobSpec.RestartPolicy
}

//
// concurrency policy
//

// default concurrency policy
cronjob.Spec.ConcurrencyPolicy = batchv1beta1.AllowConcurrent

// override default policy from global spec
if cr.Spec.ConcurrencyPolicy != nil {
cronjob.Spec.ConcurrencyPolicy = *cr.Spec.ConcurrencyPolicy
}

// override global policy from task spec
if jobSpec.ConcurrencyPolicy != nil {
cronjob.Spec.ConcurrencyPolicy = *jobSpec.ConcurrencyPolicy
}

//
// ENV variables
//

envs := []corev1.EnvFromSource{}
// add global ENVs
if cr.Spec.EnvConfigmap != "" {
envsGlobal := corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: cr.Spec.EnvConfigmap},
},
}
envs = append(envs, envsGlobal)
}

// override global ENVs with local for task
if jobSpec.EnvConfigmap != "" {
envsTask := corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: jobSpec.EnvConfigmap},
},
}
envs = append(envs, envsTask)
}

//
// history limits
//

SuccessfulJobsHistoryLimit := int32(3)
if cr.Spec.SuccessfulJobsHistoryLimit != nil {
SuccessfulJobsHistoryLimit = *cr.Spec.SuccessfulJobsHistoryLimit
}
cronjob.Spec.SuccessfulJobsHistoryLimit = &SuccessfulJobsHistoryLimit

FailedJobsHistoryLimit := int32(3)
if cr.Spec.FailedJobsHistoryLimit != nil {
FailedJobsHistoryLimit = *cr.Spec.FailedJobsHistoryLimit
}

cronjob.Spec.FailedJobsHistoryLimit = &FailedJobsHistoryLimit

jobContainer := corev1.Container{
Name: "crontask",
Image: imageName,
Command: jobSpec.Commands,
WorkingDir: workdir,
}

jobContainer.EnvFrom = envs

// If persistence is present
if cr.Spec.PersistentVolumeName != "" {
// Attach PVC to Pod
cronjob.Spec.JobTemplate.Spec.Template.Spec.Volumes = []corev1.Volume{
{
Name: "persistent-volume",
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: cr.Spec.PersistentVolumeName,
},
},
},
}

// Set mount point
jobContainer.VolumeMounts = []corev1.VolumeMount{
{
Name: "persistent-volume",
MountPath: cr.Spec.PersistentVolumePath,
},
}
}

cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers = []corev1.Container{jobContainer}

return cronjob
}

+ 221
- 0
pkg/controller/cronop/cronop_controller.go View File

@@ -2,13 +2,17 @@ package cronop

import (
"context"
"reflect"

"github.com/getsentry/raven-go"
"github.com/go-logr/logr"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"k8s.io/apimachinery/pkg/labels"

blindagev1alpha1 "git.blindage.org/21h/cron-operator/pkg/apis/blindage/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -23,6 +27,11 @@ import (

var log = logf.Log.WithName("controller_cronop")

/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/

// Add creates a new CronOp Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
@@ -159,3 +168,215 @@ func (r *ReconcileCronOp) Reconcile(request reconcile.Request) (reconcile.Result
reqLogger.Info("Reconcile complete", "Namespace", instance.Namespace, "Name", instance.Name)
return reconcile.Result{}, nil
}

// decide to reconcile CronJob
func reconcileCronJob(found batchv1beta1.CronJob, new batchv1beta1.CronJob) (bool, batchv1beta1.CronJob) {

reconcileRequired := false

if !reflect.DeepEqual(found.Spec.Schedule, new.Spec.Schedule) {
found.Spec.Schedule = new.Spec.Schedule
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.ConcurrencyPolicy, new.Spec.ConcurrencyPolicy) {
found.Spec.ConcurrencyPolicy = new.Spec.ConcurrencyPolicy
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy, new.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy) {
found.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = new.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.JobTemplate.Spec, new.Spec.JobTemplate.Spec) {
found.Spec.JobTemplate.Spec = new.Spec.JobTemplate.Spec
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.SuccessfulJobsHistoryLimit, new.Spec.SuccessfulJobsHistoryLimit) {
found.Spec.SuccessfulJobsHistoryLimit = new.Spec.SuccessfulJobsHistoryLimit
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.FailedJobsHistoryLimit, new.Spec.FailedJobsHistoryLimit) {
found.Spec.FailedJobsHistoryLimit = new.Spec.FailedJobsHistoryLimit
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.JobTemplate.Spec.Template.Spec.Containers, new.Spec.JobTemplate.Spec.Template.Spec.Containers) {
found.Spec.JobTemplate.Spec.Template.Spec.Containers = new.Spec.JobTemplate.Spec.Template.Spec.Containers
reconcileRequired = true
}

return reconcileRequired, found

}

// generate one cronjob for specific task
func generateCronjob(reqLogger logr.Logger, cr *blindagev1alpha1.CronOp, jobSpec blindagev1alpha1.CronTask) batchv1beta1.CronJob {
labels := map[string]string{
"operator": "cron-operator",
"cronop": cr.Name,
}

imageName := "busybox"
if cr.Spec.Image != "" {
reqLogger.Info("Found Image override", "Namespace", cr.Namespace, "Name", cr.Name, "Image", cr.Spec.Image)
imageName = cr.Spec.Image
}

cronjob := batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name + "-" + jobSpec.Name,
Namespace: cr.Namespace,
Labels: labels,
},
Spec: batchv1beta1.CronJobSpec{},
}

cronjob.Spec.Schedule = "0 * * * *"
if jobSpec.Schedule != "" {
cronjob.Spec.Schedule = jobSpec.Schedule
}

workdir := ""
if jobSpec.Workdir != "" {
workdir = jobSpec.Workdir
}

//
// restart policy
//

// default restart policy
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure

// override default policy from global spec
if cr.Spec.RestartPolicy != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = *cr.Spec.RestartPolicy
}

// override global policy from task spec
if jobSpec.RestartPolicy != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = *jobSpec.RestartPolicy
}

//
// concurrency policy
//

// default concurrency policy
cronjob.Spec.ConcurrencyPolicy = batchv1beta1.AllowConcurrent

// override default policy from global spec
if cr.Spec.ConcurrencyPolicy != nil {
cronjob.Spec.ConcurrencyPolicy = *cr.Spec.ConcurrencyPolicy
}

// override global policy from task spec
if jobSpec.ConcurrencyPolicy != nil {
cronjob.Spec.ConcurrencyPolicy = *jobSpec.ConcurrencyPolicy
}

cronjob.Spec.JobTemplate.Spec.Template.Spec.NodeSelector = cr.Spec.NodeSelector

if cr.Spec.Affinity != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.Affinity = cr.Spec.Affinity
}

if cr.Spec.Tolerations != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.Tolerations = *cr.Spec.Tolerations
}

if cr.Spec.PodSecurityContext != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.SecurityContext = cr.Spec.PodSecurityContext
}

if jobSpec.PodSecurityContext != nil {
cronjob.Spec.JobTemplate.Spec.Template.Spec.SecurityContext = jobSpec.PodSecurityContext
}

//
// ENV variables
//

envs := []corev1.EnvFromSource{}
// add global ENVs
if cr.Spec.EnvConfigmap != "" {
envsGlobal := corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: cr.Spec.EnvConfigmap},
},
}
envs = append(envs, envsGlobal)
}

// override global ENVs with local for task
if jobSpec.EnvConfigmap != "" {
envsTask := corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{Name: jobSpec.EnvConfigmap},
},
}
envs = append(envs, envsTask)
}

jobContainer := corev1.Container{
Name: "crontask",
Image: imageName,
Command: jobSpec.Commands,
WorkingDir: workdir,
Resources: corev1.ResourceRequirements{
Requests: cr.Spec.PodRequests,
Limits: cr.Spec.PodLimits,
},
}

jobContainer.EnvFrom = envs

// If persistence is present
if cr.Spec.PersistentVolumeName != "" {
// Attach PVC to Pod
cronjob.Spec.JobTemplate.Spec.Template.Spec.Volumes = []corev1.Volume{
{
Name: "persistent-volume",
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: cr.Spec.PersistentVolumeName,
},
},
},
}

// Set mount point
volumeMount := corev1.VolumeMount{
Name: "persistent-volume",
MountPath: cr.Spec.PersistentVolumePath,
}
if cr.Spec.PersistentVolumeSubPath != "" {
volumeMount.SubPath = cr.Spec.PersistentVolumeSubPath
}
jobContainer.VolumeMounts = []corev1.VolumeMount{volumeMount}
}

//
// history limits
//

SuccessfulJobsHistoryLimit := int32(3)
if cr.Spec.SuccessfulJobsHistoryLimit != nil {
SuccessfulJobsHistoryLimit = *cr.Spec.SuccessfulJobsHistoryLimit
}
cronjob.Spec.SuccessfulJobsHistoryLimit = &SuccessfulJobsHistoryLimit

FailedJobsHistoryLimit := int32(3)
if cr.Spec.FailedJobsHistoryLimit != nil {
FailedJobsHistoryLimit = *cr.Spec.FailedJobsHistoryLimit
}
cronjob.Spec.FailedJobsHistoryLimit = &FailedJobsHistoryLimit

cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers = []corev1.Container{jobContainer}

return cronjob
}

+ 0
- 46
pkg/controller/cronop/reconcile.go View File

@@ -1,46 +0,0 @@
package cronop

import (
"reflect"

batchv1beta1 "k8s.io/api/batch/v1beta1"
)

// decide to reconcile CronJob
func reconcileCronJob(found batchv1beta1.CronJob, new batchv1beta1.CronJob) (bool, batchv1beta1.CronJob) {

reconcileRequired := false

if !reflect.DeepEqual(found.Spec.Schedule, new.Spec.Schedule) {
found.Spec.Schedule = new.Spec.Schedule
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.ConcurrencyPolicy, new.Spec.ConcurrencyPolicy) {
found.Spec.ConcurrencyPolicy = new.Spec.ConcurrencyPolicy
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy, new.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy) {
found.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = new.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.JobTemplate.Spec, new.Spec.JobTemplate.Spec) {
found.Spec.JobTemplate.Spec = new.Spec.JobTemplate.Spec
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.SuccessfulJobsHistoryLimit, new.Spec.SuccessfulJobsHistoryLimit) {
found.Spec.SuccessfulJobsHistoryLimit = new.Spec.SuccessfulJobsHistoryLimit
reconcileRequired = true
}

if !reflect.DeepEqual(found.Spec.FailedJobsHistoryLimit, new.Spec.FailedJobsHistoryLimit) {
found.Spec.FailedJobsHistoryLimit = new.Spec.FailedJobsHistoryLimit
reconcileRequired = true
}

return reconcileRequired, found

}

+ 1
- 1
version/version.go View File

@@ -1,5 +1,5 @@
package version

var (
Version = "0.0.4"
Version = "0.0.5"
)

Loading…
Cancel
Save