convoy docs
Search

Change rendered objects for each cluster with mutations

Add the region of the target cluster to a Deployment, change its replicas, and check the result before convoy applies it.

This guide changes the rendered objects of a Release for its target cluster. You put a region key in the metadata of the Cluster. Then three mutations change the podinfo Deployment:

  • A goTemplate overlay adds a region label.
  • A jsonPatch sets the replicas.
  • A celTransform from a ConfigMap adds a label and an annotation.

The Release uses manual sync mode. Thus, you can examine the mutated objects before convoy applies them. For the full behavior, see Mutations.

Before you start#

  • Make sure that the GitRepository podinfo exists. See Deploy from Git.
  • Save each example under the file name in its title bar. The commands use these file names.

Steps#

1. Add the region to the Cluster#

This Cluster has the same name as the dev-local example. It adds the metadata key region.

guides/cluster-dev-local-with-region.yaml
apiVersion: core.convoy.kumodo.io/v1alpha1
kind: Cluster
metadata:
  name: dev-local
  namespace: default
spec:
  connectionMode: inCluster
  metadata:
    type: kind
    role: managed
    region: eu-west-1
  1. Apply the Cluster.

    sh
    kubectl apply -f guides/cluster-dev-local-with-region.yaml
  2. Make sure that the Cluster is ready.

    sh
    kubectl get clusters dev-local

Mutations read spec.metadata as cluster. A change to a Cluster does not start a reconcile of its Releases. A Release reads the new metadata at its next reconcile.

2. Create the ConfigMap for the CEL transform#

The CEL expression returns a map. convoy merges the map into each matching object. The expression reads self.name and cluster.region.

mutations/configmap-cel-transform.yaml
# celTransform content sourced from a ConfigMap.
#
# The CEL expression returns a map overlay that is RFC7386-merged onto each
# matched object. Available variables:
#   cluster -> the target Cluster's name (cluster.name) + its Spec.Metadata keys
#              (e.g. cluster.region)
#   self    -> the Release's name / namespace / labels / annotations
#   object  -> the object being mutated
#
#   mutations:
#     - type: celTransform
#       target: { group: apps, version: v1, kind: Deployment }
#       from: { kind: ConfigMap, name: mutation-cel-transform, path: transform.cel }
#   from.name and from.path may themselves be templated with {.cluster,.self}
#   (release-level only, no .object), e.g. name: "{{ .cluster.name }}-transform".
apiVersion: v1
kind: ConfigMap
metadata:
  name: mutation-cel-transform
  namespace: default
data:
  transform.cel: |
    {
      "metadata": {
        "labels": {"convoy.kumodo.io/release": self.name},
        "annotations": {"convoy.kumodo.io/region": cluster.region}
      }
    }
  1. Apply the ConfigMap in the namespace of the Release.

    sh
    kubectl apply -f mutations/configmap-cel-transform.yaml

In CEL, a read of a missing key is an error. If the Cluster has no region key, the render stops.

3. Create the Release#

The Release has three mutations. convoy runs them in the order of the list:

  1. goTemplate: an RFC 7386 overlay. It adds the label topology.kubernetes.io/region to the Deployment and to its pod template. The other fields stay.
  2. jsonPatch: an RFC 6902 operation. It changes /spec/replicas to 2.
  3. celTransform: the expression from the ConfigMap.

spec.syncPolicy.automatic is not set. Thus, the Release uses manual sync mode.

guides/release-podinfo-mutations.yaml
apiVersion: core.convoy.kumodo.io/v1alpha1
kind: Release
metadata:
  name: podinfo-mutations
  namespace: default
mutations:
  - type: goTemplate
    target:
      group: apps
      version: v1
      kind: Deployment
    inline:
      - |
        metadata:
          labels:
            topology.kubernetes.io/region: "{{ .cluster.region }}"
        spec:
          template:
            metadata:
              labels:
                topology.kubernetes.io/region: "{{ .cluster.region }}"
  - type: jsonPatch
    target:
      group: apps
      version: v1
      kind: Deployment
    patches:
      - op: replace
        path: /spec/replicas
        value: 2
  - type: celTransform
    target:
      group: apps
      version: v1
      kind: Deployment
    from:
      kind: ConfigMap
      name: mutation-cel-transform
      path: transform.cel
spec:
  clusterRef:
    name: dev-local
  source:
    type: gitRepository
    gitRepository:
      name: podinfo
      namespace: default
      ref:
        branch: master
  content:
    type: helm
    helm:
      path: charts/podinfo
      releaseName: podinfo-mutations
  syncPolicy:
    interval: 1m
  1. Apply the Release.

    sh
    kubectl apply -f guides/release-podinfo-mutations.yaml
  2. Examine the Release.

    sh
    kubectl get releases podinfo-mutations
  3. Make sure that APPLIED is False. In manual sync mode, convoy waits for a /sync request.

4. Preview the mutated objects#

  1. Show the rendered objects.

    sh
    kubectl convoy release preview podinfo-mutations
  2. Find the Deployment in the output.

  3. Make sure that it has the label topology.kubernetes.io/region: eu-west-1.

  4. Make sure that spec.replicas is 2.

  5. Make sure that it has the label convoy.kumodo.io/release: podinfo-mutations.

  6. Make sure that it has the annotation convoy.kumodo.io/region: eu-west-1.

5. Compare with the live objects#

  1. Show the difference between the render and the live objects.

    sh
    kubectl convoy release diff podinfo-mutations
  2. Examine the status of each object. Before the first apply, each object is Missing.

For the full result, add -o json.

6. Apply the render#

  1. Send a sync request.

    sh
    kubectl convoy release sync podinfo-mutations
  2. Monitor the Release until APPLIED is True.

    sh
    kubectl get releases podinfo-mutations -w

7. Add a mutation with the plugin#

The /mutations subresource holds the list of mutations. Save this list as extra.yaml. It is not a Kubernetes object.

yaml
- type: goTemplate
  target:
    group: apps
    version: v1
    kind: Deployment
  inline:
    - |
      metadata:
        annotations:
          example.convoy.kumodo.io/tier: "{{ if .cluster.tier }}{{ .cluster.tier }}{{ else }}none{{ end }}"

The if and else actions replace the default function. The dev-local Cluster has no tier key. Thus, the value is none.

  1. Add the mutation to the end of the list.

    sh
    kubectl convoy release mutations add podinfo-mutations -f extra.yaml
  2. Show the full list.

    sh
    kubectl convoy release mutations get podinfo-mutations

mutations add reads the list and writes it again with the new items. mutations set replaces the full list.

A write to /mutations starts an apply, also in manual sync mode. To examine a change before the apply, change mutations in the Release manifest. Then use kubectl apply, preview and diff before you send /sync.

Check the result#

  1. Show the difference again.

    sh
    kubectl convoy release diff podinfo-mutations
  2. Make sure that the output is in sync.

  3. Read the resolved mutations.

    sh
    kubectl get releases podinfo-mutations -o jsonpath='{.status.lastAttempted.resolvedMutations}'
  4. Make sure that the list has four entries. Each entry has an index, a type and a contentHash.

If a mutation fails, Applied is False with reason DeployFailed. An unknown type or from.kind gives reason Unsupported. Admission does not parse templates or compile CEL. These errors show only when convoy renders the Release.

Clean up#

  1. Delete the Release.

    sh
    kubectl delete releases podinfo-mutations
  2. Delete the ConfigMap.

    sh
    kubectl delete configmap mutation-cel-transform -n default

Next steps#

In the code

convoy v0.9.0