Package Templates

Package Operator supports templates to pre-process objects with configuration and environment information before applying them to a Kubernetes Cluster.

Adding the .gotmpl suffix to a file, marks it as a Go Template.
All templates have access to Template Functions in addition to Go templates built-in functionalities. Package Operator also provides context, configuration and environment information to the templates: Package Operator API - TemplateContext.

Templates within packages are only executed once, when a package is being unpacked.
A package is unpacked when it is being created or either .spec.image or .spec.config changes.

CEL Expressions

Package Operator templates provide functionality for CEL expression evaluation, see CEL Expressions.

Test Framework

Because running YAML files through a template engine can lead to unexpected results, Package Operator provides a template test framework to quickly iterate over changes locally and spot unwanted changes and regressions early. It can also be used within CI/CD pipelines.

The test framework is configured by adding test cases to the Package Manifest, like this:

apiVersion: manifests.package-operator.run/v1alpha1
kind: PackageManifest
# [...]
test:
  template:
    # Name of the test-case
  - name: '15-bananas'
    # Template context to run template engine with.
    context:
      package:
        name: banana
      config:
        replicas: 15
      environment:
        openShift:
          version: v4.12.5

Running kubectl package validate <path-to-package> will now create a .test-fixtures/<test-case> folder, when it does not already exist. This folder will contain the output of rendering the package templates with the given context information to check if the templates work as expected.

Subsequent runs of the validate command will compare the new output with the test fixtures and alert on differences.
Adjust both the templates and the test fixtures at the same time to have the validation pass.

Example validate output

Error: validating package: loading package from files: File mismatch against fixture in deployment.yaml: Testcase "my-testcase"
--- FIXTURE/deployment.yaml
+++ ACTUAL/deployment.yaml
@@ -8,7 +8,7 @@
   annotations:
     package-operator.run/phase: deploy
 spec:
-  replicas: 4
+  replicas: 1
   selector:
     matchLabels:
       app.kubernetes.io/name: nginx

Example Template

Saved as deployment.yaml.gotmpl:

apiVersion: apps/v1
kind: Deployment
metadata:
  # Resolves to the name of the (Cluster)Package object.
  name: "test-stub-{{.package.metadata.name}}"
{{- if eq .package.metadata.namespace ""}}
  # Included if package is installed in a Namespace scope.
  # e.g. via the Package-API and not via the ClusterPackage-API.
  namespace: "{{.package.metadata.name}}"
{{- end}}
  labels:
    app: test-stub
    instance: "{{.package.metadata.name}}"
  annotations:
    # Dumps all available Environment information into this annotation.
    test-environment: {{.environment | toJson | quote}}
    package-operator.run/phase: deploy
spec:
  # Puts in the value supplied via .spec.config.replicas of the Package object.
  replicas: {{.config.replicas}}
  selector:
    matchLabels:
      app: test-stub
      instance: "{{.package.metadata.name}}"
  template:
    metadata:
      labels:
        app: test-stub
        instance: "{{.package.metadata.name}}"
    spec:
      containers:
      - name: test-stub
        # Filled by resolved image digest, if added to PackageManifest.
        # see guide on "Working with image digests"
        image: "{{ index .images "test-stub" }}"