> ## Documentation Index
> Fetch the complete documentation index at: https://firebolt-aggregate-helm-docs-pr-5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# FireboltEngineClass design

> FireboltEngineClass namespacing, pod-template and engine-settings inheritance, and merge behavior.

## Pod template merge layer

Per-engine pod-template overrides live on `FireboltEngine.spec.template`,
parallel to the existing `FireboltEngineClass.spec.template`.
When an engine sets `spec.engineClassRef`, the reconciler resolves the
referenced `FireboltEngineClass` and composes the rendered pod
template from three layers, top wins on conflict for scalar and
struct fields:

1. **Firebolt Operator defaults**: Hardcoded engine container identity
   (`name`, `command`, `args`, `ports`, probes, reserved env keys),
   the StatefulSet-bound pod fields (`terminationGracePeriodSeconds=60`,
   `subdomain`, `hostname`, `restartPolicy`, `activeDeadlineSeconds`),
   and the operator-owned volumes / mounts (`nodes-config`, data). Always win.
2. **`FireboltEngineClass.spec.template`** (when `spec.engineClassRef`
   is set): Fills in user-owned fields the engine template doesn't set.
3. **`FireboltEngine.spec.template`**: Wins over the class on conflict.

The validating webhook applies the same allowlist (`FireboltEngineClassPodTemplateRules`)
to both class and engine templates, so a field accepted on one side is
also accepted on the other and operator-owned paths are rejected
uniformly on both.

Merge rules (centralized in the `effective*` helpers in
`engine_reconcile.go` so `buildStatefulSet` and `stsMatchesSpec` agree
on the resolved value):

| Field                                            | Rule                                                                                                                                                                                                    |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `serviceAccountName`                             | engine template > class > `""`                                                                                                                                                                          |
| `nodeSelector`                                   | map-merge, engine keys win                                                                                                                                                                              |
| `tolerations`                                    | class slice + engine slice, concatenated                                                                                                                                                                |
| `affinity`                                       | engine wins if non-nil, else class (no field-merge)                                                                                                                                                     |
| pod-template labels                              | Reserved keys (`firebolt.io/engine`, `firebolt.io/generation`) non-overridable. Engine template labels > class labels                                                                                   |
| pod-template annotations                         | engine template annotations > class annotations                                                                                                                                                         |
| pod-level `securityContext`                      | engine template > class > Firebolt Operator default (FSGroup, FSGroupChangePolicy always stamped)                                                                                                       |
| pod-level `volumes`                              | Firebolt Operator-owned volumes (`nodes-config`, data) come first. Class volumes then engine volumes are appended in that order. Names colliding with operator volumes are dropped                      |
| pod-level `imagePullSecrets`                     | class slice + engine slice, concatenated                                                                                                                                                                |
| init containers                                  | class slice + engine slice, concatenated (mirrors `tolerations`)                                                                                                                                        |
| sidecar containers (anything not named `engine`) | class sidecars + engine sidecars, appended after the operator-built engine container                                                                                                                    |
| engine container `image` / `imagePullPolicy`     | engine template > class > Firebolt Operator default                                                                                                                                                     |
| engine container `resources`                     | engine template wins wholesale if it carries any requests/limits/claims, else class fills in (no field-level merge)                                                                                     |
| engine container `securityContext`               | engine template wins if non-nil, else class                                                                                                                                                             |
| engine container `env`                           | Firebolt Operator-injected vars (`POD_INDEX`, `FB_AWS_EC2_METADATA_CLIENT_ENABLED`, `FIREBOLT_CORE_MODE`) first. Class env then engine env appended in that order (reserved keys rejected at admission) |
| engine container `envFrom`                       | class slice + engine slice, concatenated                                                                                                                                                                |
| engine container `volumeMounts`                  | Firebolt Operator-owned mounts first. Class mounts then engine mounts appended in that order. Mounts colliding with operator volume names are dropped                                                   |
| engine container `lifecycle`                     | engine template wins if non-nil, else class                                                                                                                                                             |

The engine controller watches `FireboltEngineClass` via
`EnqueueRequestsFromMapFunc` so a class edit immediately enqueues
every consumer engine. The validating webhooks on `FireboltEngine`
and `FireboltEngineClass` reject user input on paths the Firebolt
Operator owns end-to-end. See
[fireboltengineclass-crd-reference](../crd-reference/fireboltengineclass-crd-reference)
and [engine-crd-reference](../crd-reference/engine-crd-reference). The
merge layer therefore assumes both resolved templates only carry
fields it knows how to handle.

## Inherited engine settings

Beyond the pod template, a `FireboltEngineClass` carries defaults for a subset
of `FireboltEngine` settings. Each resolves the engine value first, then the
class value, then the operator default, the same precedence the pod template
follows. The `effective*` helpers in `engine_reconcile.go` centralize the
resolution so the renderer and the drift comparator agree.

| Field                | Rule                                                                                                                                                                                                                                                                                                                                          |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `storage`            | Whole-struct. The engine owns its storage when it names any backend (`persistentVolumeClaim`, `emptyDir`, or `hostPath`). Otherwise the class backend applies, falling through to the default `emptyDir`. Whole-struct selection keeps the three backends mutually exclusive.                                                                 |
| `customEngineConfig` | Deep-merged: operator base, then class, then engine, so an engine key wins over the same key on the class. The Firebolt Operator strips its owned config paths from both layers, so neither can override identity, routing, or topology.                                                                                                      |
| `rollout`            | engine > class > `graceful`.                                                                                                                                                                                                                                                                                                                  |
| `drainCheckEnabled`  | engine > class > `true`.                                                                                                                                                                                                                                                                                                                      |
| `drainCheckInterval` | engine > class > operator default.                                                                                                                                                                                                                                                                                                            |
| `autoStop`           | Whole-struct. An engine that sets `spec.autoStop` owns the entire policy. The class policy applies only when the engine omits it.                                                                                                                                                                                                             |
| `uiSidecar`          | engine > class > `false`. When it resolves to `true`, the Firebolt Operator injects a built-in, operator-owned `nginx` container named `engine-web` (serving the Engine Web UI, pointed at the local engine) into each engine pod. The `engine-web` name is reserved: a user-supplied container or init container with that name is rejected. |

`rollout`, `drainCheckEnabled`, `drainCheckInterval`, and `uiSidecar` carry no
CRD default on `FireboltEngine`. The Firebolt Operator applies the default in
the resolver instead, so an unset engine value falls through to the class
rather than being filled in at admission.

## Deletion

The validating webhook **refuses** `DELETE` while any `FireboltEngine` in
the class's namespace references the class via `spec.engineClassRef`.
Only same-namespace engines count. The check lists engines live from the API
server at admission time rather than reading `status.boundEngines`, so a
class bound between reconciler runs is still protected. Clear
`spec.engineClassRef` on every referencing engine first, then delete the
class. `failurePolicy: Fail` on the webhook configuration prevents a webhook
outage from opening a window in which a bound class could be removed.

## Class edit rollouts

Editing the class spec can trigger a blue-green rollout on every referencing
engine in the same namespace. The engine controller watches
`FireboltEngineClass` and enqueues every consumer whose
`spec.engineClassRef` matches. `stsMatchesSpec` compares the resolved class
template hash against the `firebolt.io/engine-class-hash` annotation on the
StatefulSet, and detects `storage`, `customEngineConfig`, and `uiSidecar`
changes (from either the engine or the class) through their own resolved-value
comparators.
Any mismatch bumps `currentGeneration`, whether the class template was edited in
place, the engine flipped to a different class, or the reference was cleared.
Class changes to `rollout`, `drainCheckEnabled`, `drainCheckInterval`, and
`autoStop` do not reshape the pod, so they take effect on the next reconcile
without forcing a new generation.
