Files
jobs-manager-operator/controllers/helpers.go
T
lukaszraczylo 2b36071647 Multiple fixes (#29)
* Multiple fixes

- add goreleaser to the build / release process
- add kubectl plugin for job graphs visualization
- add installation scripts
- update dependencies

* Update the release & CRD content.

* Next set of improvements.

  Code Quality

  - Label constants: Added LabelWorkflowName, LabelGroupName, LabelJobName, LabelJobID in controllers/definitions.go
  - Removed commented debug code: Cleaned up dead code from multiple files
  - Removed unused dependencyTree field: Cleaned connPackage struct
  - Fixed snake_case variables: Changed to camelCase (runGroup, groupDep, runJob, jobDep, k8sJob)

  Kubernetes Best Practices

  - Finalizers: Implemented handleDeletion() and deleteChildJobs() for proper cleanup
  - Status enum validation: Added +kubebuilder:validation:Enum=pending;running;succeeded;failed;aborted
  - ImagePullPolicy default: Created getImagePullPolicy() helper that defaults to IfNotPresent
  - Resource limits support: Added Resources *corev1.ResourceRequirements to ManagedJobParameters

  Observability

  - Prometheus metrics: Created controllers/metrics.go with counters (jobs created/succeeded/failed), histogram (reconciliation duration), and gauge (active jobs)
  - Structured logging: Added logger field to connPackage, used context-based logging throughout

  Configuration

  - Leader election ID: Made configurable via --leader-election-id flag
  - Development mode: Made configurable via --dev-mode flag and LOG_LEVEL env var

  Performance

  - Dependency lookup optimization: Changed from O(n*m) to O(1) using lookup maps (jobDepMap, groupDepMap)
  - Reconciliation backoff: Added RequeueAfter: 30*time.Second when workflow is running

  Documentation & Testing

  - Godoc documentation: Added comprehensive comments to API types and controller
  - Unit tests: Added helpers_test.go with tests for all helper functions
  - Integration tests: Added managedjob_controller_test.go with Ginkgo/Gomega tests

* Add the helm chart release.

* Add reasonable test coverage.
2025-12-17 22:33:23 +00:00

101 lines
2.9 KiB
Go

package controllers
import (
"context"
"strings"
"sync"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
jobsmanagerv1beta1 "raczylo.com/jobs-manager-operator/api/v1beta1"
ctrl "sigs.k8s.io/controller-runtime"
)
func jobNameGenerator(name ...string) string {
// join name parts with "-" and convert to lowercase
return strings.ToLower(strings.Join(name, "-"))
}
type connPackage struct {
r *ManagedJobReconciler
ctx context.Context
req ctrl.Request
mtx sync.Mutex
mj *jobsmanagerv1beta1.ManagedJob
logger logr.Logger
// jobDepMap maps job names to dependencies that reference them (for O(1) lookup)
jobDepMap map[string][]*jobsmanagerv1beta1.ManagedJobDependencies
// groupDepMap maps group names to dependencies that reference them (for O(1) lookup)
groupDepMap map[string][]*jobsmanagerv1beta1.ManagedJobDependencies
}
// buildDependencyMaps constructs lookup maps for efficient dependency status updates.
// This converts O(n*m) lookups to O(1) by mapping job/group names to their dependents.
func (cp *connPackage) buildDependencyMaps() {
cp.jobDepMap = make(map[string][]*jobsmanagerv1beta1.ManagedJobDependencies)
cp.groupDepMap = make(map[string][]*jobsmanagerv1beta1.ManagedJobDependencies)
for _, group := range cp.mj.Spec.Groups {
// Map group dependencies
for _, dep := range group.Dependencies {
cp.groupDepMap[dep.Name] = append(cp.groupDepMap[dep.Name], dep)
}
// Map job dependencies
for _, job := range group.Jobs {
for _, dep := range job.Dependencies {
cp.jobDepMap[dep.Name] = append(cp.jobDepMap[dep.Name], dep)
}
}
}
}
func (cp *connPackage) getOwnerReference() (metav1.OwnerReference, error) {
mj := &jobsmanagerv1beta1.ManagedJob{}
err := cp.r.Client.Get(cp.ctx, cp.req.NamespacedName, mj)
if err != nil {
return metav1.OwnerReference{}, err
}
t := true
return metav1.OwnerReference{
APIVersion: jobsmanagerv1beta1.GroupVersion.String(),
Kind: "ManagedJob",
Name: mj.Name,
UID: mj.UID,
Controller: &t,
}, nil
}
func (cp *connPackage) updateCRDStatusDirectly() error {
cp.mtx.Lock()
defer cp.mtx.Unlock()
if err := cp.r.Update(cp.ctx, cp.mj); err != nil {
cp.logger.Error(err, "Unable to update ManagedJob status directly")
return err
}
if err := cp.r.Client.Get(cp.ctx, cp.req.NamespacedName, cp.mj); err != nil {
cp.logger.Error(err, "Unable to get updated ManagedJob")
return err
}
return nil
}
// getImagePullPolicy returns the specified pull policy or IfNotPresent as default
func getImagePullPolicy(policy string) corev1.PullPolicy {
if policy == "" {
return corev1.PullIfNotPresent
}
return corev1.PullPolicy(policy)
}
// getResources returns the resource requirements or empty requirements if nil
func getResources(resources *corev1.ResourceRequirements) corev1.ResourceRequirements {
if resources == nil {
return corev1.ResourceRequirements{}
}
return *resources
}