Patch strategy

By default, KubeVela will merge patched values with CUE’s merge. However, CUE cannot handle conflicting fields currently.

KubeVela provides a series of patching strategies to help resolve conflicting issues. When writing patch traits and workflow steps, you can use these patch strategies to solve conflicting values. Note that the patch strategy is not an official capability provided by CUE, but an extension developed by KubeVela.

For more information about how to patch definitions, please refer to Patch in the Definitions.

Let’s write an env-patch trait to show how to use these patch strategies.

If you want to add multiple environment variables for a specific container, you can use the +patchKey=name annotation to find the container. In this case, KubeVela will merge these environment variables by default. This means that patchKey cannot handle duplicate fields.

After KubeVela version 1.4, you can use , to split multiple patchKeys, such as patchKey=name,image.

Apply the following definition to your cluster:

  1. myenv: {
  2. type: "trait"
  3. annotations: {}
  4. labels: {
  5. "ui-hidden": "true"
  6. }
  7. description: "Add env on K8s pod for your workload which follows the pod spec in path 'spec.template'"
  8. attributes: appliesToWorkloads: ["*"]
  9. }
  10. template: {
  11. patch: {
  12. spec: template: spec: {
  13. // +patchKey=name
  14. containers: [{
  15. name: context.name
  16. env: [
  17. for k, v in parameter.env {
  18. name: k
  19. value: v
  20. },
  21. ]
  22. }]
  23. }
  24. }
  25. parameter: {
  26. env: [string]: string
  27. }
  28. }

Use the above myenv trait in your application:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: webservice-app
  5. spec:
  6. components:
  7. - name: express-server
  8. type: webservice
  9. properties:
  10. image: crccheck/hello-world
  11. env:
  12. - name: OLD
  13. value: old
  14. traits:
  15. - type: myenv
  16. properties:
  17. env:
  18. NEW: new

Before using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: OLD
  5. value: old

After using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: OLD
  5. value: old
  6. - name: NEW
  7. value: new

Finally, we can see that the application’s env contains two environment variables: OLD=old and NEW=new.

You can use the +patchStrategy=retainKeys annotation if you want to be able to override duplicate values while merging variables.

The strategy of this annotation is similar to the Kubernetes official [retainKeys](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#use-strategic-merge-patch- to-update-a-deployment-using-the-retainkeys-strategy) strategy.

In the following example, +patchKey=name specifies which container the patch should be applied to, while +patchStrategy=retainKeys specifies that when merge environment variables, if a duplicate environment variable name is specified, the environment variable value will be overwritten.

  1. myenv: {
  2. type: "trait"
  3. annotations: {}
  4. labels: {
  5. "ui-hidden": "true"
  6. }
  7. description: "Add env on K8s pod for your workload which follows the pod spec in path 'spec.template'"
  8. attributes: appliesToWorkloads: ["*"]
  9. }
  10. template: {
  11. patch: {
  12. spec: template: spec: {
  13. // +patchKey=name
  14. containers: [{
  15. name: context.name
  16. // +patchStrategy=retainKeys
  17. env: [
  18. for k, v in parameter.env {
  19. name: k
  20. value: v
  21. },
  22. ]
  23. }]
  24. }
  25. }
  26. parameter: {
  27. env: [string]: string
  28. }
  29. }

Use the above myenv trait in your application:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: webservice-app
  5. spec:
  6. components:
  7. - name: express-server
  8. type: webservice
  9. properties:
  10. image: crccheck/hello-world
  11. env:
  12. - name: OLD
  13. value: old
  14. - name: OLD2
  15. value: old2
  16. traits:
  17. - type: myenv
  18. properties:
  19. env:
  20. NEW: new
  21. OLD2: override

Before using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: OLD
  5. value: old
  6. - name: OLD2
  7. value: old2

After using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: OLD
  5. value: old
  6. - name: OLD2
  7. value: override
  8. - name: NEW
  9. value: new

Finally, we can see that the application’s env contains three environment variables: OLD=old, OLD2=override and NEW=new.

If you wish to replace the entire env array directly, you can use the +patchStrategy=replace annotation.

In the following example, +patchKey=name specifies which container the patch should be applied to, while +patchStrategy=replace specifies that when merge the arrays, the entire array of environment variables will be replaced directly.

  1. myenv: {
  2. type: "trait"
  3. annotations: {}
  4. labels: {
  5. "ui-hidden": "true"
  6. }
  7. description: "Add env on K8s pod for your workload which follows the pod spec in path 'spec.template'"
  8. attributes: appliesToWorkloads: ["*"]
  9. }
  10. template: {
  11. patch: {
  12. spec: template: spec: {
  13. // +patchKey=name
  14. containers: [{
  15. name: context.name
  16. // +patchStrategy=replace
  17. env: [
  18. for k, v in parameter.env {
  19. name: k
  20. value: v
  21. },
  22. ]
  23. }]
  24. }
  25. }
  26. parameter: {
  27. env: [string]: string
  28. }
  29. }

Use the above myenv trait in your application:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: webservice-app
  5. spec:
  6. components:
  7. - name: express-server
  8. type: webservice
  9. properties:
  10. image: crccheck/hello-world
  11. env:
  12. - name: OLD
  13. value: old
  14. - name: OLD2
  15. value: old2
  16. traits:
  17. - type: myenv
  18. properties:
  19. env:
  20. NEW: replace

Before using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: OLD
  5. value: old
  6. - name: OLD2
  7. value: old2

After using the myenv patch trait, the env in the application is like:

  1. spec:
  2. containers:
  3. - env:
  4. - name: NEW
  5. value: replace

Finally, we can see that the application’s env contains one environment variable: NEW=replace.

Last updated on Aug 4, 2023 by Daniel Higuero