Documentation
¶
Overview ¶
Package kubeutil provides low-level Kubernetes utilities for controllers.
This package consolidates utility functions used across BubuStack controllers for common Kubernetes operations including:
- Environment variable construction
- Event recording
- Finalizer management
- Resource listing and deletion
- Status patching with retry
- DNS-1123 name generation
- Duration configuration
- Retry and periodic execution loops
- JSON merge utilities
- RBAC management
- Service management
- Validation status computation
Environment Variables ¶
Build step config environment variables for pods:
envVars := kubeutil.ConfigEnvVars(jsonBytes) kubeutil.AppendConfigEnvVar(&envVars, jsonBytes)
Event Recording ¶
Record Kubernetes events with nil-safe logging fallback:
kubeutil.RecordEvent(recorder, logger, obj, corev1.EventTypeWarning, "Failed", "error message")
Finalizer Management ¶
Ensure finalizers are present on objects:
added, err := kubeutil.EnsureFinalizer(ctx, client, obj, "my.finalizer.io")
Resource Listing and Deletion ¶
List resources with label selectors:
err := kubeutil.ListByLabels(ctx, client, namespace, labels, &podList)
Delete resources with NotFound handling:
err := kubeutil.DeleteIfExists(ctx, client, obj, logger)
Status Patching ¶
Retry status patches on conflicts:
err := kubeutil.RetryableStatusPatch(ctx, client, obj, func(o client.Object) {
o.(*v1alpha1.StepRun).Status.Phase = enums.PhaseRunning
})
Name Generation ¶
Generate DNS-1123 compliant names:
name := kubeutil.ComposeName("storyrun", "my-story", "step-1")
Duration Configuration ¶
Resolve durations with fallbacks:
timeout := kubeutil.PositiveDurationOrDefault(userTimeout, 30*time.Second) timeout := kubeutil.FirstPositiveDuration(step, story, default)
Retry and Periodic Execution ¶
Retry with exponential backoff:
err := kubeutil.Retry(ctx, kubeutil.RetryConfig{MaxAttempts: 3}, fn, retryable)
Run periodic tasks:
err := kubeutil.Periodic(ctx, 10*time.Second, fn)
JSON Merge ¶
Merge runtime.RawExtension with-blocks:
merged, err := kubeutil.MergeWithBlocks(base, overlay)
Merge string maps:
labels := kubeutil.MergeStringMaps(base, override)
RBAC Management ¶
Ensure Roles and RoleBindings:
result, err := kubeutil.EnsureRole(ctx, client, scheme, owner, name, ns, rules, style) result, err := kubeutil.EnsureRoleBinding(ctx, client, scheme, owner, name, ns, roleRef, subjects, style)
Service Management ¶
Ensure Services with immutable field preservation:
svc, result, err := kubeutil.EnsureService(ctx, client, scheme, owner, desired)
Validation Status ¶
Compute Ready conditions from validation/binding states:
outcome := kubeutil.ComputeValidationOutcome(kubeutil.ValidationParams{
ValidationErrors: errs,
SuccessReason: "Valid",
})
Index ¶
- func AppendConfigEnvVar(envVars *[]corev1.EnvVar, raw []byte)
- func CloneServicePorts(ports []corev1.ServicePort) []corev1.ServicePort
- func ComposeName(parts ...string) string
- func ConfigEnvVars(raw []byte) []corev1.EnvVar
- func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object, logger DeleteLogger) error
- func EnsureFinalizer(ctx context.Context, c client.Client, obj client.Object, finalizer string) (bool, error)
- func EnsureRole(ctx context.Context, c client.Client, scheme *runtime.Scheme, ...) (controllerutil.OperationResult, error)
- func EnsureRoleBinding(ctx context.Context, c client.Client, scheme *runtime.Scheme, ...) (controllerutil.OperationResult, error)
- func EnsureSecretCopy(ctx context.Context, reader client.Reader, writer client.Client, ...) error
- func EnsureService(ctx context.Context, c client.Client, scheme *runtime.Scheme, ...) (*corev1.Service, controllerutil.OperationResult, error)
- func FirstPositiveDuration(values ...time.Duration) time.Duration
- func ListByLabels(ctx context.Context, c client.Client, namespace string, ...) error
- func MergeStringMaps(maps ...map[string]string) map[string]string
- func MergeWithBlocks(engramWith, stepWith *runtime.RawExtension) (*runtime.RawExtension, error)
- func Periodic(ctx context.Context, interval time.Duration, fn func(context.Context) error) error
- func PositiveDurationOrDefault(candidate, fallback time.Duration) time.Duration
- func PreserveImmutableServiceFields(current, desired *corev1.Service)
- func RecordEvent(recorder events.EventRecorder, logger logr.Logger, obj client.Object, ...)
- func Retry(ctx context.Context, cfg RetryConfig, fn func(context.Context) error, ...) error
- func RetryableStatusPatch(ctx context.Context, cl client.Client, obj client.Object, ...) error
- func RetryableStatusUpdate(ctx context.Context, cl client.Client, obj client.Object, ...) error
- func StoryRunEngramRunnerSubject(storyRun, namespace string) rbacv1.Subject
- type DeleteLogger
- type ReferenceStyle
- type RetryConfig
- type SecretCopyAuthorization
- type UpdateFunc
- type ValidationOutcome
- type ValidationParams
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AppendConfigEnvVar ¶
AppendConfigEnvVar appends the BUBU_STEP_CONFIG env var to envVars when raw is non-empty.
Arguments:
- envVars *[]corev1.EnvVar: pointer to the env var slice to append to.
- raw []byte: JSON configuration bytes.
Side Effects:
- Appends to *envVars if raw is non-empty and envVars is non-nil.
func CloneServicePorts ¶
func CloneServicePorts(ports []corev1.ServicePort) []corev1.ServicePort
CloneServicePorts returns a deep copy of the provided ServicePort slice.
Arguments:
- ports []corev1.ServicePort: ports to clone.
Returns:
- []corev1.ServicePort: cloned ports, or nil if empty.
func ComposeName ¶
ComposeName builds a DNS-1123 compliant resource name from the provided parts.
Arguments:
- parts ...string: name segments to join with hyphens.
Returns:
- string: DNS-1123 compliant name (max 63 characters).
Behavior:
- Joins parts with hyphens.
- When the result exceeds 63 characters, truncates and appends a deterministic FNV-1a hash suffix for uniqueness.
- Results are stable across reconciliations for the same inputs.
func ConfigEnvVars ¶
ConfigEnvVars returns a slice containing the BUBU_STEP_CONFIG env var when raw is non-empty, otherwise it returns nil.
Arguments:
- raw []byte: JSON configuration bytes.
Returns:
- []corev1.EnvVar: slice containing the config env var, or nil if raw is empty.
func DeleteIfExists ¶
func DeleteIfExists(ctx context.Context, c client.Client, obj client.Object, logger DeleteLogger) error
DeleteIfExists deletes the provided object using background propagation and treats IsNotFound as success so controllers can safely converge deletes.
Arguments:
- ctx context.Context: context for the delete operation.
- c client.Client: Kubernetes client.
- obj client.Object: the object to delete.
- logger DeleteLogger: optional logger for operation details.
Returns:
- error: nil on success (including NotFound), or the delete error.
func EnsureFinalizer ¶
func EnsureFinalizer(ctx context.Context, c client.Client, obj client.Object, finalizer string) (bool, error)
EnsureFinalizer adds the supplied finalizer to obj (if missing) using a merge patch.
Arguments:
- ctx context.Context: context for the patch operation.
- c client.Client: Kubernetes client.
- obj client.Object: the object to add the finalizer to.
- finalizer string: the finalizer string to add.
Returns:
- bool: true when the finalizer was added, false if already present.
- error: patch error or nil on success.
Behavior:
- Returns (false, nil) if the finalizer is already present.
- Uses merge patch for atomic finalizer addition.
func EnsureRole ¶
func EnsureRole( ctx context.Context, c client.Client, scheme *runtime.Scheme, owner client.Object, name string, namespace string, rules []rbacv1.PolicyRule, style ReferenceStyle, ) (controllerutil.OperationResult, error)
EnsureRole CreateOrUpdates a namespaced Role with the supplied rules and owner reference.
Arguments:
- ctx context.Context: context for the operation.
- c client.Client: Kubernetes client.
- scheme *runtime.Scheme: scheme for setting owner references.
- owner client.Object: the owner object for the Role.
- name string: Role name.
- namespace string: Role namespace.
- rules []rbacv1.PolicyRule: Role rules.
- style ReferenceStyle: how to set the owner reference.
Returns:
- controllerutil.OperationResult: result of the operation.
- error: any error encountered.
func EnsureRoleBinding ¶
func EnsureRoleBinding( ctx context.Context, c client.Client, scheme *runtime.Scheme, owner client.Object, name string, namespace string, roleRef rbacv1.RoleRef, subjects []rbacv1.Subject, style ReferenceStyle, ) (controllerutil.OperationResult, error)
EnsureRoleBinding CreateOrUpdates a namespaced RoleBinding with the supplied RoleRef, subjects, and owner reference.
Arguments:
- ctx context.Context: context for the operation.
- c client.Client: Kubernetes client.
- scheme *runtime.Scheme: scheme for setting owner references.
- owner client.Object: the owner object for the RoleBinding.
- name string: RoleBinding name.
- namespace string: RoleBinding namespace.
- roleRef rbacv1.RoleRef: the role reference.
- subjects []rbacv1.Subject: the subjects.
- style ReferenceStyle: how to set the owner reference.
Returns:
- controllerutil.OperationResult: result of the operation.
- error: any error encountered.
func EnsureSecretCopy ¶
func EnsureSecretCopy( ctx context.Context, reader client.Reader, writer client.Client, sourceNamespace string, targetNamespace string, name string, auth ...SecretCopyAuthorization, ) error
EnsureSecretCopy ensures a Secret exists in targetNamespace, copying data from sourceNamespace.
Behavior:
- Returns early when name is empty or namespaces are equal.
- Requires explicit authorization for cross-namespace copies.
- Creates the target Secret if missing, marking it as managed.
- Updates the target Secret only when it is marked as managed.
Arguments:
- ctx context.Context: request-scoped context.
- reader client.Reader: reader for source Secret (use APIReader if available).
- writer client.Client: writer for creating/updating the target Secret.
- sourceNamespace string: namespace holding the source Secret.
- targetNamespace string: namespace to receive the Secret copy.
- name string: Secret name.
- auth SecretCopyAuthorization: explicit cross-namespace authorization policies.
Returns:
- error: on read/create/update failures.
func EnsureService ¶
func EnsureService( ctx context.Context, c client.Client, scheme *runtime.Scheme, owner client.Object, desired *corev1.Service, ) (*corev1.Service, controllerutil.OperationResult, error)
EnsureService converges the live Service to match the desired spec by creating it when absent or patching metadata/spec differences. The returned OperationResult matches controllerutil.CreateOrUpdate semantics.
Arguments:
- ctx context.Context: context for the operation.
- c client.Client: Kubernetes client.
- scheme *runtime.Scheme: scheme for setting owner references.
- owner client.Object: the owner object for the Service.
- desired *corev1.Service: the desired Service specification.
Returns:
- *corev1.Service: the resulting Service.
- controllerutil.OperationResult: result of the operation.
- error: any error encountered.
func FirstPositiveDuration ¶
FirstPositiveDuration returns the first positive duration from the provided list.
Arguments:
- values ...time.Duration: durations to check in order.
Returns:
- time.Duration: the first positive value, or 0 if none are positive.
func ListByLabels ¶
func ListByLabels( ctx context.Context, c client.Client, namespace string, labels map[string]string, list client.ObjectList, ) error
ListByLabels fetches objects in a namespace that match the provided labels.
Arguments:
- ctx context.Context: context for the list operation.
- c client.Client: Kubernetes client.
- namespace string: namespace to list in; empty string lists cluster-wide.
- labels map[string]string: label selector; empty map matches all labels.
- list client.ObjectList: the list object to populate with results.
Returns:
- error: list error or nil on success.
Behavior:
- Applies namespace filter when namespace is non-empty.
- Applies label matching when labels is non-empty.
func MergeStringMaps ¶
MergeStringMaps returns a copy that contains the union of the provided maps, applying them in order so later maps override earlier keys.
Arguments:
- maps ...map[string]string: maps to merge in order.
Returns:
- map[string]string: merged map, or nil if all inputs are empty.
func MergeWithBlocks ¶
func MergeWithBlocks(engramWith, stepWith *runtime.RawExtension) (*runtime.RawExtension, error)
MergeWithBlocks merges two runtime.RawExtension values representing JSON "with" blocks. Values from the stepWith overlay the engramWith while preserving nested structures.
Arguments:
- engramWith *runtime.RawExtension: base configuration.
- stepWith *runtime.RawExtension: overlay configuration.
Returns:
- *runtime.RawExtension: merged configuration.
- error: JSON parsing/marshaling errors.
func Periodic ¶
Periodic executes fn every interval until the context is canceled. Returning an error from fn stops the loop early and surfaces the error to the caller.
Arguments:
- ctx context.Context: context for cancellation.
- interval time.Duration: how often to execute fn.
- fn func(context.Context) error: the function to execute periodically.
Returns:
- error: nil when context is canceled, or the error from fn.
func PositiveDurationOrDefault ¶
PositiveDurationOrDefault returns candidate when it is greater than zero, otherwise it falls back to the provided default value.
Arguments:
- candidate time.Duration: the preferred duration.
- fallback time.Duration: the default duration to use if candidate <= 0.
Returns:
- time.Duration: candidate if positive, otherwise fallback.
func PreserveImmutableServiceFields ¶
PreserveImmutableServiceFields copies ClusterIP/IPFamilies metadata from the existing Service into the desired Service so patches do not attempt to mutate immutable fields.
Arguments:
- current *corev1.Service: the existing Service.
- desired *corev1.Service: the desired Service to update.
func RecordEvent ¶
func RecordEvent( recorder events.EventRecorder, logger logr.Logger, obj client.Object, eventType string, reason string, message string, )
RecordEvent emits a Kubernetes event for obj when a recorder is configured, or logs the event metadata when the recorder is nil. Messages are trimmed before emission so controllers can pass multi-line errors safely.
Arguments:
- recorder events.EventRecorder: events API event recorder (may be nil in tests).
- logger logr.Logger: contextual logger used when recorder is nil.
- obj client.Object: Kubernetes object receiving the event; required.
- eventType string: event type (e.g., corev1.EventTypeWarning).
- reason string: short, CamelCase reason for the event.
- message string: human-readable description of the event.
Behavior:
- No-op if obj is nil or message is empty after trimming.
- Emits via recorder.Eventf when recorder is non-nil.
- Falls back to logger.Info when recorder is nil.
func Retry ¶
func Retry( ctx context.Context, cfg RetryConfig, fn func(context.Context) error, retryable func(error) bool, ) error
Retry invokes fn until it succeeds, the context is canceled, or MaxAttempts is reached. The retryable callback decides whether a returned error should trigger another attempt.
Arguments:
- ctx context.Context: context for cancellation.
- cfg RetryConfig: retry configuration.
- fn func(context.Context) error: the function to retry.
- retryable func(error) bool: determines if an error is retryable.
Returns:
- error: nil on success, or the last error after exhausting retries.
func RetryableStatusPatch ¶
func RetryableStatusPatch(ctx context.Context, cl client.Client, obj client.Object, updateFunc UpdateFunc) error
RetryableStatusPatch patches the /status subresource of obj using a merge patch, retrying on optimistic-concurrency conflicts.
The function:
- GETs the latest object by key,
- deep-copies it as the patch base,
- applies updateFunc to the fresh copy,
- PATCHes status with client.MergeFrom(original).
Arguments:
- ctx context.Context: context for the operations.
- cl client.Client: Kubernetes client.
- obj client.Object: the object whose status to patch.
- updateFunc UpdateFunc: function that mutates status fields only.
Returns:
- error: non-conflict failures or retry budget exhaustion.
Behavior:
- updateFunc must only mutate status fields, not spec/metadata.
- Works on fresh copies to avoid mutating caller's in-memory objects.
func RetryableStatusUpdate ¶
func RetryableStatusUpdate(ctx context.Context, cl client.Client, obj client.Object, updateFunc UpdateFunc) error
RetryableStatusUpdate updates an object's status using optimistic concurrency.
Arguments:
- ctx context.Context: context for the operations.
- cl client.Client: Kubernetes client.
- obj client.Object: the object whose status to update.
- updateFunc UpdateFunc: function that mutates status fields.
Returns:
- error: non-conflict failures or retry budget exhaustion.
func StoryRunEngramRunnerSubject ¶
StoryRunEngramRunnerSubject returns the canonical ServiceAccount subject for a StoryRun.
Arguments:
- storyRun string: the StoryRun name.
- namespace string: the namespace.
Returns:
- rbacv1.Subject: the ServiceAccount subject.
Types ¶
type DeleteLogger ¶
type DeleteLogger interface {
Info(msg string, keysAndValues ...any)
Error(err error, msg string, keysAndValues ...any)
}
DeleteLogger captures the logging interface required by DeleteIfExists.
type ReferenceStyle ¶
type ReferenceStyle int
ReferenceStyle selects how RBAC resources should attach owner references.
const ( // ControllerReference sets a controller reference via controllerutil.SetControllerReference. ControllerReference ReferenceStyle = iota // OwnerReference sets a non-controller owner reference via controllerutil.SetOwnerReference. OwnerReference )
type RetryConfig ¶
type RetryConfig struct {
// MaxAttempts controls how many times fn is invoked before giving up.
// Values <= 0 default to a single attempt.
MaxAttempts int
// InitialDelay determines how long Retry waits before the second attempt.
// Values <= 0 default to 250 milliseconds.
InitialDelay time.Duration
// Multiplier controls the exponential backoff multiplier applied after each wait.
// Values <= 0 default to 1 (no backoff).
Multiplier float64
}
RetryConfig specifies how Retry executes the provided function.
type SecretCopyAuthorization ¶
type SecretCopyAuthorization struct {
AllowedNames []string
}
SecretCopyAuthorization constrains which Secrets may be copied across namespaces by EnsureSecretCopy.
func AllowSecretCopyNames ¶
func AllowSecretCopyNames(names ...string) SecretCopyAuthorization
AllowSecretCopyNames authorizes cross-namespace copies for the provided Secret names only.
type UpdateFunc ¶
UpdateFunc is a function that mutates an object.
type ValidationOutcome ¶
type ValidationOutcome struct {
ReadyStatus metav1.ConditionStatus
ReadyReason string
ReadyMessage string
ValidationStatus enums.ValidationStatus
ValidationErrors []string
// contains filtered or unexported fields
}
ValidationOutcome encapsulates the Ready condition data plus the enums.ValidationStatus value that callers should persist.
func ComputeValidationOutcome ¶
func ComputeValidationOutcome(p ValidationParams) ValidationOutcome
ComputeValidationOutcome normalizes validation/binding inputs into a single Ready condition + validation status tuple so controllers present consistent messaging. All slices are defensively copied to avoid aliasing caller data.
Arguments:
- p ValidationParams: the validation parameters.
Returns:
- ValidationOutcome: computed Ready condition and validation status.
func (ValidationOutcome) AggregatedErrors ¶
func (o ValidationOutcome) AggregatedErrors() []string
AggregatedErrors returns the combined validation+binding error slice, guaranteeing a nil slice when no errors were supplied. Callers can store this in their status structs without additional copying.
type ValidationParams ¶
type ValidationParams struct {
// ValidationErrors lists spec validation failures (trimmed human-readable text).
ValidationErrors []string
// BindingErrors lists runtime/binding failures that should surface alongside
// validation errors when present.
BindingErrors []string
// BindingSummary allows callers to provide an already-compacted summary to
// avoid rejoining binding errors; when empty, BindingErrors are joined with "; ".
BindingSummary string
// ReadyBindings/TotalBindings/PendingBindings describe live binding counts.
// Pending logic only activates when TotalBindings > 0, ReadyBindings == 0,
// and PendingBindings > 0 (mirroring existing controller behavior).
ReadyBindings int
TotalBindings int
PendingBindings int
// SuccessReason/SuccessMessage annotate the Ready condition when all checks pass.
SuccessReason string
SuccessMessage string
// ValidationFailedReason describes the Ready condition reason when spec validation fails.
ValidationFailedReason string
// BindingFailedReason is used when runtime/binding errors exist; defaults to
// ValidationFailedReason when omitted.
BindingFailedReason string
// PendingReason labels the Ready condition while bindings negotiate; defaults
// to ValidationFailedReason when omitted.
PendingReason string
// SuccessStatus / ValidationFailedStatus / BindingFailedStatus / PendingStatus
// let callers override the enums.ValidationStatus assigned for each scenario.
// When left empty they default to Valid / Invalid / Invalid / Unknown.
SuccessStatus enums.ValidationStatus
ValidationFailedStatus enums.ValidationStatus
BindingFailedStatus enums.ValidationStatus
PendingStatus enums.ValidationStatus
// PendingMessageFormatter renders the Ready condition message for the pending
// state when provided; otherwise we fall back to a generic
// "awaiting reconciliation (%d pending)" string.
PendingMessageFormatter func(pending int) string
}
ValidationParams captures the aggregated validation/binding context required to derive a Ready condition snapshot plus the corresponding enums.ValidationStatus value for controllers. Callers supply controller-specific reason/message text so Ready conditions remain informative while the helper enforces consistent state transitions across controllers.