Profiles: Patches
Patches are a way to perform minor overrides to the configuration without having to create a separate config file. Patch functionality follows JSON Patch(RFC) semantics, as implemented by the yaml-patch library.
Example
Patches are ideal for reflecting changes between different environments, e.g. dev, staging and production.
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
- name: backend
helm:
componentChart: true
values:
containers:
- image: john/devbackend
- image: john/debugger
profiles:
- name: production
patches:
- op: replace
path: images.backend.image
value: john/prodbackend
- op: remove
path: deployments.name=backend.helm.values.containers[1]
- op: add
path: deployments.name=backend.helm.values.containers
value:
image: john/cache
Explanation:
- The above example defines 1 profile:
production
- When using the profile
production
, the config would be patched with 3 patches. - The resulting config used in-memory when the profile
production
is used would look like this:
# In-Memory Config After Applying Patches For Profile `production`
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
- name: backend
helm:
componentChart: true
values:
containers:
- image: john/prodbackend
- image: john/cache
Configuration
name
The name
option is mandatory and expects a string defining the name of the profile.
patches
The patches
option expects a patch object which consists of the following properties:
op
stating the patch operation (possible values:replace
,add
,remove
)path
stating a jsonpath or an xpath within the config (e.g.images.backend.image
,deployments.name=backend.helm.values.containers[1]
)value
stating an arbitrary value used by the operation (e.g. a string, an integer, a boolean, a yaml object)
op: add
only for arraysUsing op: add
only works as expected when path
points to an array value. Using op: add
to add properties to an object (e.g. deployments[*].helm.values
) will not work and instead replace all existing properties.
When you want to define a path
that contains an array (e.g. deployments
), you have two options:
- Use the index of the array item you want to patch, e.g.
deployments[0]
- Use a property selector matching the array item(s) you want to patch, e.g.
deployments.name=backend
Using a property selector is often better because it is more resilient and will not cause any issues even if the order of an array's items is changed later on. A property selector is also able to select multiple array items if all of them have the same value for this property.
If you use the replace
or add
operation, value
is a mandatory property.
If value
is defined, the new value must provide the correct type to be used when adding or replacing the existing value found under path
using the newly provided value
, e.g. an array must be replaced with an array.
The patches
of a profile can modify all parts of the configuration except the sections profiles
and commands
.
Example: Config Patches
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
- name: backend
helm:
componentChart: true
values:
containers:
- image: john/devbackend
- image: john/debugger
profiles:
- name: staging
patches:
- op: replace
path: images.backend.image
value: john/stagingbackend
- op: remove
path: deployments.name=backend.helm.values.containers[1]
- name: production
patches:
- op: replace
path: images.backend.image
value: john/prodbackend
- op: remove
path: deployments.name=backend.helm.values.containers[1]
- op: add
path: deployments.name=backend.helm.values.containers
value:
image: john/cache
Explanation:
- The above example defines 2 profiles:
staging
,production
- Users can use the flag
-p staging
to use thestaging
profile for a single command execution - Users can use the flag
-p production
to use theproduction
profile for a single command execution - Users can permanently switch to the
staging
profile using:devspace use profile staging
- Users can permanently switch to the
production
profile using:devspace use profile production
parent
The parent
option is optional and expects the name of another profile which should be applied before this profile. The kind of profile inheritance that the parent
option provides can help to reduce redundancy when multiple profiles need to change the config in a similar way.
A parent profile is applied before the profile that defines the parent. A parent profile can have a parent of its own.
Example: Defining a Parent Profile
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
- name: backend
helm:
componentChart: true
values:
containers:
- image: john/devbackend
- image: john/debugger
profiles:
- name: production
parent: staging
patches:
- op: add
path: deployments.name=backend.helm.values.containers
value:
image: john/cache
- name: staging
replace:
images:
backend:
image: john/backendprod
patches:
- op: replace
path: deployments.name=backend.helm.values.container[0].image
value: john/backendprod
- op: remove
path: deployments.name=backend.helm.values.containers[1]
When the production
profile is active, the replace
and patches
statements configured in staging
would be applied first because of the parent: staging
statement in line 16. After applying the staging
profile, DevSpace would additionally apply the currently active production
profile. In this example, the production
profile is based on the staging
profile and the only difference is that the production
profile adds another container to the backend
deployment which is using the image john/cache
.
Useful Commands
devspace print -p [profile]
The following command is useful to verify that the profile modifies the config as intended:
devspace print -p my-profile
The above command would print the config after applying all profile patches
and replace
statements as well as after replacing all config variables.