Use Case: Dynamic environments for Pull Requests
This use case was the initial and first use case why the Template Controller was created. You can use ListGithubPullRequests
to query the GitHub API for a list of pull requests on a GitHub Repo and then use the result inside a ObjectTemplate
to generate GitOps environments for new pull requests.
Flux
This example will create templated Kustomization objects. The means, that you should first install Flux on your cluster. The dev install variant should be sufficient.
podtato-head as example
This example uses the podtato-head demo project to demonstrate the
Template Controller. You must fork the repository and replace all occurrences of podtato-head as owner with your
own username. It is not recommended to blindly use the public repository as you this will lead to unverified and
potentially dangerous environments being deployed into your cluster!
GitHub credentials
In case you want to listen for PRs from a private repository (e.g. because you’ve forked podtato-head), you’ll need to store a GitHub personal access token inside a Kubernetes Secret.
  apiVersion: v1
kind: Secret
metadata:
  name: git-credentials
  namespace: default
stringData:
  github-token: "<your-github-token>"
  WARNING: Of course, in a real setup you would NOT store the plain token inside a manifest, but instead use Sealed Secrets or SOPS.
A dedicated ServiceAccount
The Template Controller uses service accounts to query matrix inputs and apply rendered objects. These service accounts
determine what the template can access and what not. In this example, we’ll create a service account with the
cluster-admin role, which you should NOT do in production. Instead, define your own Role or ClusterRole and
attach it to the service account. This role should have read/write access to all objects references in the matrix and
the rendered objects.
  apiVersion: v1
kind: ServiceAccount
metadata:
  name: podtato-head-envs-objecttemplate
  namespace: default
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: podtato-head-envs-objecttemplate
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  # WARNING, this is only for demo purposes. You should use a more restricted role for the ObjectTemplate
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: podtato-head-envs-objecttemplate
    namespace: default
  The above serviceAccount is then later referenced inside the ObjectTemplate object.
Listing GitHub pull requests
Listing pull requests from a GitHub repository can be done through the
ListGithubPullRequests CRD. It specifies the GitHub repository to use and
some filter options.
  apiVersion: templates.kluctl.io/v1alpha1
kind: ListGithubPullRequests
metadata:
  name: list-gh-prs
  namespace: default
spec:
  interval: 1m
  # Replace the owner with your username in case you forked podtato-head
  owner: podtato-head
  repo: podtato-head
  # Ignore closed PRs
  state: open
  # Only PR's that go against the main branch
  base: main
  # Replace `podtato-head` with your username. This will only allows heads from your own fork!
  # Otherwise, you risk deploying unsafe environments into your cluster!
  head: podtato-head:.*
  tokenRef:
    secretName: git-credentials
    key: github-token
  After applying this resource, the Template Controller will start to query the GitHub API for matching pull requests and
then store the results inside the status of the ListGithubPullRequests CR. Example:
  apiVersion: templates.kluctl.io/v1alpha1
kind: ListGithubPullRequests
metadata:
  name: list-gh-prs
  namespace: default
spec:
  ...
status:
  conditions:
  - lastTransitionTime: "2022-11-07T14:55:36Z"
    message: Success
    observedGeneration: 3
    reason: Success
    status: "True"
    type: Ready
  # The pullRequests list contains much more detailed info, but to keep it short I've reduced verbosity here
  pullRequests:
  - base:
      label: podtato-head:main
      ref: main
      repo:
        full_name: podtato-head/podtato-head
        name: podtato-head
      sha: de7e66af16d41b0ef83de9a0b3be6f5cf0caf942
    body: "..."
    created_at: "2022-02-02T23:06:28Z"
    head:
      label: vivek:issue-79_implement_ms_ketch
      ref: issue-79_implement_ms_ketch
      repo:
        full_name: vivek/podtato-head
        name: podtato-head
      sha: 6379b4c8f413dae70daa03a5a13de4267486fd59
    number: 151
    state: open
    title: '...'
    updated_at: "2022-02-04T03:53:03Z"
  The ObjectTemplate
The pullRequests field from the above status can then be used as an input into the an
ObjectTemplate.
  apiVersion: templates.kluctl.io/v1alpha1
kind: ObjectTemplate
metadata:
  name: pr-envs
  namespace: default
spec:
  serviceAccountName: podtato-head-envs-objecttemplate
  # This causes removal of templated objects in case they disappear from the rendered list of objects
  prune: true
  matrix:
    - name: pr
      object:
        ref:
          apiVersion: templates.kluctl.io/v1alpha1
          kind: ListGithubPullRequests
          name: list-gh-prs
        jsonPath: status.pullRequests
        expandLists: true
  templates:
    - object:
        apiVersion: v1
        kind: Namespace
        metadata:
          # Give each one its own namespace
          name: "podtato-head-{{ matrix.pr.head.label | slugify }}"
    - object:
        apiVersion: source.toolkit.fluxcd.io/v1beta2
        kind: GitRepository
        metadata:
          # The pullRequests status field from the ListGithubPullRequests is a reduced form of the REST API result
          # of https://docs.github.com/en/rest/pulls/pulls#list-pull-requests, meaning that fields like `head` and `base`
          # are also available.
          name: "podtato-head-{{ matrix.pr.head.label | slugify }}"
          namespace: default
        spec:
          interval: 5m
          url: "https://github.com/{{ matrix.pr.head.repo.full_name }}.git"
          ref:
            branch: "{{ matrix.pr.head.ref }}"
    - object:
        apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
        kind: Kustomization
        metadata:
          name: "podtato-head-env-{{ matrix.pr.head.label | slugify }}"
          namespace: default
        spec:
          interval: 10m
          targetNamespace: "podtato-head-{{ matrix.pr.head.label | slugify }}"
          sourceRef:
            kind: GitRepository
            # refers to the same GitRepository created above
            name: "podtato-head-{{ matrix.pr.head.label | slugify }}"
          path: "./delivery/kustomize/base"
          prune: true
  The above ObjectTemplate will create 3 objects per pull request:
- A namespace with the name podtato-head-{{ matrix.pr.head.label | slugify }}. Please note the use of Jinja2 templating. Details about what can be done can be found in theObjectTemplatedocumentation.
- A Flux GitRepository that points to repository and branch of the current pull request.
- A Flux Kustomization that is deployed into the above namespace.