The context (GlobalContext)
The context (GlobalContext)
A driver's templates and actions use values that are not in the driver: the cluster, the environment, the namespace that Styrmin chose, the storage class, and the application version that the user requested.
Styrmin puts these values in one object, the context
(GlobalContext in code). Every Helm values template and every
lifecycle action receives it, and every StyrminDeployment resource
contains it. Driver templates and actions read most of their values
from the context.
Where the context comes from
Styrmin builds the context once, when it creates an instance of a deployment. The server reads configuration from five sources and merges them into one object:
Cluster config ──┐
Environment config ──┤
Deployment config ──┼──► GlobalContext ──► driver templates + actions
Driver / chart ──┤ +
Resolved values ──┘ embedded in StyrminDeployment
The context does not change during the lifetime of the instance. When something changes, Styrmin creates a new instance with a new context. Templates and actions therefore read one consistent set of values, and do not query the server for the latest value. See Reconciliation for why this matters.
Structure
The context is a hierarchical Pydantic model. Top level:
GlobalContext
├── cluster ── cluster-level config + capabilities
├── environment ── environment-level config + capabilities (overrides cluster)
├── deployment ── deployment-level config (overrides environment)
├── instance ── the merged config + namespace + Helm release name
├── app ── application name, version, Helm chart info
├── components ── per-component config (optional)
├── parameters ── user-supplied parameter values
├── services ── declared services (from the driver spec)
├── deployed_services ── services as they exist in the cluster
├── utilities ── per-cluster utility info (ingress, etc.)
└── labels ── Kubernetes labels Styrmin will apply
The sections below describe each entry.
cluster
cluster:
config: # ClusterConfig — URLs, FQDN suffix, storage-class mappings…
capabilities: # dict — what this cluster can do (ingress, backups, …)
The cluster configuration, set when the cluster is created and changed
with styrminctl clusters update.
It includes values such as fqdn_suffix and standard_storage that most
driver templates use.
environment
environment:
name: # the environment's name
config: # EnvironmentConfig — IP whitelist, dedicated_ingress, …
capabilities: # dict — what this environment can do
The environment configuration, which overrides the cluster configuration. A field can be set at both levels; where it is, the environment's value applies.
When the environment has a backup storage location,
capabilities.backup.velero.storage_location is the name of the
location's Velero BackupStorageLocation object,
styrmin-bsl-<location id>. Renaming the location does not change it.
deployment
deployment:
config: # DeploymentConfig — the user's deployment-level config
The deployment configuration that the user set. It is the most specific of the three configuration layers.
instance
instance:
id: # the instance ID (None for a freshly generated context)
deployment_id: # the ID of the parent deployment
namespace: # the Kubernetes namespace Styrmin chose for this deployment
release_reference: # the Helm release name (and version reference)
config: # ResolvedConfig — the merged view of cluster + env + deployment
Most templates read from this layer. instance.config is the merged
configuration of the cluster, the environment, and the deployment,
where each layer overrides the one above it. Read
{{ instance.config.X }} instead of the individual layers, unless you
need the value of one layer.
Templates also often read namespace and release_reference: the
Kubernetes namespace and the Helm release name that Styrmin chose.
app
app:
name: # application name (e.g. "infrahub")
version: # the application version the user picked
helm: # info about the Helm chart this driver version uses
Use it for the image tag, for version-specific paths, or for any chart value that depends on the application version you deploy.
components
components: # ComponentsContext, keyed by component name
The configuration of each component. A driver declares its components
in driver.styrmin.yml, and Styrmin puts the resolved values of each
component here:
components.<name>.config: the component's definition, including its declaredcontainercomponents.<name>.env_vars.config: its resolved env varscomponents.<name>.image_override: its image overridecomponents.<name>.container_override: its containercommandandargsoverridecomponents.<name>.replica_override: its replica override
components is None when the driver has no per-component
configuration.
Env vars are read-only data
After the chart renders, Styrmin adds the env vars that users set to each component's declared component container, so templates do not pass them to the chart (see Create a driver). The resolved values stay readable for other uses, such as building a ConfigMap or enabling a chart feature:
instance.config.env_vars: the environment and deployment scopes merged, where the most specific scope wins.components.<name>.env_vars.config: the environment, deployment, and component scopes merged, where the most specific scope wins.
Both are absent when no env var is set, so write
(instance.config.env_vars or {}). Do not write them into a chart env
key: Styrmin already adds them, and because Helm replaces lists, the
chart's own env defaults would be removed.
Image overrides are read-only data
After the chart renders, Styrmin also applies a user's
image override to the
component container, so templates do not pass it to the chart either.
components.<name>.image_override is present only when the user set
one, and Styrmin applies it only when the component declares a
component container. Its reference is the stored string, with any
{{ app.version }} token not yet replaced. Do not write it into a
chart image key: Styrmin already sets the image.
Container overrides are read-only data
After the chart renders, Styrmin applies a user's container command
and args override to the component container, so templates do not
pass it to the chart. components.<name>.container_override is present
only when the user set command or args, and Styrmin applies it only
when the component declares a component container. Its command and
args are lists, and a field that the user did not set is absent. Do
not write them into a chart command or args key: Styrmin already
applies them, and when no override is set, the chart key would no
longer hold the chart's own value.
Replica overrides are read-only data
After the chart renders, Styrmin sets each component's replica count on
its Deployment and StatefulSet workloads (see
Replica overrides),
so templates do not pass a count to the chart.
components.<name>.replica_override is present only when the user set
one. The driver's own replicas and singleton are under
components.<name>.config, each only when the driver sets it. Do not
write either into a chart replica key: Styrmin sets spec.replicas
after the chart renders, which replaces any count the chart renders.
parameters
parameters: # dict[str, Any] — values the user filled in for the driver's parameters
When the driver declares spec.parameters in driver.styrmin.yml,
parameters contains the values that the user set. Use them directly:
{{ parameters.storage_size }}.
services
services: # dict — declared services from the driver spec
deployed_services: # list of dicts — services as they exist in the cluster
services contains the services that the driver declares under
spec.services. deployed_services contains the services that Styrmin
created in the cluster. Templates usually read services; actions
sometimes read deployed_services.
utilities and labels
utilities: [UtilityContext] # ingress controller, etc.
labels: {key: value} # Kubernetes labels Styrmin applies to managed resources
Most drivers do not read these.
Using the context in a Helm values template
Your values.j2.yml receives the context as Jinja variables: each
top-level field is available by name.
A minimal example:
# values.j2.yml
fullnameOverride: styrmin-{{ app.name }}
image:
repository: {{ app.name }}
tag: "{{ app.version }}"
ingress:
enabled: true
hosts:
- host: "{{ app.name }}-{{ instance.deployment_id }}.{{ instance.config.fqdn_suffix }}"
storage:
storageClassName: "{{ instance.config.standard_storage.storage_class }}"
The example shows these patterns:
{{ app.X }}for the application name and version.{{ instance.config.X }}for the merged configuration. Read from it by default.{{ cluster.config.X }}only when you need the cluster's own value, not the value that the environment or the deployment sets. When an administrator changes the cluster config, Styrmin redeploys only the deployments whoseinstance.configchanges, so a deployment that is not redeployed keeps thecluster.configof its last render.- Do not pass env vars to the chart. User env vars reach each
component's declared container without template code. The
component_env_varsfilter is removed (see Migrate env vars to component containers). - Do not pass
commandorargsto the chart. A user's containercommandandargsoverrides reach each component's declared container without template code. Thecomponent_commandandcomponent_argsfilters are retired: a template that calls either one fails to load withRETIRED_TEMPLATE_FILTER(see Migrate command and args overrides to component containers). - You do not need to set the Ingress class. Styrmin sets the class
of every Ingress after the chart renders, or removes it when the
environment has a dedicated ingress controller. A template that still
sets the class gives the same Ingress, so you can remove the line in
the driver's next version. The class that the template sets stays only
when the environment has no dedicated ingress controller and no
ingress_classis resolved. See Dedicated ingress controller or shared ingress class.
Using the context in an action
Lifecycle actions receive the context as a Python object. Its fields match the structure above; read them as attributes:
from prefect import flow
from styrmin_backend.actions import GlobalContext
@flow
async def post_setup(context: GlobalContext) -> None:
namespace = context.instance.namespace
app_version = context.app.version
ingress_class = context.instance.config.ingress_class
# … use them to compose subsequent calls
Built-in primitives such as stop_components, start_components, and
execute_commands take the context as an argument and read the values
they need from it, such as the deployment ID:
await stop_components(components=["server"], context=context)
Rules for reading the context
- Read from
instance.configby default. It holds the merged configuration, which most templates need. Read an individual layer only when you need the value of that layer. - Do not change the context. It stays the same for the lifetime of the instance. To change a value, add a parameter or create a new deployment.
- Read values only from the context. A driver that reads cluster state, environment variables, or live APIs can render differently for the same instance, which breaks the declarative model. Add the value to the context through the cluster or environment configuration, or through a driver parameter.
Next steps
- Create a driver: write your first driver with the context.
- Reconciliation: why the context does not change during the lifetime of an instance.