feat: add v2 foundation packages for config and workload abstraction

This commit is contained in:
TheiLLeniumStudios
2025-12-28 08:47:51 +01:00
parent a5d1012570
commit b9a7b8997c
12 changed files with 1757 additions and 1 deletions
+208
View File
@@ -0,0 +1,208 @@
package workload
import (
"context"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// CronJobWorkload wraps a Kubernetes CronJob.
// Note: CronJobs have a special update mechanism - instead of updating the CronJob itself,
// Reloader creates a new Job from the CronJob's template.
type CronJobWorkload struct {
cronjob *batchv1.CronJob
}
// NewCronJobWorkload creates a new CronJobWorkload.
func NewCronJobWorkload(c *batchv1.CronJob) *CronJobWorkload {
return &CronJobWorkload{cronjob: c}
}
// Ensure CronJobWorkload implements WorkloadAccessor.
var _ WorkloadAccessor = (*CronJobWorkload)(nil)
func (w *CronJobWorkload) Kind() Kind {
return KindCronJob
}
func (w *CronJobWorkload) GetObject() client.Object {
return w.cronjob
}
func (w *CronJobWorkload) GetName() string {
return w.cronjob.Name
}
func (w *CronJobWorkload) GetNamespace() string {
return w.cronjob.Namespace
}
func (w *CronJobWorkload) GetAnnotations() map[string]string {
return w.cronjob.Annotations
}
// GetPodTemplateAnnotations returns annotations from the JobTemplate's pod template.
func (w *CronJobWorkload) GetPodTemplateAnnotations() map[string]string {
if w.cronjob.Spec.JobTemplate.Spec.Template.Annotations == nil {
w.cronjob.Spec.JobTemplate.Spec.Template.Annotations = make(map[string]string)
}
return w.cronjob.Spec.JobTemplate.Spec.Template.Annotations
}
func (w *CronJobWorkload) SetPodTemplateAnnotation(key, value string) {
if w.cronjob.Spec.JobTemplate.Spec.Template.Annotations == nil {
w.cronjob.Spec.JobTemplate.Spec.Template.Annotations = make(map[string]string)
}
w.cronjob.Spec.JobTemplate.Spec.Template.Annotations[key] = value
}
func (w *CronJobWorkload) GetContainers() []corev1.Container {
return w.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers
}
func (w *CronJobWorkload) SetContainers(containers []corev1.Container) {
w.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers = containers
}
func (w *CronJobWorkload) GetInitContainers() []corev1.Container {
return w.cronjob.Spec.JobTemplate.Spec.Template.Spec.InitContainers
}
func (w *CronJobWorkload) SetInitContainers(containers []corev1.Container) {
w.cronjob.Spec.JobTemplate.Spec.Template.Spec.InitContainers = containers
}
func (w *CronJobWorkload) GetVolumes() []corev1.Volume {
return w.cronjob.Spec.JobTemplate.Spec.Template.Spec.Volumes
}
// Update for CronJob is a no-op - use CreateJobFromCronJob instead.
// CronJobs trigger reloads by creating a new Job from their template.
func (w *CronJobWorkload) Update(ctx context.Context, c client.Client) error {
// CronJobs don't get updated directly - a new Job is created instead
// This is handled by the reload package's special CronJob logic
return nil
}
func (w *CronJobWorkload) DeepCopy() Workload {
return &CronJobWorkload{cronjob: w.cronjob.DeepCopy()}
}
func (w *CronJobWorkload) GetEnvFromSources() []corev1.EnvFromSource {
var sources []corev1.EnvFromSource
for _, container := range w.cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers {
sources = append(sources, container.EnvFrom...)
}
for _, container := range w.cronjob.Spec.JobTemplate.Spec.Template.Spec.InitContainers {
sources = append(sources, container.EnvFrom...)
}
return sources
}
func (w *CronJobWorkload) UsesConfigMap(name string) bool {
spec := &w.cronjob.Spec.JobTemplate.Spec.Template.Spec
// Check volumes
for _, vol := range spec.Volumes {
if vol.ConfigMap != nil && vol.ConfigMap.Name == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.ConfigMap != nil && source.ConfigMap.Name == name {
return true
}
}
}
}
// Check containers
for _, container := range spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *CronJobWorkload) UsesSecret(name string) bool {
spec := &w.cronjob.Spec.JobTemplate.Spec.Template.Spec
// Check volumes
for _, vol := range spec.Volumes {
if vol.Secret != nil && vol.Secret.SecretName == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.Secret != nil && source.Secret.Name == name {
return true
}
}
}
}
// Check containers
for _, container := range spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *CronJobWorkload) GetOwnerReferences() []metav1.OwnerReference {
return w.cronjob.OwnerReferences
}
// GetCronJob returns the underlying CronJob for special handling.
func (w *CronJobWorkload) GetCronJob() *batchv1.CronJob {
return w.cronjob
}
+192
View File
@@ -0,0 +1,192 @@
package workload
import (
"context"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// DaemonSetWorkload wraps a Kubernetes DaemonSet.
type DaemonSetWorkload struct {
daemonset *appsv1.DaemonSet
}
// NewDaemonSetWorkload creates a new DaemonSetWorkload.
func NewDaemonSetWorkload(d *appsv1.DaemonSet) *DaemonSetWorkload {
return &DaemonSetWorkload{daemonset: d}
}
// Ensure DaemonSetWorkload implements WorkloadAccessor.
var _ WorkloadAccessor = (*DaemonSetWorkload)(nil)
func (w *DaemonSetWorkload) Kind() Kind {
return KindDaemonSet
}
func (w *DaemonSetWorkload) GetObject() client.Object {
return w.daemonset
}
func (w *DaemonSetWorkload) GetName() string {
return w.daemonset.Name
}
func (w *DaemonSetWorkload) GetNamespace() string {
return w.daemonset.Namespace
}
func (w *DaemonSetWorkload) GetAnnotations() map[string]string {
return w.daemonset.Annotations
}
func (w *DaemonSetWorkload) GetPodTemplateAnnotations() map[string]string {
if w.daemonset.Spec.Template.Annotations == nil {
w.daemonset.Spec.Template.Annotations = make(map[string]string)
}
return w.daemonset.Spec.Template.Annotations
}
func (w *DaemonSetWorkload) SetPodTemplateAnnotation(key, value string) {
if w.daemonset.Spec.Template.Annotations == nil {
w.daemonset.Spec.Template.Annotations = make(map[string]string)
}
w.daemonset.Spec.Template.Annotations[key] = value
}
func (w *DaemonSetWorkload) GetContainers() []corev1.Container {
return w.daemonset.Spec.Template.Spec.Containers
}
func (w *DaemonSetWorkload) SetContainers(containers []corev1.Container) {
w.daemonset.Spec.Template.Spec.Containers = containers
}
func (w *DaemonSetWorkload) GetInitContainers() []corev1.Container {
return w.daemonset.Spec.Template.Spec.InitContainers
}
func (w *DaemonSetWorkload) SetInitContainers(containers []corev1.Container) {
w.daemonset.Spec.Template.Spec.InitContainers = containers
}
func (w *DaemonSetWorkload) GetVolumes() []corev1.Volume {
return w.daemonset.Spec.Template.Spec.Volumes
}
func (w *DaemonSetWorkload) Update(ctx context.Context, c client.Client) error {
return c.Update(ctx, w.daemonset)
}
func (w *DaemonSetWorkload) DeepCopy() Workload {
return &DaemonSetWorkload{daemonset: w.daemonset.DeepCopy()}
}
func (w *DaemonSetWorkload) GetEnvFromSources() []corev1.EnvFromSource {
var sources []corev1.EnvFromSource
for _, container := range w.daemonset.Spec.Template.Spec.Containers {
sources = append(sources, container.EnvFrom...)
}
for _, container := range w.daemonset.Spec.Template.Spec.InitContainers {
sources = append(sources, container.EnvFrom...)
}
return sources
}
func (w *DaemonSetWorkload) UsesConfigMap(name string) bool {
// Check volumes
for _, vol := range w.daemonset.Spec.Template.Spec.Volumes {
if vol.ConfigMap != nil && vol.ConfigMap.Name == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.ConfigMap != nil && source.ConfigMap.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.daemonset.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.daemonset.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *DaemonSetWorkload) UsesSecret(name string) bool {
// Check volumes
for _, vol := range w.daemonset.Spec.Template.Spec.Volumes {
if vol.Secret != nil && vol.Secret.SecretName == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.Secret != nil && source.Secret.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.daemonset.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.daemonset.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *DaemonSetWorkload) GetOwnerReferences() []metav1.OwnerReference {
return w.daemonset.OwnerReferences
}
+194
View File
@@ -0,0 +1,194 @@
package workload
import (
"context"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// DeploymentWorkload wraps a Kubernetes Deployment.
type DeploymentWorkload struct {
deployment *appsv1.Deployment
}
// NewDeploymentWorkload creates a new DeploymentWorkload.
func NewDeploymentWorkload(d *appsv1.Deployment) *DeploymentWorkload {
return &DeploymentWorkload{deployment: d}
}
// Ensure DeploymentWorkload implements WorkloadAccessor.
var _ WorkloadAccessor = (*DeploymentWorkload)(nil)
func (w *DeploymentWorkload) Kind() Kind {
return KindDeployment
}
func (w *DeploymentWorkload) GetObject() client.Object {
return w.deployment
}
func (w *DeploymentWorkload) GetName() string {
return w.deployment.Name
}
func (w *DeploymentWorkload) GetNamespace() string {
return w.deployment.Namespace
}
func (w *DeploymentWorkload) GetAnnotations() map[string]string {
return w.deployment.Annotations
}
func (w *DeploymentWorkload) GetPodTemplateAnnotations() map[string]string {
if w.deployment.Spec.Template.Annotations == nil {
w.deployment.Spec.Template.Annotations = make(map[string]string)
}
return w.deployment.Spec.Template.Annotations
}
func (w *DeploymentWorkload) SetPodTemplateAnnotation(key, value string) {
if w.deployment.Spec.Template.Annotations == nil {
w.deployment.Spec.Template.Annotations = make(map[string]string)
}
w.deployment.Spec.Template.Annotations[key] = value
}
func (w *DeploymentWorkload) GetContainers() []corev1.Container {
return w.deployment.Spec.Template.Spec.Containers
}
func (w *DeploymentWorkload) SetContainers(containers []corev1.Container) {
w.deployment.Spec.Template.Spec.Containers = containers
}
func (w *DeploymentWorkload) GetInitContainers() []corev1.Container {
return w.deployment.Spec.Template.Spec.InitContainers
}
func (w *DeploymentWorkload) SetInitContainers(containers []corev1.Container) {
w.deployment.Spec.Template.Spec.InitContainers = containers
}
func (w *DeploymentWorkload) GetVolumes() []corev1.Volume {
return w.deployment.Spec.Template.Spec.Volumes
}
func (w *DeploymentWorkload) Update(ctx context.Context, c client.Client) error {
return c.Update(ctx, w.deployment)
}
func (w *DeploymentWorkload) DeepCopy() Workload {
return &DeploymentWorkload{deployment: w.deployment.DeepCopy()}
}
func (w *DeploymentWorkload) GetEnvFromSources() []corev1.EnvFromSource {
var sources []corev1.EnvFromSource
for _, container := range w.deployment.Spec.Template.Spec.Containers {
sources = append(sources, container.EnvFrom...)
}
for _, container := range w.deployment.Spec.Template.Spec.InitContainers {
sources = append(sources, container.EnvFrom...)
}
return sources
}
func (w *DeploymentWorkload) UsesConfigMap(name string) bool {
// Check volumes
for _, vol := range w.deployment.Spec.Template.Spec.Volumes {
if vol.ConfigMap != nil && vol.ConfigMap.Name == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.ConfigMap != nil && source.ConfigMap.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.deployment.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
// Check individual env vars
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.deployment.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *DeploymentWorkload) UsesSecret(name string) bool {
// Check volumes
for _, vol := range w.deployment.Spec.Template.Spec.Volumes {
if vol.Secret != nil && vol.Secret.SecretName == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.Secret != nil && source.Secret.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.deployment.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
// Check individual env vars
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.deployment.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *DeploymentWorkload) GetOwnerReferences() []metav1.OwnerReference {
return w.deployment.OwnerReferences
}
+120
View File
@@ -0,0 +1,120 @@
// Package workload provides an abstraction layer for Kubernetes workload types.
// It allows uniform handling of Deployments, DaemonSets, StatefulSets, Jobs, CronJobs, and Argo Rollouts.
//
// Note: Jobs and CronJobs have special update mechanisms:
// - Job: deleted and recreated with the same spec
// - CronJob: a new Job is created from the CronJob's template
package workload
import (
"context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Kind represents the type of workload.
type Kind string
const (
KindDeployment Kind = "Deployment"
KindDaemonSet Kind = "DaemonSet"
KindStatefulSet Kind = "StatefulSet"
KindArgoRollout Kind = "Rollout"
KindJob Kind = "Job"
KindCronJob Kind = "CronJob"
)
// Workload provides a uniform interface for managing Kubernetes workloads.
// All implementations must be safe for concurrent use.
type Workload interface {
// Kind returns the workload type.
Kind() Kind
// GetObject returns the underlying Kubernetes object.
GetObject() client.Object
// GetName returns the workload name.
GetName() string
// GetNamespace returns the workload namespace.
GetNamespace() string
// GetAnnotations returns the workload's annotations.
GetAnnotations() map[string]string
// GetPodTemplateAnnotations returns annotations from the pod template spec.
GetPodTemplateAnnotations() map[string]string
// SetPodTemplateAnnotation sets an annotation on the pod template.
SetPodTemplateAnnotation(key, value string)
// GetContainers returns all containers (including init containers).
GetContainers() []corev1.Container
// SetContainers updates the containers.
SetContainers(containers []corev1.Container)
// GetInitContainers returns all init containers.
GetInitContainers() []corev1.Container
// SetInitContainers updates the init containers.
SetInitContainers(containers []corev1.Container)
// GetVolumes returns the pod template volumes.
GetVolumes() []corev1.Volume
// Update persists changes to the workload.
Update(ctx context.Context, c client.Client) error
// DeepCopy returns a deep copy of the workload.
DeepCopy() Workload
}
// Accessor provides read-only access to workload configuration.
// Use this interface when you only need to inspect workload state.
type Accessor interface {
// Kind returns the workload type.
Kind() Kind
// GetName returns the workload name.
GetName() string
// GetNamespace returns the workload namespace.
GetNamespace() string
// GetAnnotations returns the workload's annotations.
GetAnnotations() map[string]string
// GetPodTemplateAnnotations returns annotations from the pod template spec.
GetPodTemplateAnnotations() map[string]string
// GetContainers returns all containers (including init containers).
GetContainers() []corev1.Container
// GetInitContainers returns all init containers.
GetInitContainers() []corev1.Container
// GetVolumes returns the pod template volumes.
GetVolumes() []corev1.Volume
// GetEnvFromSources returns all envFrom sources from all containers.
GetEnvFromSources() []corev1.EnvFromSource
// UsesConfigMap checks if the workload uses a specific ConfigMap.
UsesConfigMap(name string) bool
// UsesSecret checks if the workload uses a specific Secret.
UsesSecret(name string) bool
// GetOwnerReferences returns the owner references of the workload.
GetOwnerReferences() []metav1.OwnerReference
}
// WorkloadAccessor provides both Workload and Accessor interfaces.
// This is the primary type returned by the registry.
type WorkloadAccessor interface {
Workload
Accessor
}
+203
View File
@@ -0,0 +1,203 @@
package workload
import (
"context"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// JobWorkload wraps a Kubernetes Job.
// Note: Jobs have a special update mechanism - instead of updating the Job,
// Reloader deletes and recreates it with the same spec.
type JobWorkload struct {
job *batchv1.Job
}
// NewJobWorkload creates a new JobWorkload.
func NewJobWorkload(j *batchv1.Job) *JobWorkload {
return &JobWorkload{job: j}
}
// Ensure JobWorkload implements WorkloadAccessor.
var _ WorkloadAccessor = (*JobWorkload)(nil)
func (w *JobWorkload) Kind() Kind {
return KindJob
}
func (w *JobWorkload) GetObject() client.Object {
return w.job
}
func (w *JobWorkload) GetName() string {
return w.job.Name
}
func (w *JobWorkload) GetNamespace() string {
return w.job.Namespace
}
func (w *JobWorkload) GetAnnotations() map[string]string {
return w.job.Annotations
}
func (w *JobWorkload) GetPodTemplateAnnotations() map[string]string {
if w.job.Spec.Template.Annotations == nil {
w.job.Spec.Template.Annotations = make(map[string]string)
}
return w.job.Spec.Template.Annotations
}
func (w *JobWorkload) SetPodTemplateAnnotation(key, value string) {
if w.job.Spec.Template.Annotations == nil {
w.job.Spec.Template.Annotations = make(map[string]string)
}
w.job.Spec.Template.Annotations[key] = value
}
func (w *JobWorkload) GetContainers() []corev1.Container {
return w.job.Spec.Template.Spec.Containers
}
func (w *JobWorkload) SetContainers(containers []corev1.Container) {
w.job.Spec.Template.Spec.Containers = containers
}
func (w *JobWorkload) GetInitContainers() []corev1.Container {
return w.job.Spec.Template.Spec.InitContainers
}
func (w *JobWorkload) SetInitContainers(containers []corev1.Container) {
w.job.Spec.Template.Spec.InitContainers = containers
}
func (w *JobWorkload) GetVolumes() []corev1.Volume {
return w.job.Spec.Template.Spec.Volumes
}
// Update for Job is a no-op - use RecreateJob instead.
// Jobs trigger reloads by being deleted and recreated.
func (w *JobWorkload) Update(ctx context.Context, c client.Client) error {
// Jobs don't get updated directly - they are deleted and recreated
// This is handled by the reload package's special Job logic
return nil
}
func (w *JobWorkload) DeepCopy() Workload {
return &JobWorkload{job: w.job.DeepCopy()}
}
func (w *JobWorkload) GetEnvFromSources() []corev1.EnvFromSource {
var sources []corev1.EnvFromSource
for _, container := range w.job.Spec.Template.Spec.Containers {
sources = append(sources, container.EnvFrom...)
}
for _, container := range w.job.Spec.Template.Spec.InitContainers {
sources = append(sources, container.EnvFrom...)
}
return sources
}
func (w *JobWorkload) UsesConfigMap(name string) bool {
// Check volumes
for _, vol := range w.job.Spec.Template.Spec.Volumes {
if vol.ConfigMap != nil && vol.ConfigMap.Name == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.ConfigMap != nil && source.ConfigMap.Name == name {
return true
}
}
}
}
// Check containers
for _, container := range w.job.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.job.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *JobWorkload) UsesSecret(name string) bool {
// Check volumes
for _, vol := range w.job.Spec.Template.Spec.Volumes {
if vol.Secret != nil && vol.Secret.SecretName == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.Secret != nil && source.Secret.Name == name {
return true
}
}
}
}
// Check containers
for _, container := range w.job.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.job.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *JobWorkload) GetOwnerReferences() []metav1.OwnerReference {
return w.job.OwnerReferences
}
// GetJob returns the underlying Job for special handling.
func (w *JobWorkload) GetJob() *batchv1.Job {
return w.job
}
+74
View File
@@ -0,0 +1,74 @@
package workload
import (
"fmt"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Registry provides factory methods for creating Workload instances.
type Registry struct {
argoRolloutsEnabled bool
}
// NewRegistry creates a new workload registry.
func NewRegistry(argoRolloutsEnabled bool) *Registry {
return &Registry{
argoRolloutsEnabled: argoRolloutsEnabled,
}
}
// SupportedKinds returns all supported workload kinds.
func (r *Registry) SupportedKinds() []Kind {
kinds := []Kind{
KindDeployment,
KindDaemonSet,
KindStatefulSet,
KindJob,
KindCronJob,
}
if r.argoRolloutsEnabled {
kinds = append(kinds, KindArgoRollout)
}
return kinds
}
// FromObject creates a WorkloadAccessor from a Kubernetes object.
func (r *Registry) FromObject(obj client.Object) (WorkloadAccessor, error) {
switch o := obj.(type) {
case *appsv1.Deployment:
return NewDeploymentWorkload(o), nil
case *appsv1.DaemonSet:
return NewDaemonSetWorkload(o), nil
case *appsv1.StatefulSet:
return NewStatefulSetWorkload(o), nil
case *batchv1.Job:
return NewJobWorkload(o), nil
case *batchv1.CronJob:
return NewCronJobWorkload(o), nil
default:
return nil, fmt.Errorf("unsupported object type: %T", obj)
}
}
// KindFromString converts a string to a Kind.
func KindFromString(s string) (Kind, error) {
switch s {
case "Deployment", "deployment", "deployments":
return KindDeployment, nil
case "DaemonSet", "daemonset", "daemonsets":
return KindDaemonSet, nil
case "StatefulSet", "statefulset", "statefulsets":
return KindStatefulSet, nil
case "Rollout", "rollout", "rollouts":
return KindArgoRollout, nil
case "Job", "job", "jobs":
return KindJob, nil
case "CronJob", "cronjob", "cronjobs":
return KindCronJob, nil
default:
return "", fmt.Errorf("unknown workload kind: %s", s)
}
}
+192
View File
@@ -0,0 +1,192 @@
package workload
import (
"context"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// StatefulSetWorkload wraps a Kubernetes StatefulSet.
type StatefulSetWorkload struct {
statefulset *appsv1.StatefulSet
}
// NewStatefulSetWorkload creates a new StatefulSetWorkload.
func NewStatefulSetWorkload(s *appsv1.StatefulSet) *StatefulSetWorkload {
return &StatefulSetWorkload{statefulset: s}
}
// Ensure StatefulSetWorkload implements WorkloadAccessor.
var _ WorkloadAccessor = (*StatefulSetWorkload)(nil)
func (w *StatefulSetWorkload) Kind() Kind {
return KindStatefulSet
}
func (w *StatefulSetWorkload) GetObject() client.Object {
return w.statefulset
}
func (w *StatefulSetWorkload) GetName() string {
return w.statefulset.Name
}
func (w *StatefulSetWorkload) GetNamespace() string {
return w.statefulset.Namespace
}
func (w *StatefulSetWorkload) GetAnnotations() map[string]string {
return w.statefulset.Annotations
}
func (w *StatefulSetWorkload) GetPodTemplateAnnotations() map[string]string {
if w.statefulset.Spec.Template.Annotations == nil {
w.statefulset.Spec.Template.Annotations = make(map[string]string)
}
return w.statefulset.Spec.Template.Annotations
}
func (w *StatefulSetWorkload) SetPodTemplateAnnotation(key, value string) {
if w.statefulset.Spec.Template.Annotations == nil {
w.statefulset.Spec.Template.Annotations = make(map[string]string)
}
w.statefulset.Spec.Template.Annotations[key] = value
}
func (w *StatefulSetWorkload) GetContainers() []corev1.Container {
return w.statefulset.Spec.Template.Spec.Containers
}
func (w *StatefulSetWorkload) SetContainers(containers []corev1.Container) {
w.statefulset.Spec.Template.Spec.Containers = containers
}
func (w *StatefulSetWorkload) GetInitContainers() []corev1.Container {
return w.statefulset.Spec.Template.Spec.InitContainers
}
func (w *StatefulSetWorkload) SetInitContainers(containers []corev1.Container) {
w.statefulset.Spec.Template.Spec.InitContainers = containers
}
func (w *StatefulSetWorkload) GetVolumes() []corev1.Volume {
return w.statefulset.Spec.Template.Spec.Volumes
}
func (w *StatefulSetWorkload) Update(ctx context.Context, c client.Client) error {
return c.Update(ctx, w.statefulset)
}
func (w *StatefulSetWorkload) DeepCopy() Workload {
return &StatefulSetWorkload{statefulset: w.statefulset.DeepCopy()}
}
func (w *StatefulSetWorkload) GetEnvFromSources() []corev1.EnvFromSource {
var sources []corev1.EnvFromSource
for _, container := range w.statefulset.Spec.Template.Spec.Containers {
sources = append(sources, container.EnvFrom...)
}
for _, container := range w.statefulset.Spec.Template.Spec.InitContainers {
sources = append(sources, container.EnvFrom...)
}
return sources
}
func (w *StatefulSetWorkload) UsesConfigMap(name string) bool {
// Check volumes
for _, vol := range w.statefulset.Spec.Template.Spec.Volumes {
if vol.ConfigMap != nil && vol.ConfigMap.Name == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.ConfigMap != nil && source.ConfigMap.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.statefulset.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.statefulset.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *StatefulSetWorkload) UsesSecret(name string) bool {
// Check volumes
for _, vol := range w.statefulset.Spec.Template.Spec.Volumes {
if vol.Secret != nil && vol.Secret.SecretName == name {
return true
}
if vol.Projected != nil {
for _, source := range vol.Projected.Sources {
if source.Secret != nil && source.Secret.Name == name {
return true
}
}
}
}
// Check envFrom
for _, container := range w.statefulset.Spec.Template.Spec.Containers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
// Check init containers
for _, container := range w.statefulset.Spec.Template.Spec.InitContainers {
for _, envFrom := range container.EnvFrom {
if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name {
return true
}
}
for _, env := range container.Env {
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name {
return true
}
}
}
return false
}
func (w *StatefulSetWorkload) GetOwnerReferences() []metav1.OwnerReference {
return w.statefulset.OwnerReferences
}