Markers¶
Operator Builder uses commented markers as the basis for defining a new API.
The fields for a custom resource kind are created when it finds a +operator-builder
marker in a source manifest.
A workload marker is commented out so the manifest is still valid and can be
used if needed. The marker must begin with +operator-builder followed by some
colon-separated fields:
These markers should always be provided as an in-line comment or as a head
comment. The marker always begins with +operator-builder:field: or
+operator-builder:collection:field: (more on this later).
That is followed by arguments separated by ,. Arguments can be given in any order.
Arguments¶
Arguments come after the actual marker and are separated from the marker name
with a :. They are given in the format of argument=value and separated by
the ,. Additionally, if the argument name is given by itself with no value, it
is assumed to have an implict =true on the end and is treated as a flag.
Below you will find the supported markers and their supported arguments.
Field Markers¶
Defined as +operator-builder:field this marker can be used to define a CRD
field for your workload.
| Field | Type | Required |
|---|---|---|
| name | string | true |
| type | string{string, int, bool, stringArray, stringMap} | true |
| default | type | false |
| replace | string | false |
| merge | bool | false |
| arbitrary | bool | false |
| description | string | false |
Name (required if Parent is unspecified)¶
The name you want to use for the field in the custom resource that Operator Builder will create. If you're not sure what that means, it will become clear shortly.
Example:
1 | |
Parent (required if Name is unspecified)¶
The parent field in which you wish to substitute. Currently, only metadata.name and metadata.namespace are supported.
This will allow you to use the parent name or namespace as a value in the child resource.
Example:
1 | |
The metadata.name and metadata.namespace fields from the collection workload is also supported:
1 | |
Type (required)¶
The other required field is the type field which specifies the data type for
the value.
The supported Go data types are:
boolstringintstringArray(an[]stringGo data type)stringMap(amap[string]stringGo data type)
ex. +operator-builder:field:name=myName,type=string
stringArray fields¶
Use type=stringArray to define a CRD field that holds a list of strings. Place
the marker as a head comment above a YAML sequence:
1 2 3 4 | |
The generated Go spec field will be []string and can carry an optional default
using semicolon-separated syntax (aligning with kubebuilder's enum convention):
1 2 3 4 | |
Note: The
replace=argument is not supported forstringArrayfields.Note: Default values that start with
-or contain=,,, or:must be wrapped in double quotes so the marker parser treats them as string literals:default="--log-level;info". Unquoted values starting with-are tokenised as numeric literals, which causes a parse error.Note: To include a literal semicolon inside one element, escape it with a backslash:
default=foo\;bar;bazproduces["foo;bar", "baz"].
stringMap fields¶
Use type=stringMap to define a CRD field that holds a map[string]string. Place the
marker as a head comment above a YAML mapping:
1 2 3 4 | |
The generated Go spec field will be map[string]string.
stringMap fields are always optional. An absent or empty map is equivalent to
map[string]string{} — no +kubebuilder:validation:Required is ever emitted for this
type regardless of whether a default= is provided.
A default can be specified using semicolon-separated key=value pairs:
1 2 3 4 | |
Important: The
default=value for astringMapfield must always be wrapped in double quotes. Each pair contains an=sign, and the marker parser uses=to separate argument names from values. Without quotes the parser would misreadAPP_ENV=productionas two separate arguments. Always write:default="KEY1=value1;KEY2=value2".Note: Values may themselves contain
=signs — only the first=in each pair is treated as the key/value separator:default="JDBC_URL=jdbc:postgresql://host/db?ssl=true"produces{"JDBC_URL": "jdbc:postgresql://host/db?ssl=true"}.Note: The
replace=argument is not supported forstringMapfields.Note: To include a literal semicolon inside a value, escape it with a backslash:
default="A=foo\;bar"produces{"A": "foo;bar"}.
merge flag for stringMap fields¶
The merge flag merges the static key-value pairs already in the YAML manifest with the
user-supplied map from the custom resource spec. User-supplied values always win — keys
present in both the manifest and the spec take their value from the spec.
1 2 3 4 | |
Given the above marker, operator-builder emits code equivalent to:
1 2 3 4 5 6 | |
You can combine merge with a default= to seed the spec field as well:
1 2 3 | |
This makes the configData spec field default to {"DEBUG": "false"}, which is then
merged on top of the static {"LOG_LEVEL": "info"} at runtime.
Note:
mergeis only valid forstringMapfields. Using it with any other type returns a parse error.Note:
mergeandreplace=are mutually exclusive; use one or the other.
Default (optional)¶
This will make configuration optional for your operator's end user. the supplied value will be used for the default value. If a field has no default, it will be a required field in the custom resource. For example:
1 | |
Replace (optional)¶
There may be some instances where you only want a specific portion of a value to be configurable (such as config maps). In these scenarios you can use the replace argument to specify a search string (or regex) to target for configuration.
Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
In this scenario three custom resource fields will be generated. The value from
the environment field will replace the dev portion of myapp-dev. For
example, if prod is provided as a value for the environment field, the
resulting config map will get the label app: myapp-prod. Values from the
configOption and yamlType fields will replace corresponding strings in the
content of config.yaml. The resulting configmap will look as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Merge (optional)¶
The merge flag is only valid for stringMap fields. When set, the static
key-value pairs in the YAML manifest are used as a base and the user-supplied
map from the custom resource spec is merged on top — user values always win.
1 2 3 | |
See stringMap merge for full details and examples.
Arbitrary (optional)¶
If you wish to create a field for a custom resource that does not directly map to a value in a child resource, mark a field as arbitrary.
Here is an example of how to mark a field as arbitrary:
1 2 3 4 5 6 7 8 9 10 | |
On the first line you can see the nginx.installType custom resource field is
marked as arbitrary with the arbitrary marker field. Where you place this
marker is unimportant but it is recommended you put all arbitrary fields at the
beginning of one chosen manifest for ease of maintenance.
This will result in a custom resource sample that looks as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
This arbitrary field will not map to any child resource value. However it can be leveraged by some custom mutation code or by a resource marker such as this:
1 2 3 4 5 6 7 8 | |
The marker on line one indicates the deployment resource only be created if
nginx.installType has a value of deployment (as shown in the custom resource
sample above). In this example, we are providing an option to install the Nginx
Ingress Controller as a deployment or a daemonset.
Description (optional)¶
An optional description can be provided which will be used in the source code as
a Doc String, backticks ` may be used to capture multiline strings (head
comments only).
By injecting documentation to
the CRD, the consumer of the custom resource gets the added benefit by being
able to run kubectl explain against their resource and having documentation
right at their fingertips without having to navigate to API documentation in
order to see the usage of the API. For example:
1 | |
Note: that you can use a single custom resource field name to configure multiple fields in the resource.
Consider the following Deployment:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
In this case, operator-builder will create and add three fields to the custom resource:
- A
productionfield that is a boolean. It will have a default offalseand will inform the value of the label when the deployment is configured. - A
webAppReplicasfield that will default to2and allow the user to specify the number of replicas for the deployment in the custom resource manifest. - A
webAppImagefield that will set the value for the images used in the pods.
Now the end-user of the operator will be able to define a custom resource similar to the following to configure the deployment created:
1 2 3 4 5 6 7 8 | |
Struct Markers¶
Defined as +operator-builder:struct this marker annotates a generated Go struct type with a
description. Unlike a field marker, it does not create a new API field — it attaches documentation
to a struct that is already created by the field markers nested within it.
| Field | Type | Required |
|---|---|---|
| name | string | true |
| description | string | true |
Struct Name (required)¶
The dot-separated path of the struct to annotate, matching the prefix used in the nested field
marker names. For example, if you have a field marker name=subscriptionManager.image, the
corresponding struct name is subscriptionManager.
Use the special value "." to target the root Spec struct itself.
1 2 3 4 | |
1 2 3 4 | |
Struct Description (required)¶
The description to emit as a comment above both the field declaration in the parent struct and the
type declaration. kubebuilder reads both locations to populate description in the generated CRD
schema, so this text will appear in kubectl explain output.
1 2 3 4 5 6 | |
Struct marker constraints¶
A struct marker must have at least one field marker nested within the named struct. If the named path does not correspond to any existing struct node (because no field markers were defined for it), processing fails with an error.
The marker can be placed as a head comment above any YAML node in the resource manifest — it does not need to be directly above the YAML key that corresponds to the struct. Placing it above the first node of the manifest document or above the relevant map key are both valid and common patterns.
Struct markers in collection components¶
Struct markers work the same way in collection components. A struct marker placed in a component resource file annotates that component's spec; a struct marker placed in a collection resource file annotates the collection's spec.
1 2 3 4 5 | |
1 2 3 4 5 6 7 | |
Generated output¶
Given a struct marker and matching field markers, operator-builder emits the description in two
places in the generated _types.go file:
1 2 3 4 5 6 7 8 9 | |
Collection Markers¶
A second marker type +operator-builder:collection:field can be used with the
same arguments as a Field Marker. These markers are used to define global fields
for your Collection and can be used in any of its associated components.
If you include any marker on a collection resource it will be treated as a collection marker and will configure a field in the collection's custom resource.
Resource Markers¶
Defined as +operator-builder:resource this marker can be used to control a specific
resource with arguments in the marker.
Note: a resource marker must reference a field defined by a field marker. If you include a resource marker with a unique field name that is not also defined by a field marker you will get an error. You may use an arbitrary field on a field marker if you don't wish to associate the field with a value in a child resource.
| Field | Type | Required |
|---|---|---|
| field | string | true |
| collectionField | string{string, int, bool} | true |
| value | type | true |
| include | bool | true |
Field / CollectionField (required)¶
The conditional field to associate with an action (currently only include).
One of field or collectionField must be provided depending upon if you are
checking a condition against a collection, or a component/standalone workload spec.
The field input relates directly to a given workload marker such as
+operator-builder:field:name=provider would produce a field of provider to be used
in a resource marker with argument field=provider.
ex. +operator-builder:resource:collectionField=provider,value="aws",include ex. +operator-builder:resource:field=provider,value="aws",include=false
Value (required)¶
The conditional value to associate with an action (currently only include - see
above). The value input relates directly to the value of field as it exists
in the API spec requested by the user.
Examples:
1 2 | |
Include (required)¶
The action to perform on the resource. Include will include the resource for
deployment during a control loop given a field or collectionField and a value.
Using this means that the resource will only be included if this condition
is met. If the condition is not met, the resource will not be deployed.
Here are some sample marker examples:
1 2 3 4 | |
With include set to false, the opposite is true and the resource is
excluded from being deployed during a control loop if a condition is met:
Examples:
1 2 | |
At this time, the include argument with field and value can be simply thought of
as (pseudo-code):
1 2 3 4 5 | |
IMPORTANT: A resource marker is not required and should only be used when there is a desire to act upon a resource. If no resource marker is provided, a resource is always deployed during a control loop.
Include Resource On Condition¶
Below is a sample of how to include a resource only if a condition is met. If the condition is not met, the resource is not deployed during the control loop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Given the below CRD, the resource would be included:
1 2 3 4 5 6 7 | |
Given the below CRD, the resource would NOT be included:
1 2 3 4 5 6 7 | |
Exclude Resource On Condition¶
Below is a sample of how to exclude a resource only if a condition is met. If the condition is not met, the resource is not deployed during the control loop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Given the below CRD, the resource would be included:
1 2 3 4 5 6 7 | |
Given the below CRD, the resource would NOT be included:
1 2 3 4 5 6 7 | |
Stacking Resource Markers¶
You can include multiple resource markers on a particular resource. For example:
1 2 3 4 5 6 7 8 | |
The purpose of the first marker is to include all nginx ingress contoller
resources when spec.nginx.include: true. The second gives users a choice
to install nginx ingress controller as a deployment or daemonset. When
spec.nginx.installType: deployment the deployment resource is included.
Therefore the custom resource will need to look as follows for this deployment
resource to be created:
1 2 3 4 5 6 7 8 9 10 11 | |
The resulting source code looks as follows. If either if-statement is evaluated as true, the function will return without any object - hence the deployment will not be included.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |