mirror of
https://github.com/lukaszraczylo/jobs-manager-operator.git
synced 2026-10-12 05:32:52 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c81d4c479
|
||
|
|
728c9f0b8d
|
||
|
|
75393e791a
|
||
|
|
c99558314c | ||
|
|
56edfc8d8c
|
||
|
|
5c92083161
|
||
|
|
60cb552179
|
||
|
|
9d3086c839
|
||
|
|
6189a2e2f3
|
||
|
|
6e39505910
|
||
|
|
9ab900dbd2
|
||
|
|
55cd8bb7a6 |
@@ -0,0 +1,76 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '22 9 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -0,0 +1 @@
|
||||
* lukaszraczylo
|
||||
@@ -145,6 +145,43 @@ In this case - result for the first job will look like this:
|
||||
status: succeeded
|
||||
```
|
||||
|
||||
### Available params
|
||||
|
||||
There's quite a lot of of flexibility with parameters. On every level where params are allowed, you can define:
|
||||
|
||||
```yaml
|
||||
params:
|
||||
fromEnv:
|
||||
- configMapRef:
|
||||
name: "configmap-name"
|
||||
key: "key-name"
|
||||
env:
|
||||
- name: "FOO"
|
||||
value: "bar"
|
||||
volumes:
|
||||
- name: secrets-store-api
|
||||
csi:
|
||||
driver: secrets-store.csi.k8s.io
|
||||
readOnly: true
|
||||
volumeAttributes:
|
||||
secretProviderClass: api-secrets-provider
|
||||
volumeMount:
|
||||
- name: secrets-store-api
|
||||
mountPath: "/mnt/secrets-api"
|
||||
readOnly: true
|
||||
serviceAccount: "service-account-name"
|
||||
restartPolicy: "Never"
|
||||
imagePullSecrets:
|
||||
- "ghcr-token"
|
||||
imagePullPolicy:
|
||||
- "Always"
|
||||
labels:
|
||||
this/works: "true"
|
||||
annotations:
|
||||
this/works/aswell: "true"
|
||||
```
|
||||
|
||||
|
||||
### Kustomization and references
|
||||
|
||||
In case of any issues with `configmapGenerator` or `secretGenerator`, please add following to your `kustomization.yaml`:
|
||||
|
||||
@@ -94,6 +94,8 @@ type ManagedJobParameters struct {
|
||||
ImagePullPolicy string `json:"imagePullPolicy,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// ManagedJobSpec defines the desired state of ManagedJob
|
||||
@@ -107,9 +109,6 @@ type ManagedJobSpec struct {
|
||||
Groups []*ManagedJobGroup `json:"groups"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Params ManagedJobParameters `json:"params"`
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default=pending
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
@@ -213,6 +213,13 @@ func (in *ManagedJobParameters) DeepCopyInto(out *ManagedJobParameters) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedJobParameters.
|
||||
|
||||
@@ -13,12 +13,12 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.0.8
|
||||
version: 0.0.24
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.0.8"
|
||||
appVersion: "0.0.24"
|
||||
keywords:
|
||||
- operator
|
||||
- jobs
|
||||
|
||||
@@ -59,6 +59,10 @@ spec:
|
||||
type: array
|
||||
compiledParams:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable
|
||||
@@ -2135,6 +2139,10 @@ spec:
|
||||
type: boolean
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable
|
||||
@@ -4205,6 +4213,10 @@ spec:
|
||||
type: boolean
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable present
|
||||
@@ -6131,6 +6143,10 @@ spec:
|
||||
type: array
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable present
|
||||
@@ -7927,9 +7943,6 @@ spec:
|
||||
default: 1
|
||||
minimum: 1
|
||||
type: integer
|
||||
status:
|
||||
default: pending
|
||||
type: string
|
||||
required:
|
||||
- groups
|
||||
- retries
|
||||
|
||||
@@ -5,6 +5,30 @@ metadata:
|
||||
labels:
|
||||
{{- include "chart.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- jobsmanager.raczylo.com
|
||||
resources:
|
||||
@@ -31,28 +55,6 @@ rules:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
# Add ability to manage jobs
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
# Add access to events
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
|
||||
@@ -13,7 +13,7 @@ controllerManager:
|
||||
manager:
|
||||
image:
|
||||
repository: ghcr.io/lukaszraczylo/jobs-manager-operator
|
||||
tag: 0.0.5
|
||||
tag: latest
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
|
||||
@@ -59,6 +59,10 @@ spec:
|
||||
type: array
|
||||
compiledParams:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable
|
||||
@@ -2205,6 +2209,10 @@ spec:
|
||||
type: boolean
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable
|
||||
@@ -4345,6 +4353,10 @@ spec:
|
||||
type: boolean
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable
|
||||
@@ -6317,6 +6329,10 @@ spec:
|
||||
type: array
|
||||
params:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
env:
|
||||
items:
|
||||
description: EnvVar represents an environment variable present
|
||||
@@ -8150,9 +8166,6 @@ spec:
|
||||
default: 1
|
||||
minimum: 1
|
||||
type: integer
|
||||
status:
|
||||
default: pending
|
||||
type: string
|
||||
required:
|
||||
- groups
|
||||
- retries
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
resources:
|
||||
- manager.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
|
||||
@@ -5,6 +5,30 @@ metadata:
|
||||
creationTimestamp: null
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- jobsmanager.raczylo.com
|
||||
resources:
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (cp *connPackage) checkGroupsStatus() {
|
||||
groupsTotal := len(cp.mj.Spec.Groups)
|
||||
totalJobs := 0
|
||||
completedJobs := 0
|
||||
didAnyJobAbort := false
|
||||
changePresent := false
|
||||
groupsCompleted := 0
|
||||
|
||||
// Check if all groups have completed and set ManagedJob status to "succeeded" if so.
|
||||
if cp.mj.Spec.Status != ExecutionStatusSucceeded && groupsTotal > 0 {
|
||||
for _, group := range cp.mj.Spec.Groups {
|
||||
groupJobsTotal := len(group.Jobs)
|
||||
groupJobsCompleted := 0
|
||||
|
||||
for _, job := range group.Jobs {
|
||||
if job.Status == ExecutionStatusSucceeded {
|
||||
groupJobsCompleted++
|
||||
completedJobs++
|
||||
}
|
||||
if job.Status == ExecutionStatusFailed || job.Status == ExecutionStatusAborted {
|
||||
didAnyJobAbort = true
|
||||
}
|
||||
totalJobs++
|
||||
}
|
||||
|
||||
if groupJobsTotal == groupJobsCompleted {
|
||||
// All the jobs in the group are completed.
|
||||
if group.Status != ExecutionStatusSucceeded {
|
||||
group.Status = ExecutionStatusSucceeded
|
||||
changePresent = true
|
||||
}
|
||||
groupsCompleted++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if groupsTotal == groupsCompleted && cp.mj.Spec.Status != ExecutionStatusSucceeded && cp.mj.Status != ExecutionStatusSucceeded {
|
||||
cp.mj.Spec.Status = ExecutionStatusSucceeded
|
||||
changePresent = true
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Completed", "All jobs completed")
|
||||
}
|
||||
|
||||
// Update status if any job aborted.
|
||||
if didAnyJobAbort && cp.mj.Spec.Status != ExecutionStatusFailed {
|
||||
cp.mj.Spec.Status = ExecutionStatusFailed
|
||||
changePresent = true
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Aborted", "One of the jobs aborted")
|
||||
}
|
||||
|
||||
// Update status to "running" if not already set.
|
||||
// if cp.mj.Spec.Status != ExecutionStatusRunning && cp.mj.Spec.Status != ExecutionStatusSucceeded {
|
||||
// cp.mj.Spec.Status = ExecutionStatusRunning
|
||||
// cp.mj.Status = ExecutionStatusRunning
|
||||
// changePresent = true
|
||||
// }
|
||||
|
||||
// Check if the ManagedJob status has changed.
|
||||
statusChanged := cp.mj.Spec.Status != cp.mj.Status
|
||||
|
||||
// Update status and send event if it has changed.
|
||||
if statusChanged || changePresent {
|
||||
cp.updateCRDStatusDirectly()
|
||||
// cp.r.Client.Status().Update(cp.ctx, cp.mj)
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ func (cp *connPackage) generateDependencyTree() {
|
||||
groupTree := mainTree.Add(group.Name)
|
||||
for _, job := range group.Jobs {
|
||||
jobTree := groupTree.Add(job.Name)
|
||||
job.CompiledParams = cp.compileParameters(cp.mj.Spec.Params, group.Params, job.Params)
|
||||
if job.Parallel {
|
||||
continue
|
||||
} else {
|
||||
@@ -130,7 +131,6 @@ func (cp *connPackage) generateDependencyTree() {
|
||||
jobTree.Add("Depends on: " + generatedJobName)
|
||||
if !cp.checkIfPresentInDependencies(job.Dependencies, generatedJobName) {
|
||||
job.Dependencies = append(job.Dependencies, &jobsmanagerv1beta1.ManagedJobDependencies{Name: generatedJobName, Status: ExecutionStatusPending})
|
||||
job.CompiledParams = cp.compileParameters(cp.mj.Spec.Params, group.Params, job.Params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-25
@@ -1,7 +1,6 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lukaszraczylo/pandati"
|
||||
@@ -25,6 +24,7 @@ type compiledParams struct {
|
||||
ImagePullSecrets []corev1.LocalObjectReference
|
||||
ImagePullPolicy string
|
||||
Labels map[string]string
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
func (cp *connPackage) compileParameters(params ...jobsmanagerv1beta1.ManagedJobParameters) jobsmanagerv1beta1.ManagedJobParameters {
|
||||
@@ -53,10 +53,17 @@ func (cp *connPackage) compileParameters(params ...jobsmanagerv1beta1.ManagedJob
|
||||
cparams.ImagePullSecrets = append(cparams.ImagePullSecrets, params.ImagePullSecrets...)
|
||||
}
|
||||
if params.Labels != nil {
|
||||
cparams.Labels = make(map[string]string)
|
||||
for k, v := range params.Labels {
|
||||
cparams.Labels[k] = v
|
||||
}
|
||||
}
|
||||
if params.Annotations != nil {
|
||||
cparams.Annotations = make(map[string]string)
|
||||
for k, v := range params.Annotations {
|
||||
cparams.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cparams
|
||||
@@ -102,18 +109,17 @@ func (cp *connPackage) checkRunningJobsStatus() {
|
||||
generatedJobName := jobNameGenerator(cp.mj.Name, group.Name, job.Name)
|
||||
if childJob.Name == generatedJobName {
|
||||
if childJob.Status.Succeeded > 0 && job.Status != ExecutionStatusSucceeded {
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Completed", "Job %s completed [prev: %s]", childJob.Name, job.Status)
|
||||
job.Status = ExecutionStatusSucceeded
|
||||
cp.updateDependentJobs(generatedJobName, ExecutionStatusSucceeded)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Completed", "Job %s completed", childJob.Name)
|
||||
} else if childJob.Status.Failed > 0 && job.Status != ExecutionStatusFailed {
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeWarning, "Failed", "Job %s failed [prev: %s]", childJob.Name, job.Status)
|
||||
job.Status = ExecutionStatusFailed
|
||||
cp.updateDependentJobs(generatedJobName, ExecutionStatusFailed)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeWarning, "Failed", "Job %s failed", childJob.Name)
|
||||
} else if childJob.Status.Active > 0 && job.Status != ExecutionStatusRunning {
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Running", "Job %s running [prev: %s]", childJob.Name, job.Status)
|
||||
job.Status = ExecutionStatusRunning
|
||||
cp.updateDependentJobs(generatedJobName, ExecutionStatusRunning)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Running", "Job %s running", childJob.Name)
|
||||
}
|
||||
cp.updateDependentJobs(generatedJobName, job.Status)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,11 +143,13 @@ func (cp *connPackage) runPendingJobs() {
|
||||
continue
|
||||
}
|
||||
|
||||
if group.Status == ExecutionStatusSucceeded || group.Status == ExecutionStatusFailed || group.Status == ExecutionStatusAborted {
|
||||
approvedStatuses := []string{ExecutionStatusSucceeded, ExecutionStatusFailed, ExecutionStatusAborted}
|
||||
if pandati.ExistsInSlice(approvedStatuses, group.Status) {
|
||||
cp.updateDependentGroups(group.Name, group.Status)
|
||||
}
|
||||
|
||||
if group.Status == ExecutionStatusPending || group.Status == ExecutionStatusRunning {
|
||||
approvedStatuses = []string{ExecutionStatusPending, ExecutionStatusRunning}
|
||||
if pandati.ExistsInSlice(approvedStatuses, group.Status) {
|
||||
if len(group.Dependencies) > 0 {
|
||||
groupsCompleted := 0
|
||||
for _, group_dependency := range group.Dependencies {
|
||||
@@ -161,7 +169,7 @@ func (cp *connPackage) runPendingJobs() {
|
||||
}
|
||||
|
||||
if !run_group {
|
||||
fmt.Println("Group "+group.Name+" is not running as dependencies were not met", group.Dependencies)
|
||||
// fmt.Println("Group "+group.Name+" is not running as dependencies were not met", group.Dependencies)
|
||||
continue // not running the group as dependencies were not met
|
||||
} else {
|
||||
group.Status = ExecutionStatusRunning
|
||||
@@ -192,27 +200,29 @@ func (cp *connPackage) runPendingJobs() {
|
||||
if !run_job {
|
||||
continue // job is not ready as dependencies were not met
|
||||
} else {
|
||||
if job.Status != ExecutionStatusRunning && job.Status != ExecutionStatusFailed && job.Status != ExecutionStatusSucceeded {
|
||||
job.Status = ExecutionStatusRunning
|
||||
cp.updateDependentJobs(job.Name, ExecutionStatusRunning)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Running", "Job %s from group %s running", job.Name, group.Name)
|
||||
approvedStatuses = []string{ExecutionStatusRunning, ExecutionStatusFailed, ExecutionStatusAborted}
|
||||
if !pandati.ExistsInSlice(approvedStatuses, job.Status) {
|
||||
err := cp.executeJob(job, group)
|
||||
if err != nil {
|
||||
log.Log.Info("Unable to execute job", "error", err.Error())
|
||||
if !strings.Contains(err.Error(), "already exists") {
|
||||
if !strings.Contains(err.Error(), "exists") {
|
||||
job.Status = ExecutionStatusFailed
|
||||
group.Status = ExecutionStatusFailed
|
||||
cp.updateDependentJobs(job.Name, ExecutionStatusFailed)
|
||||
cp.updateDependentGroups(group.Name, ExecutionStatusFailed)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeWarning, "Failed", "Job %s from group %s failed", job.Name, group.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
job.Status = ExecutionStatusRunning
|
||||
cp.updateDependentJobs(job.Name, ExecutionStatusRunning)
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Running", "Job %s from group %s running", job.Name, group.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Running group: ", group.Name, " with status: ", group.Status, " accepted: ", run_group)
|
||||
// fmt.Println("Running group: ", group.Name, " with status: ", group.Status, " accepted: ", run_group)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,6 +250,12 @@ func (cp *connPackage) executeJob(j *jobsmanagerv1beta1.ManagedJobDefinition, g
|
||||
labels[k] = v
|
||||
}
|
||||
|
||||
annotations := map[string]string{}
|
||||
|
||||
for k, v := range j.CompiledParams.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
|
||||
job_handler := kbatch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: generatedJobName,
|
||||
@@ -248,9 +264,10 @@ func (cp *connPackage) executeJob(j *jobsmanagerv1beta1.ManagedJobDefinition, g
|
||||
Spec: kbatch.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: generatedJobName,
|
||||
Namespace: cp.mj.Namespace,
|
||||
Labels: labels,
|
||||
Name: generatedJobName,
|
||||
Namespace: cp.mj.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Volumes: j.CompiledParams.Volumes,
|
||||
@@ -283,7 +300,6 @@ func (cp *connPackage) executeJob(j *jobsmanagerv1beta1.ManagedJobDefinition, g
|
||||
|
||||
err = cp.r.Client.Create(cp.ctx, &job_handler)
|
||||
if err != nil || pandati.IsZero(job_handler) {
|
||||
log.Log.Info("Unable to create job", "job", job_handler.Name, "error", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -293,22 +309,25 @@ func (cp *connPackage) executeJob(j *jobsmanagerv1beta1.ManagedJobDefinition, g
|
||||
|
||||
func (cp *connPackage) checkOverallStatus() {
|
||||
groupsCompleted := 0
|
||||
negativeStatuses := []string{ExecutionStatusFailed, ExecutionStatusAborted}
|
||||
for _, group := range cp.mj.Spec.Groups {
|
||||
if group.Status == ExecutionStatusSucceeded {
|
||||
groupsCompleted++
|
||||
} else if group.Status == ExecutionStatusFailed || group.Status == ExecutionStatusAborted {
|
||||
} else if pandati.ExistsInSlice(negativeStatuses, group.Status) {
|
||||
cp.mj.Status = ExecutionStatusFailed
|
||||
cp.mj.Spec.Status = cp.mj.Status
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeWarning, "Failure", "Run failed")
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeWarning, "Failure", "Run failed in group %s", group.Name)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if groupsCompleted == len(cp.mj.Spec.Groups) {
|
||||
if cp.mj.Status != ExecutionStatusSucceeded {
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Success", "Run completed successfuly")
|
||||
}
|
||||
cp.mj.Status = ExecutionStatusSucceeded
|
||||
cp.r.Recorder.Eventf(cp.mj, corev1.EventTypeNormal, "Success", "Run completed successfuly")
|
||||
} else {
|
||||
cp.mj.Status = ExecutionStatusRunning
|
||||
}
|
||||
cp.mj.Spec.Status = cp.mj.Status
|
||||
cp.r.Status().Update(cp.ctx, cp.mj)
|
||||
}
|
||||
|
||||
@@ -2,19 +2,13 @@ package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kr/pretty"
|
||||
"github.com/lukaszraczylo/pandati"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"raczylo.com/jobs-manager-operator/api/v1beta1"
|
||||
jobsmanagerv1beta1 "raczylo.com/jobs-manager-operator/api/v1beta1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
@@ -54,65 +48,8 @@ func (cp *connPackage) getOwnerReference() (metav1.OwnerReference, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type patchStringValue struct {
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (cp *connPackage) getLatestMainJobAndPatch(proposedChanges *jobsmanagerv1beta1.ManagedJob) (bool, error) {
|
||||
mj := jobsmanagerv1beta1.ManagedJob{}
|
||||
err := cp.r.Client.Get(cp.ctx, cp.req.NamespacedName, &mj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
diff, diffIdentical, _ := pandati.CompareStructsReplaced(mj, proposedChanges)
|
||||
if !diffIdentical {
|
||||
if err != nil {
|
||||
log.Log.Error(err, "Unable to marshal ManagedJob")
|
||||
}
|
||||
var payloadBytesArr []patchStringValue
|
||||
for _, d := range diff {
|
||||
if strings.HasPrefix(d.Key, "/spec") {
|
||||
operation := "replace"
|
||||
if d.OldValue == "" || d.OldValue == nil {
|
||||
operation = "add"
|
||||
}
|
||||
|
||||
payload := patchStringValue{
|
||||
Op: operation,
|
||||
Path: d.Key,
|
||||
Value: d.Value.(string),
|
||||
}
|
||||
payloadBytesArr = append(payloadBytesArr, payload)
|
||||
}
|
||||
}
|
||||
patchPayload, err := json.Marshal(payloadBytesArr)
|
||||
if err != nil {
|
||||
log.Log.Error(err, "Unable to marshal ManagedJob")
|
||||
}
|
||||
fmt.Printf("PatchPayload: %# v", pretty.Formatter(fmt.Sprintf("%s", patchPayload)))
|
||||
kubepath := client.RawPatch(types.JSONPatchType, patchPayload)
|
||||
err = cp.r.Client.Patch(cp.ctx, &mj, kubepath)
|
||||
// if err != nil {
|
||||
// log.Log.Error(err, "Unable to patch ManagedJob")
|
||||
// }
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (cp *connPackage) updateCRDStatusDirectly() error {
|
||||
cp.mtx.Lock()
|
||||
// defer cp.mtx.Unlock()
|
||||
|
||||
// val, err := cp.getLatestMainJobAndPatch(cp.mj)
|
||||
// if err != nil {
|
||||
// log.Log.Error(err, "Unable to get latest ManagedJob")
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if !val && err == nil {
|
||||
err := cp.r.Update(cp.ctx, cp.mj)
|
||||
if err != nil {
|
||||
// log.Log.Info("Error", err.Error(), "more", "Unable to update ManagedJob status directly")
|
||||
@@ -122,7 +59,6 @@ func (cp *connPackage) updateCRDStatusDirectly() error {
|
||||
if err != nil {
|
||||
log.Log.Error(err, "Unable to get updated ManagedJob")
|
||||
}
|
||||
// }
|
||||
cp.mtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ type ManagedJobReconciler struct {
|
||||
//+kubebuilder:rbac:groups=jobsmanager.raczylo.com,resources=managedjobs,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=jobsmanager.raczylo.com,resources=managedjobs/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=jobsmanager.raczylo.com,resources=managedjobs/finalizers,verbs=update
|
||||
//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups="",resources=events,verbs=create;update;patch;delete;get;list;watch
|
||||
|
||||
func (r *ManagedJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
_ = log.FromContext(ctx)
|
||||
@@ -60,18 +62,24 @@ func (r *ManagedJobReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
|
||||
originalMainJobDefinition := cp.mj.DeepCopy()
|
||||
cp.generateDependencyTree()
|
||||
_, theSame, _ := pandati.CompareStructsReplaced(originalMainJobDefinition, cp.mj)
|
||||
if !theSame {
|
||||
cp.updateCRDStatusDirectly()
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
originalMainJobDefinition = cp.mj.DeepCopy()
|
||||
|
||||
// TODO: Re-enable after testing
|
||||
cp.checkRunningJobsStatus()
|
||||
cp.runPendingJobs()
|
||||
cp.checkOverallStatus()
|
||||
|
||||
_, theSame, _ := pandati.CompareStructsReplaced(originalMainJobDefinition, cp.mj)
|
||||
_, theSame, _ = pandati.CompareStructsReplaced(originalMainJobDefinition, cp.mj)
|
||||
if !theSame {
|
||||
cp.updateCRDStatusDirectly()
|
||||
}
|
||||
// fmt.Printf("Reconcile: %# v", pretty.Formatter(r.Updater))
|
||||
|
||||
cp.checkOverallStatus()
|
||||
// fmt.Printf("Reconcile: %# v", pretty.Formatter(r.Updater))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user