mirror of
https://github.com/stakater/Reloader.git
synced 2026-05-16 21:56:55 +00:00
186 lines
6.9 KiB
Go
186 lines
6.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
)
|
|
|
|
// WorkloadType represents the type of Kubernetes workload.
|
|
type WorkloadType string
|
|
|
|
const (
|
|
WorkloadDeployment WorkloadType = "Deployment"
|
|
WorkloadDaemonSet WorkloadType = "DaemonSet"
|
|
WorkloadStatefulSet WorkloadType = "StatefulSet"
|
|
WorkloadCronJob WorkloadType = "CronJob"
|
|
WorkloadJob WorkloadType = "Job"
|
|
WorkloadArgoRollout WorkloadType = "ArgoRollout"
|
|
WorkloadDeploymentConfig WorkloadType = "DeploymentConfig"
|
|
)
|
|
|
|
// ReloadStrategy represents the reload strategy used by Reloader.
|
|
type ReloadStrategy string
|
|
|
|
const (
|
|
StrategyAnnotations ReloadStrategy = "annotations"
|
|
StrategyEnvVars ReloadStrategy = "envvars"
|
|
)
|
|
|
|
// WorkloadConfig holds configuration for workload creation.
|
|
type WorkloadConfig struct {
|
|
ConfigMapName string
|
|
SecretName string
|
|
SPCName string
|
|
Annotations map[string]string // Annotations for workload metadata (e.g., Deployment.metadata.annotations)
|
|
PodTemplateAnnotations map[string]string // Annotations for pod template metadata (e.g., Deployment.spec.template.metadata.annotations)
|
|
UseConfigMapEnvFrom bool
|
|
UseSecretEnvFrom bool
|
|
UseConfigMapVolume bool
|
|
UseSecretVolume bool
|
|
UseProjectedVolume bool
|
|
UseConfigMapKeyRef bool
|
|
UseSecretKeyRef bool
|
|
UseInitContainer bool
|
|
UseInitContainerVolume bool
|
|
UseCSIVolume bool
|
|
UseInitContainerCSI bool
|
|
ConfigMapKey string
|
|
SecretKey string
|
|
EnvVarName string
|
|
MultipleContainers int
|
|
}
|
|
|
|
// WorkloadAdapter provides a unified interface for all workload types.
|
|
// This allows tests to be parameterized across different workload types.
|
|
type WorkloadAdapter interface {
|
|
// Type returns the workload type.
|
|
Type() WorkloadType
|
|
|
|
// Create creates the workload with the given config.
|
|
Create(ctx context.Context, namespace, name string, cfg WorkloadConfig) error
|
|
|
|
// Delete removes the workload.
|
|
Delete(ctx context.Context, namespace, name string) error
|
|
|
|
// WaitReady waits for the workload to be ready.
|
|
WaitReady(ctx context.Context, namespace, name string, timeout time.Duration) error
|
|
|
|
// WaitReloaded waits for the workload to have the reload annotation.
|
|
// Returns true if the annotation was found, false if timeout occurred.
|
|
WaitReloaded(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error)
|
|
|
|
// WaitEnvVar waits for the workload to have a STAKATER_ env var (for envvars strategy).
|
|
// Returns true if the env var was found, false if timeout occurred.
|
|
WaitEnvVar(ctx context.Context, namespace, name, prefix string, timeout time.Duration) (bool, error)
|
|
|
|
// SupportsEnvVarStrategy returns true if the workload supports env var reload strategy.
|
|
// CronJob does not support this as it uses job creation instead.
|
|
SupportsEnvVarStrategy() bool
|
|
|
|
// RequiresSpecialHandling returns true for workloads that need special handling.
|
|
// For example, CronJob triggers a new job instead of rolling restart.
|
|
RequiresSpecialHandling() bool
|
|
|
|
// GetPodTemplateAnnotation returns the value of a pod template annotation.
|
|
// This is useful for tests that need to compare annotation values before/after updates.
|
|
GetPodTemplateAnnotation(ctx context.Context, namespace, name, annotationKey string) (string, error)
|
|
}
|
|
|
|
// Pausable is implemented by workloads that support pause/unpause.
|
|
// Currently only Deployment supports this capability.
|
|
type Pausable interface {
|
|
WaitPaused(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error)
|
|
WaitUnpaused(ctx context.Context, namespace, name, annotationKey string, timeout time.Duration) (bool, error)
|
|
}
|
|
|
|
// Recreatable is implemented by workloads that are recreated instead of updated.
|
|
// Currently only Job supports this capability (Jobs are immutable, so Reloader recreates them).
|
|
type Recreatable interface {
|
|
GetOriginalUID(ctx context.Context, namespace, name string) (string, error)
|
|
WaitRecreated(ctx context.Context, namespace, name, originalUID string, timeout time.Duration) (string, bool, error)
|
|
}
|
|
|
|
// JobTriggerer is implemented by workloads that trigger jobs on reload.
|
|
// Currently only CronJob supports this capability.
|
|
type JobTriggerer interface {
|
|
WaitForTriggeredJob(ctx context.Context, namespace, name string, timeout time.Duration) (bool, error)
|
|
}
|
|
|
|
// RestartAtSupporter is implemented by workloads that support the restartAt field.
|
|
// Currently only ArgoRollout supports this capability.
|
|
type RestartAtSupporter interface {
|
|
WaitRestartAt(ctx context.Context, namespace, name string, timeout time.Duration) (bool, error)
|
|
}
|
|
|
|
// AdapterRegistry holds adapters for all workload types.
|
|
type AdapterRegistry struct {
|
|
kubeClient kubernetes.Interface
|
|
adapters map[WorkloadType]WorkloadAdapter
|
|
}
|
|
|
|
// NewAdapterRegistry creates a new adapter registry with all standard adapters.
|
|
func NewAdapterRegistry(kubeClient kubernetes.Interface) *AdapterRegistry {
|
|
r := &AdapterRegistry{
|
|
kubeClient: kubeClient,
|
|
adapters: make(map[WorkloadType]WorkloadAdapter),
|
|
}
|
|
|
|
r.adapters[WorkloadDeployment] = NewDeploymentAdapter(kubeClient)
|
|
r.adapters[WorkloadDaemonSet] = NewDaemonSetAdapter(kubeClient)
|
|
r.adapters[WorkloadStatefulSet] = NewStatefulSetAdapter(kubeClient)
|
|
r.adapters[WorkloadCronJob] = NewCronJobAdapter(kubeClient)
|
|
r.adapters[WorkloadJob] = NewJobAdapter(kubeClient)
|
|
|
|
return r
|
|
}
|
|
|
|
// RegisterAdapter registers a custom adapter for a workload type.
|
|
func (r *AdapterRegistry) RegisterAdapter(adapter WorkloadAdapter) {
|
|
r.adapters[adapter.Type()] = adapter
|
|
}
|
|
|
|
// Get returns the adapter for the given workload type.
|
|
// Returns nil if the adapter is not registered.
|
|
func (r *AdapterRegistry) Get(wt WorkloadType) WorkloadAdapter {
|
|
return r.adapters[wt]
|
|
}
|
|
|
|
// GetStandardWorkloads returns the standard workload types that are always available.
|
|
func (r *AdapterRegistry) GetStandardWorkloads() []WorkloadType {
|
|
return []WorkloadType{
|
|
WorkloadDeployment,
|
|
WorkloadDaemonSet,
|
|
WorkloadStatefulSet,
|
|
}
|
|
}
|
|
|
|
// GetAllWorkloads returns all registered workload types in a canonical, deterministic order.
|
|
// Map iteration order in Go is non-deterministic, so this uses a fixed ordering to ensure
|
|
// consistent test parameterization across runs.
|
|
func (r *AdapterRegistry) GetAllWorkloads() []WorkloadType {
|
|
canonical := []WorkloadType{
|
|
WorkloadDeployment, WorkloadDaemonSet, WorkloadStatefulSet,
|
|
WorkloadCronJob, WorkloadJob, WorkloadArgoRollout, WorkloadDeploymentConfig,
|
|
}
|
|
result := make([]WorkloadType, 0, len(r.adapters))
|
|
for _, wt := range canonical {
|
|
if _, ok := r.adapters[wt]; ok {
|
|
result = append(result, wt)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetEnvVarWorkloads returns workload types that support env var reload strategy.
|
|
func (r *AdapterRegistry) GetEnvVarWorkloads() []WorkloadType {
|
|
result := make([]WorkloadType, 0)
|
|
for wt, adapter := range r.adapters {
|
|
if adapter.SupportsEnvVarStrategy() {
|
|
result = append(result, wt)
|
|
}
|
|
}
|
|
return result
|
|
}
|