Skip to content

The Context

One of KuboCD's key features is its ability to generate Helm value files from a concise set of high-level input parameters using a templating mechanism.

This mechanism combines a template with a data model.

Our previous example used only the .Parameters element of the data model:

podinfo-p01.yaml
apiVersion: v1alpha1
type: Package
name: podinfo
...
modules:
  - name: main
    ...
    values: |
      ingress:
        enabled: true
        className: {{ .Parameters.ingressClassName }}
        hosts:
          - host: {{ .Parameters.fqdn }}
            paths:
              - path: /
                pathType: ImplementationSpecific

However, the full data model includes the following top-level elements:

  • .Parameters: The parameters provided in the Release custom resource.
  • .Release: The release object itself.
  • .Context: The deployment context.

The Context is a flexible YAML object designed to hold shared configuration data relevant across multiple deployments.

For example, the podinfo package includes an ingressClassName parameter with a default value of nginx. If a cluster uses a different ingress controller, this value would need to be overridden for every Release. Ideally, widespread configuration like this should be defined once in a global cluster-level context.

Similarly, if all application ingress URLs share a common root domain, that domain should also be centralized.

Here is how to implement this logic.


Creating a Context

A Context is a KuboCD Custom Resource:

cluster.yaml
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Context
metadata:
  namespace: contexts
  name: cluster
spec:
  description: Global context for the kubodoc cluster
  protected: true
  context:
    ingress:
      className: nginx
      domain: ingress.kubodoc.local
    storageClass: 
      data: standard
      workspace: standard
    certificateIssuer:
      public: cluster-self
      internal: cluster-self

Key Attributes:

  • description: A short description of the context.
  • protected: Prevents accidental deletion (requires the KuboCD webhook).
  • context: A tree of values injected into the data model during values templating.
    • Must be valid YAML.
    • Has a flexible structure but should align with what Package templates expect.

In this example, the context includes:

  • ingress.className: The ingress controller type.
  • ingress.domain: The suffix for ingress URLs.
  • storageClass: Definitions for data and workspace storage profiles (set to standard for Kind).
  • certificateIssuer: Identifiers for public and internal certificate issuers. (We will configure a self-signed CA in the cert-manager section).

Cluster-wide contexts should be placed in a dedicated namespace:

kubectl create ns contexts
kubectl apply -f contexts/cluster.yaml

Note

Since the context is often shared across many applications, its structure should be carefully designed and well-documented.


Modifying the Package

Our initial podinfo package did not utilize the context. Here is an updated version that does:

podinfo-p02.yaml
apiVersion: v1alpha1
type: Package
name: podinfo
tag: 6.7.1-p02
schema:
  parameters:
    $schema: http://json-schema.org/schema#
    type: object
    additionalProperties: false
    properties:
      host: { type: string }
    required:
      - host
  context:
    $schema: http://json-schema.org/schema#
    additionalProperties: true
    type: object
    properties:
      ingress:
        type: object
        additionalProperties: true
        properties:
          className: { type: string }
          domain: { type: string }
        required:
          - domain
          - className
    required:
      - ingress
modules:
  - name: main
    source:
      helmRepository:
        url: https://stefanprodan.github.io/podinfo
        chart: podinfo
        version: 6.7.1
    values: |
      ingress:
        enabled: true
        className: {{ .Context.ingress.className  }}
        hosts:
          - host: {{ .Parameters.host }}.{{ .Context.ingress.domain }}
            paths:
              - path: /
                pathType: ImplementationSpecific

Key Changes:

  • tag: updated to 6.7.1-p02.
  • host parameter: Replaces fqdn. Now represents only the hostname (excluding the domain).
  • modules[X].values: Updated to use .Context variables.
  • schema.context: Added to define and validate the expected context structure.

Pack and push this new version:

kubocd pack packages/podinfo-p02.yaml
====================================== Packaging package 'podinfo-p02.yaml'
--- Handling module 'main':
    Fetching chart podinfo:6.7.1...
    Chart: podinfo:6.7.1
--- Packaging
    Generating index file
    Wrap all in assembly.tgz
--- push OCI image: quay.io/kubodoc/packages/podinfo:6.7.1-p02
    Successfully pushed

Deployment with Context

Here is the corresponding Release manifest:

podinfo2-ctx.yaml
---
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Release
metadata:
  name: podinfo2
  namespace: default
spec:
  description: A first sample release of podinfo
  package:
    repository: quay.io/kubodoc/packages/podinfo
    tag: 6.7.1-p02
  parameters:
    host: podinfo2
  contexts:
    - namespace: contexts
      name: cluster

Key Features:

  • Interval Removed: spec.package.interval is omitted and defaults to 5m.
  • Parameter Update: fqdn is replaced by host.
  • contexts Section: Lists contexts to merge into the template data model.

Warning

Referencing a non-existent context will result in an error.

Apply the deployment:

kubectl apply -f releases/podinfo2-ctx.yaml

Check that the new Release is READY:

kubectl get releases podinfo2
NAME       REPOSITORY                         TAG         CONTEXTS           STATUS   READY   WAIT   PRT   AGE   DESCRIPTION
podinfo2   quay.io/kubodoc/packages/podinfo   6.7.1-p02   contexts:cluster   READY    1/1            -     17m   A first sample release of podinfo

Verify that the ingress has been configured correctly:

kubectl get ingresses podinfo2-main
NAME            CLASS   HOSTS                            ADDRESS      PORTS   AGE
podinfo2-main   nginx   podinfo2.ingress.kubodoc.local   10.96.59.9   80      120m

Note

Remember to update your /etc/hosts or DNS if you wish to access this ingress.


Context Aggregation

An application's effective context is often the result of aggregating multiple context objects.

For instance, you might create a project-level context to share variables across all applications in a specific project, which is then merged with the global cluster context.

The examples below merge the contexts that a Release lists. A Context can also merge other contexts, its parents. See Parents.

Example 1: merging Contexts

Create a project namespace:

kubectl create namespace project01

Create the project context:

project01.yaml
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Context
metadata:
  name: project01
spec:
  description: Context for project 1
  context:
    project:
      id: p01
      subdomain: prj01

Apply it to the namespace:

kubectl -n project01 apply -f contexts/project01.yaml

List all defined contexts:

kubectl get --all-namespaces contexts.kubocd.kubotal.io
NAMESPACE   NAME        DESCRIPTION                          PARENTS   STATUS   AGE
contexts    cluster     Global context for the kubodoc cluster          READY    2d2h
project01   project01   Context for project 1                           READY    2m35s

We need to update the package to use the new project.subdomain variable:

podinfo-p03.yaml
apiVersion: v1alpha1
type: Package
name: podinfo
tag: 6.7.1-p03
schema:
  parameters:
    $schema: http://json-schema.org/schema#
    type: object
    additionalProperties: false
    properties:
      host: { type: string }
    required:
      - host
  context:
    $schema: http://json-schema.org/schema#
    additionalProperties: true
    type: object
    properties:
      ingress:
        type: object
        additionalProperties: true
        properties:
          className: { type: string }
          domain: { type: string }
        required:
          - domain
          - className
      project:
        type: object
        additionalProperties: true
        properties:
          subdomain: { type: string }
        required:
          - subdomain
    required:
      - ingress
      - project
modules:
  - name: main
    source:
      helmRepository:
        url: https://stefanprodan.github.io/podinfo
        chart: podinfo
        version: 6.7.1
    values: |
      ingress:
        enabled: true
        className: {{ .Context.ingress.className  }}
        hosts:
          - host: {{ .Parameters.host }}.{{ .Context.project.subdomain }}.{{ .Context.ingress.domain }}
            paths:
              - path: /
                pathType: ImplementationSpecific

Pack the updated package:

kubocd pack packages/podinfo-p03.yaml

Create a new Release:

podinfo-prj01.yaml
---
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Release
metadata:
  name: podinfo
spec:
  description: A release of podinfo on project01
  package:
    repository: quay.io/kubodoc/packages/podinfo
    tag: 6.7.1-p03
  parameters:
    host: podinfo
  contexts:
    - namespace: contexts
      name: cluster
    - name: project01
  debug:
    dumpContext: true
    dumpParameters: true

Notes:

  • metadata.namespace: Not defined in the file; will be set via CLI.
  • metadata.name: Simply podinfo.
  • spec.contexts: Now includes two entries. The second one references the project context (defaults to the release namespace).
  • debug: Added to dump the resulting context and parameters into the Release status for inspection.

Deploy the release:

kubectl -n project01 apply -f releases/podinfo-prj01.yaml

Verify that both contexts are active:

kubectl -n project01 get releases podinfo
NAME      REPOSITORY                         TAG         CONTEXTS                               STATUS   READY   WAIT   PRT   AGE     DESCRIPTION
podinfo   quay.io/kubodoc/packages/podinfo   6.7.1-p03   contexts:cluster,project01:project01   READY    1/1            -     8m31s   A release of podinfo on project01

Check the resulting ingress:

kubectl get --all-namespaces ingress
NAMESPACE   NAME            CLASS   HOSTS                                 ADDRESS        PORTS   AGE
default     podinfo1-main   nginx   podinfo1.ingress.kubodoc.local        10.96.207.51   80      4h46m
default     podinfo2-main   nginx   podinfo2.ingress.kubodoc.local        10.96.207.51   80      4h6m
project01   podinfo-main    nginx   podinfo.prj01.ingress.kubodoc.local   10.96.207.51   80      4h

Inspect the Release status to see the merged context:

kubectl -n project01 get release podinfo -o yaml
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Release
metadata:
  ....
spec:
  ....
status:
  context:
    ingress:
      className: nginx
      domain: ingress.kubodoc.local
    project:
      id: p01
      subdomain: prj01
    storageClass:
      data: standard
      workspace: standard
  ....      
  parameters:
    host: podinfo2
  ....

Warning

In production, contexts can become large. Use debug mode sparingly.

Example 2: Context Overrides

In this example, we keep the objective (adding a subdomain) but use the podinfo-p02 package (which does not use project.subdomain). Instead, we will override ingress.domain.

Create a dedicated namespace:

kubectl create ns project02

Create a project context:

project02.yaml
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Context
metadata:
  name: project02
spec:
  description: Context for project 2
  context:
    project:
      id: p02
    ingress:
      domain: prj02.ingress.kubodoc.local
kubectl -n project02 apply -f contexts/project02.yaml

Notice that ingress.domain exists in both the cluster and project contexts. When merging, later contexts in the list override earlier ones.

Create and deploy the release:

podinfo-prj02.yaml
---
apiVersion: kubocd.kubotal.io/v1alpha1
kind: Release
metadata:
  name: podinfo
spec:
  description: A release of podinfo on project02
  package:
    repository: quay.io/kubodoc/packages/podinfo
    tag: 6.7.1-p02
  parameters:
    host: podinfo
  contexts:
    - namespace: contexts
      name: cluster
    - name: project02
  debug:
    dumpContext: true
    dumpParameters: true
kubectl -n project02 apply -f releases/podinfo-prj02.yaml

Check the Release context:

kubectl -n project02 get release podinfo -o yaml
status:
  context:
    ingress:
      className: nginx
      domain: prj02.ingress.kubodoc.local
    project:
      id: p02
    storageClass:
      data: standard
      workspace: standard

Verify the ingress host:

kubectl get --all-namespaces ingress
NAMESPACE   NAME            CLASS   HOSTS                                 ADDRESS        PORTS   AGE
default     podinfo1-main   nginx   podinfo1.ingress.kubodoc.local        10.96.218.98   80      2d20h
default     podinfo2-main   nginx   podinfo2.ingress.kubodoc.local        10.96.218.98   80      110m
project01   podinfo-main    nginx   podinfo.prj01.ingress.kubodoc.local   10.96.218.98   80      26m
project02   podinfo-main    nginx   podinfo.prj02.ingress.kubodoc.local   10.96.218.98   80      2m52s

Modifying Contexts

Any change to a context resource is automatically propagated to the Release objects that use it:

  • The Release objects that name it in spec.contexts.
  • Every Release, when the Config resources make it a global default context.
  • Every Release of its namespace, when the Config resources make its name a namespace default context.

However, actual updates (Helm upgrades) only occur for deployments that are genuinely affected by the change.

For example, modify the project01 context:

kubectl -n project01 patch context.kubocd.kubotal.io project01 --type='json' -p='[{"op": "replace", "path": "/spec/context/project/subdomain", "value": "project01" }]'

Observe the ingress update:

kubectl get --all-namespaces ingress

The host podinfo.project01.ingress.kubodoc.local should appear shortly.

To restore the original value:

kubectl -n project01 patch context.kubocd.kubotal.io project01 --type='json' -p='[{"op": "replace", "path": "/spec/context/project/subdomain", "value": "prj01" }]'