v1alpha1

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package v1alpha1 contains API Schema definitions for the compute v1alpha1 API group. +kubebuilder:object:generate=true +groupName=compute.firebolt.io

Index

Constants

View Source
const (
	// ConditionReady is the top-level roll-up condition: True only when
	// the engine is serving traffic on its active generation with all
	// replicas ready, the backing FireboltInstance is healthy, and no
	// transition is in progress. GitOps / ArgoCD-style tooling should
	// key off this condition rather than Phase to decide whether a
	// deployment has converged.
	//
	// Reasons for False:
	//   - Initializing      : status has not been populated yet
	//   - InstanceNotReady  : ConditionInstanceReady is False
	//   - Rolling           : phase is Creating / Switching / Draining / Cleaning
	//   - PhaseFailed       : phase is Failed (terminal; human-gated recovery)
	//   - PodsNotReady      : phase is Stable but active-generation pods
	//                         have not yet reported Ready
	//   - Stopped           : phase is Stopped (spec.replicas is 0);
	//                         the engine is intentionally parked and
	//                         cannot serve traffic until replicas > 0
	//   - ExternalFinalizer : reconcileDelete observed one or more
	//                         non-operator finalizers on owned children
	//                         (StatefulSet / Service / ConfigMap).
	//                         The engine CR is still garbage-collected;
	//                         the condition message and matching
	//                         ExternalFinalizerOnOwnedResource Event
	//                         name the resources and finalizers so the
	//                         operator can identify which external
	//                         controller is holding cleanup back.
	ConditionReady = "Ready"

	// ConditionInstanceReady indicates whether the referenced FireboltInstance
	// has a populated metadata endpoint and instance ID.
	ConditionInstanceReady = "InstanceReady"
)

Condition types for FireboltEngine.

View Source
const (
	// InstanceConditionReady is the top-level roll-up: True iff every
	// required per-component condition is True. GitOps tooling should
	// key off this condition rather than Phase, because Phase is a
	// summary enum derived from the same booleans and therefore
	// cannot distinguish "stuck on Postgres" from "stuck on gateway".
	InstanceConditionReady = "Ready"

	// InstanceConditionMetadataReady reports whether the metadata
	// Deployment's resources were applied successfully and its pods
	// are reporting Ready. The operator does not track a separate
	// PostgresReady condition: postgres is brought up alongside
	// metadata in the same reconcile pass, and the metadata pod's
	// connection-retry surfaces a missing or unreachable database in
	// THIS condition's Reason/Message.
	InstanceConditionMetadataReady = "MetadataReady"

	// InstanceConditionGatewayReady reports whether the Envoy gateway
	// Deployment's resources were applied successfully and its pods
	// are reporting Ready.
	InstanceConditionGatewayReady = "GatewayReady"
)

Condition types for FireboltInstance.

The per-component conditions (MetadataReady, GatewayReady) surface the outcome of each ensure step in Reconcile. They flip to False with a descriptive Reason whenever the corresponding sub-reconciler returns an error, which replaces the previous behavior of logging-and-requeueing- silently. The roll-up InstanceConditionReady is False whenever any per-component condition is not True, carrying the first blocker's Reason/Message so `kubectl describe` shows the root cause without digging.

These conditions are additive: the boolean Status.*Ready fields are kept for backward compatibility and for printcolumn display. The conditions carry the human-readable Reason/Message that booleans cannot.

View Source
const (
	EnginePodIndexEnvKey                    = "POD_INDEX"
	EngineAwsEC2MetadataClientEnabledEnvKey = "FB_AWS_EC2_METADATA_CLIENT_ENABLED"
	EngineCoreModeEnvKey                    = "FIREBOLT_CORE_MODE"
)

Operator-injected environment variables on the engine container. These keys carry pod-index plumbing (POD_INDEX) and AWS SDK + runtime mode signals that the operator must control end to end: a user override would either crash the engine or silently divert its identity. They are rejected at admission time on FireboltEngineClass spec.template and would be stripped if injected through another template channel.

View Source
const (
	MetadataPostgresUsernameEnvKey = "POSTGRES_USERNAME_FILE"
	MetadataPostgresPasswordEnvKey = "POSTGRES_PASSWORD_FILE" //nolint:gosec // legit:ignore-secrets — env-var name, not a credential
)

MetadataPostgresUsernameEnvKey and MetadataPostgresPasswordEnvKey are the env vars the operator injects on the metadata container to point dedicated-pensieve at its Postgres credentials Secret. User-supplied templates may not redefine these names; the validator rejects them.

View Source
const (
	// EngineConfigVolumeName is the projected-volume name carrying the
	// engine config.yaml (operator-rendered ConfigMap). It is mounted
	// at ConfigMountPath on the engine container.
	EngineConfigVolumeName = "nodes-config"
	// EngineDataVolumeName is the data volume backing the engine's
	// per-pod state — either a PVC synthesized from the StatefulSet's
	// VolumeClaimTemplate, an emptyDir, or a hostPath, depending on
	// FireboltEngineSpec.Storage. Mounted at DataMountPath.
	EngineDataVolumeName = "data"
	// EngineRuntimeVolumeName is the emptyDir volume mounted at
	// /var/run/firebolt for the engine's unix domain socket.
	EngineRuntimeVolumeName = "runtime"
	// GatewayConfigVolumeName carries the operator-rendered Envoy
	// config (envoy.yaml). Mounted at /etc/envoy on the Envoy
	// container.
	GatewayConfigVolumeName = "config-volume"
	// GatewayTmpVolumeName is the writable /tmp emptyDir the Envoy
	// container needs alongside ReadOnlyRootFilesystem=true.
	GatewayTmpVolumeName = "tmp"
	// MetadataConfigVolumeName carries the operator-rendered Pensieve
	// XML config. Mounted at /configs on the metadata container.
	MetadataConfigVolumeName = "config"
	// MetadataPostgresCredsVolumeName is the projected Secret with the
	// dedicated-pensieve Postgres username/password. Mounted at
	// /secrets/postgres on the metadata container.
	MetadataPostgresCredsVolumeName = "postgres-creds" //nolint:gosec // volume name, not a credential
	// MetadataTmpVolumeName is the writable /tmp emptyDir the metadata
	// container needs alongside ReadOnlyRootFilesystem=true.
	MetadataTmpVolumeName = "tmp"
)

Operator-rendered volume names on each component's primary container. User templates may not declare volumeMounts with these names; the validator rejects them so a renamed mount can't shadow the operator's config / credentials / data volumes.

View Source
const EngineContainerName = "engine"

EngineContainerName is the fixed name of the firebolt engine container inside each generation's StatefulSet pod template. The drain check, the stsMatchesSpec drift detection, and the FireboltEngineClass validating webhook all rely on it being stable and operator-owned. The name matches the public CRD ("engine"); the binary entrypoint inside the image is still `firebolt-core`, but that's an image-internal path and doesn't surface on the pod template.

View Source
const EngineWebContainerName = "engine-web"

EngineWebContainerName is the fixed name of the operator-injected Engine Web UI sidecar, deployed into engine pods when spec.uiSidecar resolves to true. Like EngineContainerName it is operator-owned: the FireboltEngineClass validating webhook rejects a user-supplied container or init container with this name (on both the engine and class templates) so the operator's injected sidecar can never collide with one the user wrote. The operator renders the container end-to-end, so there is no user-extension surface on it the way there is on the engine container.

View Source
const FireboltEngineClassConditionReady = "Ready"

FireboltEngineClassConditionReady is the top-level roll-up condition on a FireboltEngineClass: True when the class is admissible and safe to bind. The validating webhook normally rejects offending specs at admission time; the condition is a defense in depth for classes admitted under an older operator with a narrower rejection set.

View Source
const GatewayContainerName = "envoy"

GatewayContainerName is the fixed name of the Envoy container inside the FireboltInstance gateway Deployment's pod template. The FireboltInstance validating webhook uses it to locate the primary container on a user-supplied gateway template; the controller's build function emits a single container with the same name.

View Source
const MetadataContainerName = "metadata"

MetadataContainerName is the fixed name of the dedicated-pensieve container inside the FireboltInstance metadata Deployment's pod template. Used by the validating webhook and the builder, same pattern as GatewayContainerName.

View Source
const ReservedFireboltKeyPrefix = "firebolt.io/"

ReservedFireboltKeyPrefix is the label and annotation key prefix owned by the operator. Users MUST NOT set any key with this prefix on a CR that supports user-supplied labels/annotations — the controller unconditionally overwrites several keys to drive behavior (firebolt.io/config-hash, firebolt.io/generation, etc.), and letting users seed them silently freezes rollouts or corrupts routing.

Variables

View Source
var (
	// GroupVersion is group version used to register these objects.
	GroupVersion = schema.GroupVersion{Group: "compute.firebolt.io", Version: "v1alpha1"}

	// SchemeBuilder is used to add go types to the GroupVersionResource scheme.
	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

	// AddToScheme adds the types in this group-version to the given scheme.
	AddToScheme = SchemeBuilder.AddToScheme
)
View Source
var FireboltEngineClassPodTemplateRules = PodTemplateRules{
	Component:            "engine",
	PrimaryContainerName: EngineContainerName,
	AllowedPrimaryFields: PrimaryContainerFields{
		Image:                    true,
		Resources:                true,
		Env:                      true,
		EnvFrom:                  true,
		VolumeMounts:             true,
		SecurityContext:          true,
		Lifecycle:                true,
		WorkingDir:               true,
		TerminationMessagePath:   true,
		TerminationMessagePolicy: true,
		VolumeDevices:            true,
		ResizePolicy:             true,
	},
	ReservedPrimaryEnvKeys:          operatorOwnedEngineEnvKeys,
	ReservedPrimaryVolumeMountNames: operatorOwnedEngineVolumeNames,
	AllowSidecars:                   true,
	AllowInitContainers:             true,
	ReservedContainerNames:          []string{EngineWebContainerName},
}

FireboltEngineClassPodTemplateRules is the ruleset for FireboltEngineClass.spec.template. The engine container is the user-extension point most heavily used — users routinely set image, env, volumeMounts, securityContext — so the allowlist is wide. Sidecars and additional init containers pass through; the FireboltEngineClass merge layer in engine_reconcile.go appends them onto the operator-rendered pod spec.

View Source
var GatewayPodTemplateRules = PodTemplateRules{
	Component:            "gateway",
	PrimaryContainerName: GatewayContainerName,
	AllowedPrimaryFields: PrimaryContainerFields{
		Image:     true,
		Resources: true,
	},
	ReservedPrimaryVolumeMountNames: operatorOwnedGatewayVolumeNames,
	AllowSidecars:                   true,
	AllowInitContainers:             true,
}

GatewayPodTemplateRules is the ruleset for FireboltInstance.spec.gateway.template. Envoy is operator-rendered end-to-end (config, command via args, ports, probes, preStop drain hook, securityContext, the config and tmp volume mounts), so the user-allowed surface on the primary container is intentionally narrow: only image (so users can roll Envoy versions) and resources (so users can size the pod). The user may add sidecars (e.g. a stats exporter, a network filter) and init containers (e.g. a config validator); the gateway builder appends them after the operator-rendered Envoy container.

View Source
var MetadataPodTemplateRules = PodTemplateRules{
	Component:            "metadata",
	PrimaryContainerName: MetadataContainerName,
	AllowedPrimaryFields: PrimaryContainerFields{
		Image:     true,
		Resources: true,
	},
	ReservedPrimaryEnvKeys:          operatorOwnedMetadataEnvKeys,
	ReservedPrimaryVolumeMountNames: operatorOwnedMetadataVolumeNames,
	AllowSidecars:                   true,
	AllowInitContainers:             true,
}

MetadataPodTemplateRules is the ruleset for FireboltInstance.spec.metadata.template. The Pensieve container is operator-rendered (command, ports, probes, the POSTGRES_USERNAME_FILE/POSTGRES_PASSWORD_FILE env vars, the config / postgres-creds / tmp volume mounts, securityContext), so only image and resources are user-settable on the primary container. Sidecars and additional init containers pass through, same shape as the gateway.

View Source
var OperatorOwnedEngineConfigPaths = []EngineConfigOwnedSection{
	{Section: "", Keys: []string{"schema_version"}},
	{Section: "instance", Keys: []string{"id", "type", "multi_engine"}},
	{Section: "engine", Keys: []string{"id", "nodes", "termination_grace_period"}},
}

OperatorOwnedEngineConfigPaths declares every path in the rendered engine config.yaml that the operator manages exclusively. It is consumed by stripProtectedEngineConfigPaths (internal/controller/engine_reconcile.go), which removes these paths from spec.customEngineConfig before the deep merge into the canonical document.

Stripping is silent so that the same FireboltEngine spec stays portable across operator releases even when this list grows: users do not need to chase the protected set in their CRs to keep them applying cleanly.

Functions

func EngineContainerInTemplate

func EngineContainerInTemplate(template *corev1.PodTemplateSpec) *corev1.Container

EngineContainerInTemplate returns the container named EngineContainerName from a pod template, or nil when the template is nil or carries no such container. The lookup is exported so callers outside this package (the controller's resource-bounds defense-in-depth check, e.g.) can use the same definition.

func HasContainerResources

func HasContainerResources(r corev1.ResourceRequirements) bool

HasContainerResources reports whether a ResourceRequirements struct carries any user input (requests, limits, or claims). Exported so controller code that consumes the API package can reuse it instead of restating the predicate (callers: builders that decide whether to copy a user-supplied Resources field through to the rendered container, drift comparators that need to distinguish "user said nothing" from "user said empty").

func SetupFireboltEngineClassWebhookWithManager

func SetupFireboltEngineClassWebhookWithManager(mgr ctrl.Manager) error

SetupFireboltEngineClassWebhookWithManager registers the validating webhook with the manager. FireboltEngineClass has no defaulting webhook today: every kubebuilder default is enforced via openapi schema rather than mutating admission, so a Default() implementation would have nothing to do.

func SetupFireboltEngineWebhookWithManager

func SetupFireboltEngineWebhookWithManager(mgr ctrl.Manager, bounds *EngineResourceBounds) error

SetupFireboltEngineWebhookWithManager wires the validator into the manager's webhook server. The validator holds an APIReader rather than the cached Client because admission must reflect the live API state. bounds is passed by pointer because EngineResourceBounds embeds three resource.Quantity values; callers wanting a no-op validator pass either a zero value or a pointer to one, both of which short-circuit in validateResources via IsEmpty.

func SetupFireboltInstanceWebhookWithManager

func SetupFireboltInstanceWebhookWithManager(mgr ctrl.Manager) error

SetupFireboltInstanceWebhookWithManager registers the defaulting and validating webhooks with the manager.

func ValidateOperatorOwnedPodTemplate

func ValidateOperatorOwnedPodTemplate(template *corev1.PodTemplateSpec, base *field.Path) field.ErrorList

ValidateOperatorOwnedPodTemplate is the FireboltEngineClass entry point for pod-template validation. Kept as a stable named function because the FireboltEngineClass webhook references it directly; the implementation delegates to the generic ValidatePodTemplate walker driven by FireboltEngineClassPodTemplateRules.

func ValidatePodTemplate

func ValidatePodTemplate(template *corev1.PodTemplateSpec, base *field.Path, rules PodTemplateRules) field.ErrorList

ValidatePodTemplate walks a user-supplied PodTemplateSpec and rejects any input that conflicts with the supplied component rules. It is the single enforcement entry point for every component pod template the operator templates over (engine, gateway, metadata).

Rejection covers four layers:

  • pod-template metadata.labels / metadata.annotations under the ReservedFireboltKeyPrefix.
  • pod-level fields the operator owns universally: terminationGracePeriodSeconds, subdomain, hostname, restartPolicy, activeDeadlineSeconds.
  • the primary container (matched by rules.PrimaryContainerName): allowlist-driven — only fields enabled in rules.AllowedPrimaryFields pass; everything else is rejected. Within env and volumeMounts, entries with reserved names are rejected even when those fields are allowed in general.
  • init containers and additional containers (anything not PrimaryContainerName): rejected entirely when their respective AllowSidecars / AllowInitContainers flag is false. When permitted, they pass through with the single exception that no init container may take the primary container's name.

base is the field.Path the caller used to reach this PodTemplateSpec in its own object (e.g. field.NewPath("spec","template") for FireboltEngineClass; field.NewPath("spec","gateway","template") for the FireboltInstance gateway). Returned errors carry the full nested path so kubectl apply surfaces every violation at the offending coordinate.

func ValidateReservedKeyPrefix

func ValidateReservedKeyPrefix(path *field.Path, m map[string]string) field.ErrorList

ValidateReservedKeyPrefix rejects any key in m whose name starts with ReservedFireboltKeyPrefix. Used by every webhook that accepts user-set label or annotation maps on a CR. Returns one *field.Error per offending key, sorted alphabetically so test fixtures stay stable.

Types

type AuthMode

type AuthMode string

AuthMode defines the authentication mode for the Firebolt Instance. +kubebuilder:validation:Enum=disabled;native;openid

const (
	AuthModeDisabled AuthMode = "disabled"
	AuthModeNative   AuthMode = "native"
	AuthModeOpenID   AuthMode = "openid"
)

AuthModeDisabled through AuthModeOpenID enumerate the supported authentication modes.

type AuthSpec

type AuthSpec struct {
	// Mode is the authentication mode.
	Mode AuthMode `json:"mode"`

	// OIDC configures OpenID Connect. Required when mode is "openid".
	// +optional
	OIDC *OIDCSpec `json:"oidc,omitempty"`
}

AuthSpec configures authentication for the Firebolt Instance. TODO: the operator does not enforce auth yet. This spec is persisted in the CRD so that it can later be propagated to engine node configuration (e.g. via ConfigMap or environment variables) to enable native or OIDC authentication at the engine level.

func (*AuthSpec) DeepCopy

func (in *AuthSpec) DeepCopy() *AuthSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthSpec.

func (*AuthSpec) DeepCopyInto

func (in *AuthSpec) DeepCopyInto(out *AuthSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type AutoStopSpec

type AutoStopSpec struct {
	// Enabled turns auto-stop on for this engine. Defaults to false.
	// +kubebuilder:default=false
	Enabled bool `json:"enabled"`

	// ActiveReplicas is the replica count the engine runs at while active
	// (during a Schedule window or following a wake-up). Required when Enabled
	// is true.
	// +kubebuilder:validation:Minimum=1
	ActiveReplicas int32 `json:"activeReplicas"`

	// IdleReplicas is the replica count the engine scales down to once
	// IdleTimeout elapses with no query activity. 0 fully stops the engine.
	// Defaults to 0.
	// +kubebuilder:default=0
	// +kubebuilder:validation:Minimum=0
	// +optional
	IdleReplicas *int32 `json:"idleReplicas,omitempty"`

	// IdleTimeout is how long the engine must observe zero in-flight and
	// suspended queries before it scales down to IdleReplicas. Defaults to
	// 30 minutes.
	// +kubebuilder:default="30m"
	// +optional
	IdleTimeout *metav1.Duration `json:"idleTimeout,omitempty"`

	// PollInterval is how often the operator scrapes engine metrics to
	// re-evaluate idleness. Defaults to 1 minute.
	// +kubebuilder:default="1m"
	// +optional
	PollInterval *metav1.Duration `json:"pollInterval,omitempty"`

	// Schedule is an optional list of UTC time windows during which the engine
	// is held at ActiveReplicas regardless of observed activity. Useful for
	// "always-on during business hours" policies.
	// +optional
	Schedule []ScheduleWindow `json:"schedule,omitempty"`
}

AutoStopSpec configures automatic stop and wake-up for a FireboltEngine.

When Enabled is true the operator owns spec.replicas and drives it between two fixed levels: ActiveReplicas while the engine is active and IdleReplicas (0 by default, which fully stops the engine) once it has been idle for IdleTimeout. This is an activity-gated toggle, not proportional autoscaling: the engine runs at exactly IdleReplicas or ActiveReplicas, never a value in between, and query volume never changes the count. User edits to spec.replicas while auto-stop is enabled are converged on the next reconcile.

The engine scales up to ActiveReplicas when a Schedule window is open or a wake-up is requested, and scales down to IdleReplicas after IdleTimeout with no query activity. Idleness is observed via the same Prometheus gauges (firebolt_running_queries + firebolt_suspended_queries) that drive the blue-green drain check, so no additional probe protocol is needed. +kubebuilder:validation:XValidation:rule="self.activeReplicas >= (has(self.idleReplicas) ? self.idleReplicas : 0)",message="activeReplicas must be >= idleReplicas"

func (*AutoStopSpec) DeepCopy

func (in *AutoStopSpec) DeepCopy() *AutoStopSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoStopSpec.

func (*AutoStopSpec) DeepCopyInto

func (in *AutoStopSpec) DeepCopyInto(out *AutoStopSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type EngineConfigOwnedSection

type EngineConfigOwnedSection struct {
	// Section is the top-level key in the rendered config document, or "" for
	// the document root.
	Section string

	// Keys are the immediate children of Section managed exclusively by the
	// operator. User input at any of these paths is silently stripped.
	Keys []string
}

EngineConfigOwnedSection enumerates one operator-owned section of the rendered engine config.yaml. Section is the top-level key (empty string for the document root). Keys lists the immediate child keys under Section that the operator manages exclusively.

When Section is non-empty and the user-supplied value at that section is not a JSON object, the entire section is dropped from user input: a deep merge would otherwise replace the operator-built section wholesale with the user's scalar, losing every authoritative key.

func (*EngineConfigOwnedSection) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EngineConfigOwnedSection.

func (*EngineConfigOwnedSection) DeepCopyInto

func (in *EngineConfigOwnedSection) DeepCopyInto(out *EngineConfigOwnedSection)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type EngineEmptyDirSpec

type EngineEmptyDirSpec struct {
	// Medium is the storage medium backing the emptyDir. Leave empty
	// for the default node disk; set to "Memory" for a tmpfs-backed
	// volume sized against the pod's memory limit.
	// +kubebuilder:validation:Enum="";Memory
	// +optional
	Medium corev1.StorageMedium `json:"medium,omitempty"`

	// SizeLimit caps the volume's growth. Optional; nil means
	// unbounded (subject to node disk pressure or pod memory limits
	// for Memory medium).
	// +optional
	SizeLimit *resource.Quantity `json:"sizeLimit,omitempty"`
}

EngineEmptyDirSpec configures an emptyDir-backed engine data volume.

func (*EngineEmptyDirSpec) DeepCopy

func (in *EngineEmptyDirSpec) DeepCopy() *EngineEmptyDirSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EngineEmptyDirSpec.

func (*EngineEmptyDirSpec) DeepCopyInto

func (in *EngineEmptyDirSpec) DeepCopyInto(out *EngineEmptyDirSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type EngineHostPathSpec

type EngineHostPathSpec struct {
	// Path is the absolute filesystem path on the host node.
	// +kubebuilder:validation:Pattern=`^/.*`
	Path string `json:"path"`

	// Type enforces a check on Path before mounting (Directory,
	// DirectoryOrCreate, …). See
	// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
	// for the full enum. Leave nil to skip the check (kubelet default).
	// +optional
	Type *corev1.HostPathType `json:"type,omitempty"`
}

EngineHostPathSpec configures a hostPath-backed engine data volume.

func (*EngineHostPathSpec) DeepCopy

func (in *EngineHostPathSpec) DeepCopy() *EngineHostPathSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EngineHostPathSpec.

func (*EngineHostPathSpec) DeepCopyInto

func (in *EngineHostPathSpec) DeepCopyInto(out *EngineHostPathSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type EnginePersistentVolumeClaimSpec

type EnginePersistentVolumeClaimSpec struct {
	// Size is the requested capacity for each engine pod's PVC. Defaults to 1Gi.
	// +kubebuilder:default="1Gi"
	// +optional
	Size resource.Quantity `json:"size,omitempty"`

	// AccessModes for the PVC. Defaults to [ReadWriteOnce], which matches the
	// per-pod ownership model used by the StatefulSet VolumeClaimTemplate.
	// +kubebuilder:default={ReadWriteOnce}
	// +optional
	AccessModes []corev1.PersistentVolumeAccessMode `json:"accessModes,omitempty"`

	// StorageClassName selects the StorageClass for the PVC. Leave nil to use
	// the cluster default. An empty string disables dynamic provisioning.
	// +optional
	StorageClassName *string `json:"storageClassName,omitempty"`
}

EnginePersistentVolumeClaimSpec configures the per-pod PersistentVolumeClaim the StatefulSet controller synthesizes from the VolumeClaimTemplate. Each field is optional; omit the whole struct to accept the operator's defaults.

func (*EnginePersistentVolumeClaimSpec) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnginePersistentVolumeClaimSpec.

func (*EnginePersistentVolumeClaimSpec) DeepCopyInto

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type EnginePhase

type EnginePhase string

EnginePhase represents the current phase of the engine transition.

const (
	PhaseStable    EnginePhase = "stable"
	PhaseCreating  EnginePhase = "creating"
	PhaseSwitching EnginePhase = "switching"
	PhaseDraining  EnginePhase = "draining"
	PhaseCleaning  EnginePhase = "cleaning"
	PhaseStopped   EnginePhase = "stopped"
)

PhaseStable through PhaseStopped enumerate the lifecycle phases of a FireboltEngine during a blue-green rollout.

PhaseStopped is a terminal phase reached when spec.replicas is 0. It is structurally identical to PhaseStable: the active generation exists (as an empty StatefulSet + headless Service + ConfigMap) and any spec drift triggers a new blue-green generation. The distinct name exists so kubectl get and GitOps tooling can tell a running engine apart from an intentionally parked one.

type EngineResourceBounds

type EngineResourceBounds struct {
	// MaxCPU caps spec.resources.requests[cpu] and spec.resources.limits[cpu].
	// IsZero() disables the bound.
	MaxCPU resource.Quantity
	// MaxMemory caps spec.resources.requests[memory] and
	// spec.resources.limits[memory]. IsZero() disables the bound.
	MaxMemory resource.Quantity
	// MaxEphemeralStorage caps
	// spec.resources.requests[ephemeral-storage] and
	// spec.resources.limits[ephemeral-storage]. IsZero() disables the
	// bound.
	MaxEphemeralStorage resource.Quantity
}

EngineResourceBounds caps individual values inside FireboltEngine.spec.resources at admission time. Each entry is a maximum for both Requests and Limits of the matching ResourceName; a zero-value quantity disables the bound for that dimension. The struct value is passed by the operator entrypoint, sourced from CLI flags / Helm values, so platform teams can tune the ceiling per deployment without recompiling.

func (*EngineResourceBounds) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EngineResourceBounds.

func (*EngineResourceBounds) DeepCopyInto

func (in *EngineResourceBounds) DeepCopyInto(out *EngineResourceBounds)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*EngineResourceBounds) IsEmpty

func (b *EngineResourceBounds) IsEmpty() bool

IsEmpty reports whether all bounds are zero, i.e. validation is a no-op. Pointer receiver because EngineResourceBounds embeds three resource.Quantity values and is too large to pass by value efficiently.

func (*EngineResourceBounds) Validate

Validate walks resources.Requests and resources.Limits and returns a field.ErrorList containing one *field.Error per entry whose value exceeds the configured per-dimension bound. base is the field.Path the caller used to reach the ResourceRequirements (e.g. field.NewPath("spec","resources")). Both webhook admission and the FireboltEngine controller's defense-in-depth gate consume this so a CR rejected at admission and a CR caught at reconcile carry the same field path and the same "exceeds operator-configured maximum" message. An empty bounds value (IsEmpty()) short-circuits — the platform team has not opted into bounds, so every value passes.

type EngineStorageSpec

type EngineStorageSpec struct {
	// PersistentVolumeClaim opts the engine into a per-pod
	// PersistentVolumeClaim synthesized by the StatefulSet controller
	// from a VolumeClaimTemplate. Set the field (even to an empty
	// struct) to enable the PVC backend; the kubebuilder defaults
	// (1Gi, ReadWriteOnce, cluster-default StorageClass) on
	// EnginePersistentVolumeClaimSpec fill in any omitted inner field.
	// When this field is nil the engine falls back to the default
	// emptyDir backend (see the EngineStorageSpec doc above).
	// +optional
	PersistentVolumeClaim *EnginePersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"`

	// EmptyDir backs the engine's data mount with a pod-scoped emptyDir
	// Volume. The mount holds regenerable cache only; authoritative
	// data lives in the metadata service and the managed-table S3
	// bucket (see EngineStorageSpec for the full picture), so volume
	// contents being lost on pod restart is normal engine behavior
	// rather than a durability regression.
	// +optional
	EmptyDir *EngineEmptyDirSpec `json:"emptyDir,omitempty"`

	// HostPath backs the engine's data mount with a directory on the
	// node filesystem. Pods are implicitly node-pinned (the directory only
	// exists where it was created); the path must pre-exist on every
	// node the engine pod could be scheduled onto, with permissions
	// readable by the engine web UID. Use to expose instance-store NVMe on
	// the node.
	// +optional
	HostPath *EngineHostPathSpec `json:"hostPath,omitempty"`
}

EngineStorageSpec configures the per-pod data volume mounted into the engine container at the operator's data path (see the DataMountPath constant for the current value). Exactly one of PersistentVolumeClaim, EmptyDir, or HostPath should be set; if all three are nil the operator backs the mount with an emptyDir. Engine data at this mount is regenerable cache (authoritative state lives in the metadata service and the managed-table S3 bucket), so an ephemeral pod-local volume is the safe default and avoids a hard dependency on a dynamic-provisioner StorageClass. Opt into a per-pod PVC by setting persistentVolumeClaim (with or without explicit fields). The CEL rule below makes the three-way mutual exclusion an admission-time error rather than a silent "first non-nil wins" race. +kubebuilder:validation:XValidation:rule="(has(self.persistentVolumeClaim) ? 1 : 0) + (has(self.emptyDir) ? 1 : 0) + (has(self.hostPath) ? 1 : 0) <= 1",message="storage.persistentVolumeClaim, storage.emptyDir, and storage.hostPath are mutually exclusive"

func (*EngineStorageSpec) DeepCopy

func (in *EngineStorageSpec) DeepCopy() *EngineStorageSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EngineStorageSpec.

func (*EngineStorageSpec) DeepCopyInto

func (in *EngineStorageSpec) DeepCopyInto(out *EngineStorageSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltEngine

type FireboltEngine struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   FireboltEngineSpec   `json:"spec,omitempty"`
	Status FireboltEngineStatus `json:"status,omitempty"`
}

FireboltEngine is the Schema for the fireboltengines API.

func (*FireboltEngine) DeepCopy

func (in *FireboltEngine) DeepCopy() *FireboltEngine

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngine.

func (*FireboltEngine) DeepCopyInto

func (in *FireboltEngine) DeepCopyInto(out *FireboltEngine)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltEngine) DeepCopyObject

func (in *FireboltEngine) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltEngineClass

type FireboltEngineClass struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   FireboltEngineClassSpec   `json:"spec,omitempty"`
	Status FireboltEngineClassStatus `json:"status,omitempty"`
}

FireboltEngineClass is a reusable pod-template fragment shared by multiple FireboltEngines in the same namespace. Engines reference a FireboltEngineClass by name through spec.engineClassRef and inherit its template, eliminating the need to repeat identical per-engine settings (serviceAccountName, nodeSelector, tolerations, pod annotations — including the cloud-provider IAM binding for kube2iam, IRSA, and Pod Identity).

FireboltEngineClass is namespaced (not cluster-scoped like IngressClass / GatewayClass) because its template carries namespace-resolved identifiers — ServiceAccount names, Secret / ConfigMap / PVC volume references, and the per-tenant IAM annotations that the engine pod needs. Kubernetes resolves those names in the engine's own namespace at pod admission time, so the class and its consumer engines must live together. A cluster-scoped class with `serviceAccountName: foo` referenced from two namespaces would silently bind to two different ServiceAccounts (and possibly two different IAM roles) without admission catching the divergence.

func (*FireboltEngineClass) DeepCopy

func (in *FireboltEngineClass) DeepCopy() *FireboltEngineClass

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineClass.

func (*FireboltEngineClass) DeepCopyInto

func (in *FireboltEngineClass) DeepCopyInto(out *FireboltEngineClass)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltEngineClass) DeepCopyObject

func (in *FireboltEngineClass) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltEngineClassCustomValidator

type FireboltEngineClassCustomValidator struct {
	// Reader is an uncached API client used by ValidateDelete to list
	// FireboltEngines at admission time. Wired from mgr.GetAPIReader() so
	// the gate is consistent with API-server state and not subject to
	// status / cache staleness.
	Reader client.Reader
}

FireboltEngineClassCustomValidator implements the validating admission webhook for FireboltEngineClass. The webhook rejects any spec.template input that lands at a path the operator manages exclusively (the engine container's identity / command / probes / reserved env keys; pod-template metadata keys under firebolt.io/; pod-level fields tied to the StatefulSet and headless-DNS contracts). Sidecar containers and additional init containers pass through unconstrained.

Deletion is rejected while at least one FireboltEngine in the same namespace references the class. The check lists FireboltEngines live via Reader at admission time (rather than reading status.boundEngines) so a class bound between reconciler runs still refuses deletion. Configured failurePolicy: Fail so a webhook outage cannot open a deletion window that would orphan referencing engines.

+kubebuilder:object:generate=false

func (*FireboltEngineClassCustomValidator) ValidateCreate

ValidateCreate runs on every FireboltEngineClass create. It enforces the operator-owned-path rejection set against spec.template via ValidateOperatorOwnedPodTemplate, returning every violation in one admission response so users see all errors at once.

func (*FireboltEngineClassCustomValidator) ValidateDelete

ValidateDelete rejects deletion while at least one FireboltEngine in the same namespace references this class via spec.engineClassRef. FireboltEngineClass is namespaced so engineClassRef resolves in the engine's own namespace; only engines in the class's namespace can bind to it, and only those count toward the deletion gate.

The count is recomputed live from the API server, not read from status.boundEngines, because status defaults to zero on a freshly created class — relying on it would open a race where the class can be deleted between the moment an engine first references it and the next reconcile that increments the field.

Configured failurePolicy: Fail on the ValidatingWebhookConfiguration so that a webhook outage cannot bypass this guard. List errors propagate as admission errors for the same reason: better to refuse the delete than to admit it on incomplete information.

func (*FireboltEngineClassCustomValidator) ValidateUpdate

ValidateUpdate enforces the same path rejection set as ValidateCreate. FireboltEngineClass spec is mutable by design, but every rejected path stays rejected: a typo that admission caught at Create must still be caught on a subsequent Update.

type FireboltEngineClassList

type FireboltEngineClassList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []FireboltEngineClass `json:"items"`
}

FireboltEngineClassList contains a list of FireboltEngineClass.

func (*FireboltEngineClassList) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineClassList.

func (*FireboltEngineClassList) DeepCopyInto

func (in *FireboltEngineClassList) DeepCopyInto(out *FireboltEngineClassList)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltEngineClassList) DeepCopyObject

func (in *FireboltEngineClassList) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltEngineClassSpec

type FireboltEngineClassSpec struct {
	// Template is the pod template merged into engines that reference this
	// class. See the type-level doc for the precedence rules and the list
	// of operator-owned paths the validating webhook rejects.
	//
	// The CRD schema for template.metadata is patched post-controller-gen
	// (scripts/patch-crd-template-metadata.py, invoked by `make manifests`)
	// to set x-kubernetes-preserve-unknown-fields on the embedded
	// ObjectMeta. Without that injection, structural-schema pruning would
	// strip template.metadata.labels and template.metadata.annotations at
	// admission time and any GitOps controller re-applying them would
	// drift forever. A +kubebuilder:pruning:PreserveUnknownFields marker
	// on this Go field is not sufficient: controller-gen lands it on the
	// template schema, but the marker does not propagate into a child
	// that has its own typed sub-schema (metadata: {type: object}).
	Template corev1.PodTemplateSpec `json:"template"`

	// UISidecar is the default UI sidecar setting for referencing engines
	// that leave spec.uiSidecar unset. When true, the operator injects a
	// built-in, operator-owned nginx container named "engine-web" (serving the
	// Firebolt Engine Web UI, pointed at the local engine) into each pod. The
	// operator default (false) applies when neither side sets it; nil here
	// means the class does not override.
	// +optional
	UISidecar *bool `json:"uiSidecar,omitempty"`

	// Storage is the default per-pod data-volume configuration for engines
	// that reference this class and do not declare a storage backend of
	// their own. An engine that sets any backend on spec.storage
	// (persistentVolumeClaim, emptyDir, or hostPath) owns its storage
	// wholesale and ignores this value; otherwise the class value applies,
	// falling through to the operator default (an emptyDir) when neither
	// side selects a backend. See EngineStorageSpec for the backend choice
	// and its mutual-exclusion rule, which this field inherits.
	// +kubebuilder:default={}
	// +optional
	Storage EngineStorageSpec `json:"storage,omitempty"`

	// CustomEngineConfig is deep-merged beneath each referencing engine's
	// own spec.customEngineConfig into the rendered config.yaml: operator
	// defaults first, then this class config, then the engine config on top
	// (engine keys win on conflict). Operator-owned paths (see
	// OperatorOwnedEngineConfigPaths) are stripped from this value before
	// the merge, exactly as they are from the engine's own config, so the
	// class cannot override identity, routing, or topology.
	// +kubebuilder:pruning:PreserveUnknownFields
	// +kubebuilder:validation:Schemaless
	// +kubebuilder:validation:Type=object
	// +optional
	CustomEngineConfig *apiextensionsv1.JSON `json:"customEngineConfig,omitempty"`

	// Rollout is the default rollout strategy for referencing engines that
	// leave spec.rollout unset. The operator default (graceful) applies when
	// neither the engine nor the class sets it; an empty value here means
	// the class does not override. The graceful/recreate enum is inherited
	// from RolloutStrategy.
	// +optional
	Rollout RolloutStrategy `json:"rollout,omitempty"`

	// DrainCheckEnabled is the default drain-check setting for referencing
	// engines that leave spec.drainCheckEnabled unset. The operator default
	// (true) applies when neither side sets it; nil here means the class
	// does not override.
	// +optional
	DrainCheckEnabled *bool `json:"drainCheckEnabled,omitempty"`

	// DrainCheckInterval is the default drain-poll interval for referencing
	// engines that leave spec.drainCheckInterval unset. The operator default
	// applies when neither side sets it; nil here means the class does not
	// override.
	// +optional
	DrainCheckInterval *metav1.Duration `json:"drainCheckInterval,omitempty"`

	// AutoStop is the default autoStop policy for referencing engines
	// that leave spec.autoStop unset. Resolution is whole-struct: an
	// engine that sets spec.autoStop owns the entire policy and this
	// value is not field-merged into it; when the engine omits it, the
	// class policy applies; when neither sets it, autoStop is disabled.
	// +optional
	AutoStop *AutoStopSpec `json:"autoStop,omitempty"`
}

FireboltEngineClassSpec defines the desired state of a FireboltEngineClass.

Template is a Kubernetes PodTemplateSpec merged underneath an engine's own pod template when the engine references this class via spec.engineClassRef. Precedence on every field is:

  1. operator defaults (lowest)
  2. FireboltEngineClass template
  3. FireboltEngine spec (highest)

The operator owns a subset of the pod template (the engine container's image, command, args, ports, probes, reserved env keys; pod-level terminationGracePeriodSeconds, subdomain, hostname; metadata names; the firebolt.io/* label/annotation prefix). These paths are rejected by the FireboltEngineClass validating webhook at admission time so users see the failure immediately instead of via silent stripping. The authoritative rejection set is built from operatorauthority.go.

Non-engine containers (sidecars) and additional init containers are user-owned: the webhook does not constrain their image, command, ports, or environment. The exception is the reserved EngineWebContainerName ("engine-web") that the operator injects for spec.uiSidecar; a user container or init container with that name is rejected.

Beyond Template, the class carries defaults for a subset of FireboltEngine settings (UISidecar, Storage, CustomEngineConfig, Rollout, DrainCheckEnabled, DrainCheckInterval, AutoStop). Each resolves engine-if-set → class-if-set → operator default: a referencing engine that sets the corresponding spec field owns it, the class value applies when the engine leaves it unset, and the operator default sits beneath both. This lets a platform team standardize storage, engine config, and rollout/autoStop policy across a fleet without repeating it on every FireboltEngine.

func (*FireboltEngineClassSpec) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineClassSpec.

func (*FireboltEngineClassSpec) DeepCopyInto

func (in *FireboltEngineClassSpec) DeepCopyInto(out *FireboltEngineClassSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltEngineClassStatus

type FireboltEngineClassStatus struct {
	// ObservedGeneration is the metadata.generation last reconciled by the
	// FireboltEngineClass controller. Lets tooling detect stale status.
	// +optional
	ObservedGeneration int64 `json:"observedGeneration,omitempty"`

	// BoundEngines counts the FireboltEngines in the same namespace that
	// reference this class via spec.engineClassRef. Surfaced for
	// visibility (printcolumn, kubectl describe); both the FireboltEngineClass
	// deletion webhook and the reconciler's deletion-guard finalizer
	// do their own live List against the namespace rather than trusting
	// this cached value, so a class bound between reconciler runs
	// (status still at zero) is still protected from deletion.
	// +optional
	BoundEngines int32 `json:"boundEngines,omitempty"`

	// Conditions surface the FireboltEngineClass's high-level state.
	// +optional
	Conditions []metav1.Condition `json:"conditions,omitempty"`
}

FireboltEngineClassStatus is the observed state of a FireboltEngineClass.

func (*FireboltEngineClassStatus) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineClassStatus.

func (*FireboltEngineClassStatus) DeepCopyInto

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltEngineCustomValidator

type FireboltEngineCustomValidator struct {
	Reader         client.Reader
	ResourceBounds EngineResourceBounds
}

FireboltEngineCustomValidator validates FireboltEngine resources at admission time. It performs two checks:

  1. spec.engineClassRef, when set, points to a FireboltEngineClass that exists in the engine's own namespace — the reference is hard-rejected so users see a typo (or a class-applied-in-the-wrong-namespace mistake) immediately at apply time rather than via engine status. Apply ordering matters: a FireboltEngineClass must exist in the same namespace before any FireboltEngine that references it (GitOps tooling such as Argo CD sync-waves or Flux dependsOn handles this in practice).

  2. Each value in spec.resources.requests / spec.resources.limits sits at or below the operator-configured ceiling in ResourceBounds. The bounds protect a namespace from accidentally admitting an engine whose requests would starve sibling workloads at scheduling time and an engine whose limits would OOM the node hosting it.

The validator reads through mgr.GetAPIReader (live, non-cached) because the informer cache may not yet have the FireboltEngineClass at the moment of admission — particularly in `kubectl apply -f class.yaml -f engine.yaml` where both objects land within the same poll interval.

+kubebuilder:object:generate=false

func (*FireboltEngineCustomValidator) ValidateCreate

ValidateCreate rejects a new FireboltEngine when spec.engineClassRef references a FireboltEngineClass that does not exist, or when spec.resources carries a value above the configured bound.

func (*FireboltEngineCustomValidator) ValidateDelete

ValidateDelete is a no-op. The engine has no cross-resource invariants to enforce on deletion; the controller cleans up generation-scoped resources via owner references.

func (*FireboltEngineCustomValidator) ValidateUpdate

func (v *FireboltEngineCustomValidator) ValidateUpdate(
	ctx context.Context, _, eng *FireboltEngine,
) (admission.Warnings, error)

ValidateUpdate enforces the same existence and bound checks as ValidateCreate. Symmetric handling is deliberate: a typo on edit deserves the same immediate feedback as a typo on create. Recovery from a broken state (class deleted somehow, bound lowered after the engine was created) is always possible by setting spec.engineClassRef to nil / to another existing class, or by reducing spec.resources to fit the new bound.

type FireboltEngineList

type FireboltEngineList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []FireboltEngine `json:"items"`
}

FireboltEngineList contains a list of FireboltEngine.

func (*FireboltEngineList) DeepCopy

func (in *FireboltEngineList) DeepCopy() *FireboltEngineList

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineList.

func (*FireboltEngineList) DeepCopyInto

func (in *FireboltEngineList) DeepCopyInto(out *FireboltEngineList)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltEngineList) DeepCopyObject

func (in *FireboltEngineList) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltEngineSpec

type FireboltEngineSpec struct {
	// InstanceRef is the name of the FireboltInstance in the same namespace
	// that this engine depends on. The engine reconciler will not proceed
	// until the referenced instance has a populated metadata endpoint and
	// instance ID. This field is immutable after creation.
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="instanceRef is immutable"
	InstanceRef string `json:"instanceRef"`

	// EngineClassRef is the name of a FireboltEngineClass in this engine's
	// own namespace whose pod template is merged underneath this engine's
	// pod template when the engine's StatefulSet is built. Engine spec
	// fields take precedence over class fields; operator defaults sit
	// beneath both. FireboltEngineClass is namespaced because its template
	// carries namespace-resolved identifiers (ServiceAccount names,
	// Secret / ConfigMap / PVC volume references, per-tenant IAM
	// annotations); the class and the engines that reference it must
	// live together in the same namespace.
	//
	// Mutable: changing the reference (or editing the referenced
	// class's spec) triggers a new blue-green generation, just like any
	// other pod-template-affecting spec change. The change participates
	// in stsMatchesSpec via an annotation hash so drift is detected
	// deterministically.
	//
	// The validating webhook rejects Create and Update when the
	// referenced class does not exist in the engine's namespace, so
	// users see the typo (or a class-in-the-wrong-namespace mistake)
	// immediately instead of via engine status. Apply ordering matters:
	// GitOps tooling must apply the FireboltEngineClass before any
	// engine that references it (Argo sync-waves / Flux dependsOn).
	// +kubebuilder:validation:MinLength=1
	// +optional
	EngineClassRef *string `json:"engineClassRef,omitempty"`

	// Replicas is the number of engine nodes. Set to 0 to stop the
	// engine: the operator tears down the active generation (honoring
	// spec.rollout for drain behavior) and leaves the CR in the
	// Stopped phase. Setting replicas back to a non-zero value resumes
	// the engine via a new blue-green generation.
	// +kubebuilder:validation:Minimum=0
	Replicas int32 `json:"replicas"`

	// Template is the pod template the operator merges with its
	// own-rendered engine container, data volume, projected ConfigMap,
	// probes, and pod-level securityContext to produce the engine
	// StatefulSet's pod spec. Most users set only
	// template.spec.containers[name=="engine"].image and
	// .resources, plus scheduling fields (nodeSelector / tolerations /
	// affinity / topologySpreadConstraints / priorityClassName /
	// podSecurityContext) and any sidecars / init containers / extra
	// volumes they need.
	//
	// When spec.engineClassRef is also set, the operator first merges
	// the class's spec.template underneath the operator defaults, then
	// this template on top — engine wins on conflict (whole-struct
	// ownership for pointer fields; list-type fields like tolerations
	// / initContainers / sidecars / volumes concatenate class-first
	// then engine). The same field-by-field precedence the
	// FireboltEngineClass merge layer has always used now applies to
	// the engine's own template as the topmost layer.
	//
	// The validating webhook rejects user input on paths the operator
	// owns end-to-end — see FireboltEngineClassPodTemplateRules in
	// operatorauthority.go for the authoritative allowlist. Notably,
	// terminationGracePeriodSeconds is rejected: TGPS is operator-stamped
	// from an internal default (60s), matching the same rejection on the
	// class template.
	//
	// Changes to this field trigger a new blue-green generation.
	// +optional
	Template *corev1.PodTemplateSpec `json:"template,omitempty"`

	// UISidecar enables a built-in UI sidecar (an operator-owned nginx
	// container named "engine-web" serving the Firebolt Engine Web UI, pointed at
	// this engine on localhost) in every pod of this engine. It exposes
	// container port 9100 ("web-ui"); the per-engine headless Service resolves
	// to pod IPs, so the UI is reachable at <pod-ip>:9100 without listing an
	// extra Service port. The "engine-web" container name is reserved: a
	// user-supplied container or init container with that name is rejected.
	//
	// Resolution is engine-if-set → FireboltEngineClass-if-set → operator
	// default (false). The default is applied by the controller, not the CRD,
	// so an unset value here falls through to the referenced class instead of
	// being materialized to false at admission.
	//
	// Toggling this changes the pod template and therefore triggers a new
	// blue-green generation.
	// +optional
	UISidecar *bool `json:"uiSidecar,omitempty"`

	// DrainCheckEnabled controls whether the operator performs a SQL-based drain
	// check on old-generation pods during graceful rollouts. When false, the
	// operator skips directly to cleaning after switching traffic, without
	// verifying that in-flight queries have completed. Requires a running
	// node that can execute the drain-check query when enabled.
	//
	// Resolution is engine-if-set → FireboltEngineClass-if-set → operator
	// default (true). The default is applied by the controller, not the CRD,
	// so an unset value here falls through to the referenced class instead of
	// being materialized to true at admission.
	// +optional
	DrainCheckEnabled *bool `json:"drainCheckEnabled,omitempty"`

	// DrainCheckInterval controls how often to check if old pods have finished serving queries.
	// Only used when drainCheckEnabled is true.
	// +optional
	DrainCheckInterval *metav1.Duration `json:"drainCheckInterval,omitempty"`

	// Rollout strategy for transitions: "graceful" waits for drain, "recreate"
	// deletes immediately. Resolution is engine-if-set →
	// FireboltEngineClass-if-set → operator default (graceful). The default is
	// applied by the controller, not the CRD, so an unset value here falls
	// through to the referenced class instead of being materialized to
	// graceful at admission.
	// +optional
	Rollout RolloutStrategy `json:"rollout,omitempty"`

	// MetadataEndpointOverride overrides the metadata endpoint for this engine.
	// If nil, the engine uses Instance.status.metadataEndpoint (with intra-cluster
	// topology-aware routing). Set this for cross-cluster scenarios where the engine
	// connects to a metadata service in a different cluster via private link.
	//
	// Immutable once set: see the struct-level CEL rule on FireboltEngineSpec.
	// +optional
	MetadataEndpointOverride *string `json:"metadataEndpointOverride,omitempty"`

	// Storage configures the per-pod data volume mounted into each engine
	// container. See EngineStorageSpec for the backend choice (emptyDir is
	// the default; persistentVolumeClaim or hostPath are opt-ins). Changes
	// to any field — including switching backends — force a new blue-green
	// generation, because the StatefulSet's VolumeClaimTemplates are
	// immutable and a backend swap necessarily reshapes the pod-template
	// data Volume.
	//
	// Not a Kubernetes pod-template concern: the operator owns the data
	// volume name and the StatefulSet's VolumeClaimTemplates, so this stays
	// on the spec wrapper rather than under spec.template.spec.volumes.
	// +kubebuilder:default={}
	// +optional
	Storage EngineStorageSpec `json:"storage,omitempty"`

	// CustomEngineConfig is a free-form object deep-merged into the rendered
	// engine config.yaml at the root. The rendered document follows the
	// structured schema consumed by `firebolt server` (top-level sections:
	// `auth`, `engine`, `instance`, `logging`, `schema_version`); keys
	// placed in customEngineConfig at the same paths are merged into the
	// corresponding sections, overriding the operator's defaults (e.g.
	// `logging.format`, `logging.level`, `auth.*`). Any additional sections
	// understood by the engine binary may be set at the root.
	//
	// The operator retains authority over identity, routing, and topology
	// paths. The following are silently stripped from user input before the
	// merge and cannot be overridden via this field:
	//
	//   - schema_version
	//   - instance.id
	//   - instance.type
	//   - instance.multi_engine (entire subtree, including metadata_endpoint)
	//   - engine.id
	//   - engine.nodes
	//   - engine.termination_grace_period
	//
	// Changes to this field trigger a new blue-green generation, since the
	// rendered config.yaml content changes.
	// +kubebuilder:pruning:PreserveUnknownFields
	// +kubebuilder:validation:Schemaless
	// +kubebuilder:validation:Type=object
	// +optional
	CustomEngineConfig *apiextensionsv1.JSON `json:"customEngineConfig,omitempty"`

	// AutoStop configures automatic stop and wake-up for this engine. When
	// omitted or with Enabled=false, spec.replicas is governed entirely by the
	// user. When enabled, the operator owns spec.replicas and drives it between
	// IdleReplicas and ActiveReplicas based on idleness, wake-up requests, and
	// Schedule windows. It is an activity-gated toggle, not proportional
	// autoscaling.
	// +optional
	AutoStop *AutoStopSpec `json:"autoStop,omitempty"`
}

FireboltEngineSpec defines the desired state of a Firebolt engine.

The CEL rule freezes spec.metadataEndpointOverride once it has been set (or unset) at creation time. The field selects the cross-cluster metadata topology the engine's nodes bake into their on-disk configuration; letting users mutate it later would silently force a full blue-green rollout (it participates in stsMatchesSpec via AnnotationMetadataOverride) and would break the instanceInfo-drift invariant computeStable relies on to allow in-place recovery of a deleted ConfigMap (rebuild-at-same-gen produces byte-identical content only when all ConfigMap inputs are frozen). The wording mirrors the set-once pattern on FireboltInstanceSpec.ID, adapted for an optional pointer field via has(): absent-on-create may still transition to set, but any set value may not subsequently be changed or cleared. +kubebuilder:validation:XValidation:rule="!has(oldSelf.metadataEndpointOverride) || (has(self.metadataEndpointOverride) && self.metadataEndpointOverride == oldSelf.metadataEndpointOverride)",message="spec.metadataEndpointOverride is immutable once set"

func (*FireboltEngineSpec) DeepCopy

func (in *FireboltEngineSpec) DeepCopy() *FireboltEngineSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineSpec.

func (*FireboltEngineSpec) DeepCopyInto

func (in *FireboltEngineSpec) DeepCopyInto(out *FireboltEngineSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltEngineStatus

type FireboltEngineStatus struct {
	// ObservedGeneration is the metadata.generation that was last fully reconciled to stable.
	// +optional
	ObservedGeneration int64 `json:"observedGeneration,omitempty"`

	// CurrentGeneration is the latest generation number created.
	CurrentGeneration int `json:"currentGeneration"`

	// ActiveGeneration is the generation currently serving traffic.
	ActiveGeneration int `json:"activeGeneration"`

	// DrainingGeneration is the generation being drained, if any.
	// +optional
	DrainingGeneration *int `json:"drainingGeneration,omitempty"`

	// Phase is the current lifecycle phase of the engine.
	// +optional
	Phase EnginePhase `json:"phase,omitempty"`

	// LastReconciled is the timestamp of the last reconciliation.
	// +optional
	LastReconciled *metav1.Time `json:"lastReconciled,omitempty"`

	// LastActivityTime is the timestamp of the most recent auto-stop
	// observation that recorded in-flight or suspended queries. The engine
	// scales down once now() - LastActivityTime exceeds
	// spec.autoStop.idleTimeout. Cleared when auto-stop is disabled.
	// +optional
	LastActivityTime *metav1.Time `json:"lastActivityTime,omitempty"`

	// LastScaledAt is the timestamp of the most recent auto-stop-driven
	// mutation of spec.replicas. Distinguishes auto-stop scale events from
	// user edits in audit trails.
	// +optional
	LastScaledAt *metav1.Time `json:"lastScaledAt,omitempty"`

	// AutoStopReason is a short token describing the most recent auto-stop
	// decision: "Idle", "ScheduleActive", "ActivityObserved", "ScrapeFailed",
	// "Disabled", "Stopped", "Initializing", "WakeRequested".
	// +optional
	AutoStopReason string `json:"autoStopReason,omitempty"`

	// Conditions represent the latest available observations of the engine's state.
	// +optional
	Conditions []metav1.Condition `json:"conditions,omitempty"`
}

FireboltEngineStatus defines the observed state of a Firebolt engine.

func (*FireboltEngineStatus) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltEngineStatus.

func (*FireboltEngineStatus) DeepCopyInto

func (in *FireboltEngineStatus) DeepCopyInto(out *FireboltEngineStatus)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltInstance

type FireboltInstance struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   FireboltInstanceSpec   `json:"spec"`
	Status FireboltInstanceStatus `json:"status,omitempty"`
}

FireboltInstance is the Schema for the fireboltinstances API.

func (*FireboltInstance) DeepCopy

func (in *FireboltInstance) DeepCopy() *FireboltInstance

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstance.

func (*FireboltInstance) DeepCopyInto

func (in *FireboltInstance) DeepCopyInto(out *FireboltInstance)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltInstance) DeepCopyObject

func (in *FireboltInstance) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltInstanceCustomValidator

type FireboltInstanceCustomValidator struct{}

FireboltInstanceCustomValidator validates FireboltInstance resources.

func (*FireboltInstanceCustomValidator) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstanceCustomValidator.

func (*FireboltInstanceCustomValidator) DeepCopyInto

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltInstanceCustomValidator) ValidateCreate

ValidateCreate validates a FireboltInstance on creation.

func (*FireboltInstanceCustomValidator) ValidateDelete

ValidateDelete validates a FireboltInstance on deletion.

func (*FireboltInstanceCustomValidator) ValidateUpdate

ValidateUpdate validates a FireboltInstance on update.

type FireboltInstanceDefaulter

type FireboltInstanceDefaulter struct{}

FireboltInstanceDefaulter defaults FireboltInstance resources.

func (*FireboltInstanceDefaulter) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstanceDefaulter.

func (*FireboltInstanceDefaulter) DeepCopyInto

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltInstanceDefaulter) Default

Default sets default values for a FireboltInstance. If spec.id is empty, a new ULID is generated so every instance has a stable unique identifier.

type FireboltInstanceList

type FireboltInstanceList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []FireboltInstance `json:"items"`
}

FireboltInstanceList contains a list of FireboltInstance.

func (*FireboltInstanceList) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstanceList.

func (*FireboltInstanceList) DeepCopyInto

func (in *FireboltInstanceList) DeepCopyInto(out *FireboltInstanceList)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

func (*FireboltInstanceList) DeepCopyObject

func (in *FireboltInstanceList) DeepCopyObject() runtime.Object

DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.

type FireboltInstanceSpec

type FireboltInstanceSpec struct {
	// ID is a stable unique identifier for this instance, used as the metadata
	// account ID. If empty on creation, a ULID is generated automatically by
	// the defaulting webhook. Once set, this field is immutable.
	//
	// The CEL rule allows the one-time "" -> <ulid> transition because when
	// the mutating webhook is disabled (local dev, kind, some E2E setups),
	// the controller Reconcile has a fallback that generates an ID and
	// Updates the CR. A plain `self == oldSelf` would reject that Update at
	// admission time and leave the instance permanently stuck without an ID.
	// Once ID is non-empty, subsequent Updates are still blocked from
	// changing it.
	// +optional
	// +kubebuilder:validation:XValidation:rule="oldSelf == ” || self == oldSelf",message="spec.id is immutable once set"
	ID string `json:"id,omitempty"`

	// Metadata configures the metadata service.
	Metadata MetadataSpec `json:"metadata"`

	// Gateway configures the query routing gateway (Envoy proxy).
	Gateway GatewaySpec `json:"gateway"`

	// Auth configures authentication for engine nodes.
	// TODO: not enforced yet; will be propagated to engine configuration in a future release.
	// +optional
	Auth *AuthSpec `json:"auth,omitempty"`

	// MetricScrapeMode selects the transport the operator uses to scrape
	// engine pod /metrics for the drain probe and autoStop activity
	// poll. Read fresh on every scrape so it can be flipped without a
	// controller restart. Defaults to PodIP; flip to ApiserverProxy
	// only when in-cluster pod IPs aren't reachable from the controller
	// (out-of-cluster `make run`, or networks that block node-to-node
	// on MetricsPort but allow apiserver-proxy).
	// +kubebuilder:default=PodIP
	// +optional
	MetricScrapeMode MetricScrapeMode `json:"metricScrapeMode,omitempty"`
}

FireboltInstanceSpec defines the desired state of a Firebolt Instance.

func (*FireboltInstanceSpec) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstanceSpec.

func (*FireboltInstanceSpec) DeepCopyInto

func (in *FireboltInstanceSpec) DeepCopyInto(out *FireboltInstanceSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type FireboltInstanceStatus

type FireboltInstanceStatus struct {
	// Phase is the current lifecycle phase of the Instance.
	// +optional
	Phase InstancePhase `json:"phase,omitempty"`

	// MetadataReady indicates whether the metadata service is healthy.
	// +optional
	MetadataReady bool `json:"metadataReady,omitempty"`

	// MetadataEndpoint is the resolved Service address.
	// The Engine reconciler uses this to write engine ConfigMaps.
	// +optional
	MetadataEndpoint string `json:"metadataEndpoint,omitempty"`

	// GatewayReady indicates whether the gateway is healthy.
	// +optional
	GatewayReady bool `json:"gatewayReady,omitempty"`

	// GatewayEndpoint is the resolved gateway Service address.
	// +optional
	GatewayEndpoint string `json:"gatewayEndpoint,omitempty"`

	// Conditions represent the latest available observations of the Instance's state.
	// +optional
	Conditions []metav1.Condition `json:"conditions,omitempty"`
}

FireboltInstanceStatus defines the observed state of a Firebolt Instance.

func (*FireboltInstanceStatus) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FireboltInstanceStatus.

func (*FireboltInstanceStatus) DeepCopyInto

func (in *FireboltInstanceStatus) DeepCopyInto(out *FireboltInstanceStatus)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type GatewaySpec

type GatewaySpec struct {
	// Replicas is the number of gateway pods. Defaults to 2 when nil.
	// +optional
	Replicas *int32 `json:"replicas,omitempty"`

	// MetricsPort is the container port exposing Envoy's Prometheus
	// metrics endpoint. Defaults to 9090 if zero. The operator
	// stamps a corresponding "metrics" port entry on the container.
	// +kubebuilder:default=9090
	// +kubebuilder:validation:Minimum=1
	// +kubebuilder:validation:Maximum=65535
	// +optional
	MetricsPort int32 `json:"metricsPort,omitempty"`

	// Template is the pod template the operator merges with its
	// own-rendered Envoy container, config volume mount, hardcoded
	// probes, and preStop drain hook to produce the gateway
	// Deployment's pod spec. Most users set only
	// template.spec.containers[name=="envoy"].image and .resources,
	// plus scheduling fields (nodeSelector / tolerations / affinity /
	// topologySpreadConstraints / priorityClassName).
	//
	// template.metadata is unpruned by a post-controller-gen patch (see
	// the matching note on FireboltEngineClassSpec.Template for the full
	// rationale).
	// +optional
	Template *corev1.PodTemplateSpec `json:"template,omitempty"`
}

GatewaySpec configures the gateway component.

Pod scheduling, image, resources, sidecars, init containers, volumes, imagePullSecrets, podSecurityContext, and labels / annotations are expressed via spec.gateway.template (a Kubernetes PodTemplateSpec). The FireboltInstance validating webhook rejects any input on that template that lands at a path the operator owns end-to-end: the Envoy container's args / ports / probes / lifecycle preStop hook / reserved volume mounts (config-volume / tmp), and the pod-level terminationGracePeriodSeconds / subdomain / hostname / restartPolicy / activeDeadlineSeconds. See the GatewayPodTemplateRules ruleset in operatorauthority.go for the authoritative allowlist.

The Envoy `per_connection_buffer_limit_bytes` is intentionally NOT exposed here. The operator hard-codes it (see GatewayPerConnectionBufferLimitBytes in instance_gateway.go) because it sits at the center of multiple correctness invariants — retry coverage for the X-Firebolt-Drained shutdown fence, gateway memory budget under concurrent load — that the operator owns end-to-end. A user-tunable knob would invite settings that silently break the zero-downtime contract or OOM the gateway pod. If this trade-off needs to be revisited, raise it in the architecture doc rather than re-adding a field.

func (*GatewaySpec) DeepCopy

func (in *GatewaySpec) DeepCopy() *GatewaySpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewaySpec.

func (*GatewaySpec) DeepCopyInto

func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type ImageSpec

type ImageSpec struct {
	// Repository is the container image repository. When empty, the
	// operator's default repository for this component is used; pair with
	// Tag to override the full reference, or set on its own to pull the
	// operator-default tag from a different repository (e.g. a mirror).
	// +kubebuilder:validation:MinLength=1
	// +optional
	Repository string `json:"repository,omitempty"`

	// Tag is the container image tag. When empty, the operator's default
	// tag for this component is used; set on its own to pin a specific
	// version while keeping the operator-default repository.
	// +kubebuilder:validation:MinLength=1
	// +optional
	Tag string `json:"tag,omitempty"`

	// PullPolicy defines when to pull the image.
	// +kubebuilder:validation:Enum=Always;Never;IfNotPresent
	// +kubebuilder:default=IfNotPresent
	// +optional
	PullPolicy corev1.PullPolicy `json:"pullPolicy,omitempty"`
}

ImageSpec defines the container image configuration.

Both Repository and Tag are optional. Any field left empty falls back to the corresponding operator default (see the Default*Repository / Default*Tag constants in internal/controller), so users may override only the repository (to pull from a mirror) or only the tag (to pin a version) without restating the other half. An empty struct is equivalent to omitting the field entirely: defaults are applied to every dimension.

func (*ImageSpec) DeepCopy

func (in *ImageSpec) DeepCopy() *ImageSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec.

func (*ImageSpec) DeepCopyInto

func (in *ImageSpec) DeepCopyInto(out *ImageSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type InstancePhase

type InstancePhase string

InstancePhase represents the lifecycle phase of a Firebolt Instance. +kubebuilder:validation:Enum=Provisioning;Ready;Degraded;Failed

const (
	InstancePhaseProvisioning InstancePhase = "Provisioning"
	InstancePhaseReady        InstancePhase = "Ready"
	InstancePhaseDegraded     InstancePhase = "Degraded"
	InstancePhaseFailed       InstancePhase = "Failed"
)

InstancePhaseProvisioning through InstancePhaseFailed enumerate the lifecycle phases of a FireboltInstance.

type MetadataSpec

type MetadataSpec struct {
	// Replicas is the number of metadata pods. Pinned to 1 today by
	// the CEL rule above and the validating webhook; the surface is
	// kept on the spec for the day a multi-writer metadata story
	// lands.
	// +optional
	Replicas *int32 `json:"replicas,omitempty"`

	// Template is the pod template the operator merges with its
	// own-rendered metadata container, config volume, credentials
	// mount, probes, and pod-level securityContext to produce the
	// metadata Deployment's pod spec. Most users set only
	// template.spec.containers[name=="metadata"].image and
	// .resources, plus scheduling fields (nodeSelector / tolerations /
	// affinity / topologySpreadConstraints / priorityClassName).
	//
	// template.metadata is unpruned by a post-controller-gen patch (see
	// the matching note on FireboltEngineClassSpec.Template for the full
	// rationale).
	// +optional
	Template *corev1.PodTemplateSpec `json:"template,omitempty"`

	// Postgres configures the external PostgreSQL connection.
	// If nil, the operator deploys an internal PostgreSQL instance.
	// +optional
	Postgres *PostgresSpec `json:"postgres,omitempty"`

	// EngineRegistration enables registration of Engine objects in the metadata service for SQL-level RBAC.
	// +kubebuilder:default=false
	// +optional
	EngineRegistration bool `json:"engineRegistration,omitempty"`
}

MetadataSpec configures the metadata service.

Pod scheduling, image, resources, sidecars, init containers, volumes, imagePullSecrets, podSecurityContext, and labels / annotations are expressed via spec.metadata.template (a Kubernetes PodTemplateSpec). The FireboltInstance validating webhook rejects any input on that template that lands at a path the operator owns end-to-end: the dedicated-pensieve container's command / ports / probes / reserved env keys (POSTGRES_USERNAME_FILE / POSTGRES_PASSWORD_FILE) / reserved volume mounts (config / postgres-creds / tmp), and the pod-level terminationGracePeriodSeconds / subdomain / hostname / restartPolicy / activeDeadlineSeconds. See the MetadataPodTemplateRules ruleset in operatorauthority.go for the authoritative allowlist.

Only replicas=1 is currently supported; multi-replica metadata is not yet available. The CEL rule below enforces this at admission time, in addition to the Go-level check in the validating webhook (kept for defense-in-depth and to surface a clearer error message when the webhook is in the request path). +kubebuilder:validation:XValidation:rule="!has(self.replicas) || self.replicas == 1",message="metadata replicas must be 1"

func (*MetadataSpec) DeepCopy

func (in *MetadataSpec) DeepCopy() *MetadataSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataSpec.

func (*MetadataSpec) DeepCopyInto

func (in *MetadataSpec) DeepCopyInto(out *MetadataSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type MetricScrapeMode

type MetricScrapeMode string

MetricScrapeMode selects how the operator reaches engine pods to scrape the Prometheus /metrics endpoint that backs the drain probe and the autoStop activity poll. +kubebuilder:validation:Enum=PodIP;ApiserverProxy

const (
	// MetricScrapeModePodIP dials engine pod IPs directly on
	// MetricsPort from the controller pod. Default; matches every
	// standard in-cluster scraper (Prometheus, metrics-server,
	// OpenTelemetry, KSM) and doesn't depend on apiserver->node:9090
	// SG rules that EKS / kubeadm don't open by default.
	MetricScrapeModePodIP MetricScrapeMode = "PodIP"

	// MetricScrapeModeApiserverProxy routes the scrape through the
	// apiserver pods/proxy subresource. Opt-in for out-of-cluster
	// operator runs (`make run`) or networks that forbid node-to-node
	// on MetricsPort but allow apiserver-proxy; requires the cluster
	// SG to allow apiserver->node on MetricsPort, which is NOT the
	// default on EKS.
	MetricScrapeModeApiserverProxy MetricScrapeMode = "ApiserverProxy"
)

MetricScrapeModePodIP and MetricScrapeModeApiserverProxy enumerate the supported scrape transports. See FireboltInstanceSpec.MetricScrapeMode.

type OIDCSpec

type OIDCSpec struct {
	// IssuerURL is the OIDC provider's issuer URL.
	// +kubebuilder:validation:MinLength=1
	IssuerURL string `json:"issuerURL"`

	// ClientID is the OIDC client identifier.
	// +kubebuilder:validation:MinLength=1
	ClientID string `json:"clientID"`

	// ClientSecretRef references a Secret containing the OIDC client secret.
	// +optional
	ClientSecretRef *corev1.LocalObjectReference `json:"clientSecretRef,omitempty"`

	// ClaimMappings maps OIDC claims to Firebolt user attributes (e.g. {"username": "email"}).
	// +optional
	ClaimMappings map[string]string `json:"claimMappings,omitempty"`
}

OIDCSpec configures OpenID Connect authentication.

func (*OIDCSpec) DeepCopy

func (in *OIDCSpec) DeepCopy() *OIDCSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCSpec.

func (*OIDCSpec) DeepCopyInto

func (in *OIDCSpec) DeepCopyInto(out *OIDCSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type PodTemplateRules

type PodTemplateRules struct {
	// Component is the short component name used in error messages
	// ("engine", "gateway", "metadata").
	Component string

	// PrimaryContainerName is the name of the operator-rendered
	// container for this component. A second container with the same
	// name is rejected as a duplicate.
	PrimaryContainerName string

	// AllowedPrimaryFields enumerates the container-level fields the
	// user may set on the primary container. Any field that the user
	// sets and is not allowed here is rejected.
	AllowedPrimaryFields PrimaryContainerFields

	// ReservedPrimaryEnvKeys are env var names the operator injects on
	// the primary container; user entries with these names are
	// rejected. Only consulted when AllowedPrimaryFields.Env is true.
	ReservedPrimaryEnvKeys []string

	// ReservedPrimaryVolumeMountNames are mount names the operator
	// renders on the primary container; user entries with these names
	// in the primary container's volumeMounts are rejected. Only
	// consulted when AllowedPrimaryFields.VolumeMounts is true.
	ReservedPrimaryVolumeMountNames []string

	// AllowSidecars permits additional containers (any container whose
	// name is not PrimaryContainerName). When false, any sidecar is
	// rejected as a whole.
	AllowSidecars bool

	// AllowInitContainers permits user-supplied initContainers. When
	// false, any init container is rejected as a whole. When true,
	// an init container named PrimaryContainerName is still rejected
	// (it would collide with the operator-rendered primary container).
	AllowInitContainers bool

	// ReservedContainerNames are additional operator-owned container names
	// (beyond PrimaryContainerName) that the operator may inject into the
	// rendered pod, e.g. the optional engine web UI sidecar. A user container
	// or init container with one of these names is rejected even when
	// AllowSidecars / AllowInitContainers is true: the operator-rendered
	// container would otherwise collide with it, and Kubernetes requires
	// container names to be unique across the regular and init lists.
	ReservedContainerNames []string
}

PodTemplateRules declares the per-component validation contract for a FireboltInstance subcomponent's pod template (engine, gateway, metadata). One ruleset per component, consumed by ValidatePodTemplate. The walker rejects any user-supplied input on fields the operator owns end-to-end while passing through fields the user is allowed to set; "allowed" is an explicit allowlist on the primary container so a future container field added by Kubernetes lands as rejected by default (fail-safe direction).

The pod-level rejected fields (TerminationGracePeriodSeconds, Subdomain, Hostname, RestartPolicy, ActiveDeadlineSeconds) are universally operator-owned across engine, gateway, and metadata — every component stamps them from operator constants or relies on a StatefulSet / Deployment contract — so they are rejected unconditionally and don't appear on PodTemplateRules.

func (*PodTemplateRules) DeepCopy

func (in *PodTemplateRules) DeepCopy() *PodTemplateRules

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodTemplateRules.

func (*PodTemplateRules) DeepCopyInto

func (in *PodTemplateRules) DeepCopyInto(out *PodTemplateRules)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type PostgresSpec

type PostgresSpec struct {
	// Host is the PostgreSQL server hostname or IP. Allowed characters are
	// letters, digits, ".", "-", ":", "[", and "]" (the last three for IPv6
	// literals like "[::1]"). XML metacharacters are rejected at admission
	// time to prevent injection into the rendered metadata config.
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=253
	// +kubebuilder:validation:Pattern=`^[A-Za-z0-9.\-:\[\]]+$`
	Host string `json:"host"`

	// Port is the PostgreSQL server port.
	// +kubebuilder:default=5432
	// +optional
	Port int32 `json:"port,omitempty"`

	// Database is the PostgreSQL database name. Allowed characters are
	// letters, digits, "_", ".", and "-". XML metacharacters are rejected
	// at admission time to prevent injection into the rendered metadata
	// config.
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=63
	// +kubebuilder:validation:Pattern=`^[A-Za-z0-9_.\-]+$`
	Database string `json:"database"`

	// Schema is the PostgreSQL schema used by the metadata service.
	// Defaults to "public". Allowed characters are letters, digits, "_",
	// ".", and "-". XML metacharacters are rejected at admission time to
	// prevent injection into the rendered metadata config.
	// +kubebuilder:default=public
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=63
	// +kubebuilder:validation:Pattern=`^[A-Za-z0-9_.\-]+$`
	// +optional
	Schema string `json:"schema,omitempty"`

	// CredentialsSecretRef references a Secret containing "username" and "password" keys.
	CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef"`
}

PostgresSpec configures an external PostgreSQL connection for the metadata service.

The string fields below are interpolated into the XML config the operator renders for the metadata service (see buildMetadataConfigXML). The controller XML-escapes user input at render time, but the patterns here reject XML metacharacters at admission time as defense-in-depth so a malformed CR is rejected at apply rather than producing a config that only works because the controller scrubs it.

func (*PostgresSpec) DeepCopy

func (in *PostgresSpec) DeepCopy() *PostgresSpec

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec.

func (*PostgresSpec) DeepCopyInto

func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type PrimaryContainerFields

type PrimaryContainerFields struct {
	Image                    bool // image and imagePullPolicy
	Resources                bool
	Env                      bool // entries with reserved keys still rejected
	EnvFrom                  bool
	VolumeMounts             bool // entries with reserved names still rejected
	SecurityContext          bool
	Lifecycle                bool
	WorkingDir               bool
	TerminationMessagePath   bool
	TerminationMessagePolicy bool
	VolumeDevices            bool
	ResizePolicy             bool
}

PrimaryContainerFields declares which container-level fields a user is allowed to set on the operator-rendered primary container. Every field defaults to false (rejected) so silently adding a Container field to the Kubernetes API surface keeps the operator's owned-by- default posture without a code change here.

func (*PrimaryContainerFields) DeepCopy

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrimaryContainerFields.

func (*PrimaryContainerFields) DeepCopyInto

func (in *PrimaryContainerFields) DeepCopyInto(out *PrimaryContainerFields)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

type RolloutStrategy

type RolloutStrategy string

RolloutStrategy defines how transitions between generations are handled. +kubebuilder:validation:Enum=graceful;recreate

const (
	RolloutGraceful RolloutStrategy = "graceful"
	RolloutRecreate RolloutStrategy = "recreate"
)

RolloutGraceful and RolloutRecreate define the supported rollout strategies.

type ScheduleDay

type ScheduleDay string

ScheduleDay is a UTC weekday code used in ScheduleWindow.Days. +kubebuilder:validation:Enum=Mon;Tue;Wed;Thu;Fri;Sat;Sun

type ScheduleWindow

type ScheduleWindow struct {
	// Start is the window opening time in UTC, formatted "HH:MM".
	// +kubebuilder:validation:Pattern=`^([01]\d|2[0-3]):[0-5]\d$`
	Start string `json:"start"`

	// End is the window closing time in UTC, formatted "HH:MM". An End equal
	// to Start is treated as an empty window.
	// +kubebuilder:validation:Pattern=`^([01]\d|2[0-3]):[0-5]\d$`
	End string `json:"end"`

	// Days lists the UTC weekdays the window applies to. Empty means every
	// day. The window is anchored to the day on which Start falls; if End
	// crosses midnight the trailing portion still belongs to the listed day.
	// +optional
	Days []ScheduleDay `json:"days,omitempty"`
}

ScheduleWindow is a recurring UTC time window during which the engine is pinned at ActiveReplicas. End may be less than Start to express a window that crosses midnight (e.g. 22:00-02:00).

func (*ScheduleWindow) DeepCopy

func (in *ScheduleWindow) DeepCopy() *ScheduleWindow

DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleWindow.

func (*ScheduleWindow) DeepCopyInto

func (in *ScheduleWindow) DeepCopyInto(out *ScheduleWindow)

DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL