There are cases where an object can not be created before another object is created by some other component inside the cluster, meaning that you have no control over the input object.

A simple example is the Zalando Postgres Operator, which allows you to create a Postgres database with a Custom Resource. Inside the CR, you can define databases and users to be auto-created. When the operator creates these databases and users, it also auto-creates Kubernetes secrets with the credentials allowing you to access the databases.

These secrets can however not be used directly when connecting to the databases, as you’d usually have to build some connection urls (e.g. JDBC urls). Usually, one would create some kind of init script or something like that to build this url and then pass it to the application that wants to use it.

The Template Controller allows an alternative solution.

Using ObjectTemplate to transform secrets

Let’s assume you have a sample Postgres database deployed via the Zalando Postgres Operator. The operator has also created the following secret:

  apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: foo-user.acid-minimal-cluster.credentials.postgresql.acid.zalan.do
  namespace: default
data:
  password: aHNiSVF6MFJJa0hTd2ZxS1NiTG5YV3dUQUVqcUtTNFpvU2dyOXp4b3pzMmJvTE02WWl0eTE0YjJTZlNFTHExdw==
  username: Zm9vX3VzZXI=
  

Based on that secret, you’d like to create a new secret with the JDBC url generated.

RBAC

The ObjectTemplate requires a service account with proper access rights for the involved secrets:

  apiVersion: v1
kind: ServiceAccount
metadata:
  name: postgres-secret-transformer
  namespace: default
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: postgres-secret-transformer
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    # give the ObjectTemplate access to the two involved secrets
    resourceNames: ["zalando.acid-minimal-cluster.credentials.postgresql.acid.zalan.do", "transformed-postgres-secret"]
    verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: postgres-secret-transformer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: postgres-secret-transformer
subjects:
  - kind: ServiceAccount
    name: postgres-secret-transformer
    namespace: default
  

ObjectTemplate

Use the following ObjectTemplate to perform the transformation:

  apiVersion: templates.kluctl.io/v1alpha1
kind: ObjectTemplate
metadata:
  name: postgres-secret-transformer
  namespace: default
spec:
  serviceAccountName: postgres-secret-transformer
  prune: true
  matrix:
    - name: secret
      object:
        ref:
          apiVersion: v1
          kind: Secret
          name: zalando.acid-minimal-cluster.credentials.postgresql.acid.zalan.do
  templates:
  - object:
      apiVersion: v1
      kind: Secret
      metadata:
        name: "transformed-postgres-secret"
      stringData:
        jdbc_url: "jdbc:postgresql://acid-minimal-cluster/zalando?user={{ matrix.secret.data.username | b64decode }}&password={{ matrix.secret.data.password | b64decode }}"
        # sometimes the key names inside a secret are not what another component requires, so we can simply use different names if we want
        username_with_different_key: "{{ matrix.secret.data.username | b64decode }}"
        password_with_different_key: "{{ matrix.secret.data.password | b64decode }}"
  

This will lead to the following transformed-postgres-secret

  apiVersion: v1
kind: Secret
metadata:
  name: transformed-postgres-secret
  namespace: default
type: Opaque
data:
  jdbc_url: amRiYzpwb3N0Z3Jlc3FsOi8vaG9zdC9kYXRhYmFzZT91c2VyPWZvb191c2VyJnBhc3N3b3JkPWJVUU52Zkd4amduQUdiaEhOWkZkamtwZFFYbnk1aDdXNGlFU1YyWUxVNnVrRHdXWjBPMjdRb0NBdUJTTnF3TVk=
  password_with_different_key: YlVRTnZmR3hqZ25BR2JoSE5aRmRqa3BkUVhueTVoN1c0aUVTVjJZTFU2dWtEd1daME8yN1FvQ0F1QlNOcXdNWQ==
  username_with_different_key: Zm9vX3VzZXI=
  

Base64 decoding the secret data will show:

  jdbc_url: jdbc:postgresql://host/database?user=foo_user&password=bUQNvfGxjgnAGbhHNZFdjkpdQXny5h7W4iESV2YLU6ukDwWZ0O27QoCAuBSNqwMY                                                                                                                                                                                      │
password_with_different_key: bUQNvfGxjgnAGbhHNZFdjkpdQXny5h7W4iESV2YLU6ukDwWZ0O27QoCAuBSNqwMY                                                                                                                                                                                                                          │
username_with_different_key: foo_user