Getting Started
Note
Cezanne implementation is yet intended a full Yupiik BundleBee replacement and you can refer to Yupiik Bundlebee documentation.
Defining your recipes
Note
Cezanne uses recipe naming where Yupiik BundleBee uses alveolus. It is exactly the same concept of an entry point of a deployment set of descriptors (with or without transitive dependencies).
A recipe defines a set of descriptors to deploy to Kubernetes.
The convention of Cezanne is to start from a manifest.json file set under bundlebee folder (or anywhere actually but it is generally better to respect this convention).
The manifest defines the list of recipes:
{
"recipes": [ ... ]
}
Tip
Since Cezanne tries to be as compatible as possible with Yupiik BundleBee, you can also use:
{
"alveoli": [ ... ]
}
Then every recipe defines its name (identifier) and (optionally) a set of descriptors/dependencies to install.
{
"recipes": [
{
"name": "my-recipe",
"descriptors": [
{ "name": "my-descriptor.json" }
]
}
]
}
A descriptor is a Kubernetes descriptor located in bundlebee/kubernetes/ folder (this is why it is better to put the manifest.json in bundlebee folder).
Its name is actually a path related to kubernetes folder so it is common to use some subtree there (deployments, cronjobs, ...).
The descriptor can be in json, yaml, handlebars or even cs (CSharp Script - in this case the script must return the descriptor as a string).
Tip
It is always better to use json format since it is the easiest one to get generated but also parsed in any language (so pipeline/tooling).
Here is a sample JSON descriptor to install a ConfigMap - but any type of Kubernetes resource will work:
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "my-cm",
"namespace": "default"
},
"data": {
"SAMPLE": "true"
}
}
Interpolate your descriptors
To go further, the descriptor can be marked as interpolated:
{
"name": "my-recipe",
"descriptors": [
{
"name": "my-descriptor.json",
"interpolate": true
}
]
}
Then you can use placeholders to populate the values marked with {{xxx}} in the descriptor.
Warning
It looks like mustache/handlebars templates but this interpolation - when extension is not hb or handlebars - is just using a simple templating engine, don't try to do too advanced things except setting default ({{key:-default}} and nesting placeholders if needed).
Now the descriptor can use the interpolation syntax to get value interpolated either from environment variables or the cezanne.json file (Microsoft configuration/settings):
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "my-cm",
"namespace": "{{my.namespace}}"
},
"data": {
"SAMPLE": "{{my.sample:-true}}"
}
}
Tip
To keep things easy, the placeholder keys are converted to UNIx like environment variables so my.placeholder will become MY_PLACEHOLDER - dot are replaced with underscores and case is uppercased.
You can also scope the placeholder values using placeholders map of string in any recipe.
This enables to define custom recipes which are templates to share common practise accross deployments and set variables when important them.
Important
The descriptor name must be an identifier accross all recipes.
Other descriptor formats
The descriptors can use these formats (take care it must match the extension of the descriptor - and its name to be enabled):
json- as seen, it is the recommended format.
yaml- since it is very common in Kubernetes ecosystem it is supported but not recommended since it is very error prone (indentation).
hborhandlebars- enables to use a Handlebars template where
recipe(JSON recipe as inmanifest.json),descriptor(defines descriptor extension, content, ...),executionId(execution identifier - GID) andbundlebee(enables to accesskubernetes.base- API - andkubernetes.namespace- default namespace if set) variables are defined. cs- enables to use a CSharp script (Roselyn) template where the script must return a string which will be the descriptor. The variables
K8s,Id,Substitutor,DescandRecipeare available globally (not in nested class but in the root script scope). They globally match the Handlebars case exceptSubstitutorwhich has aReplace(Manifest.Recipe? recipe, LoadedDescriptor? desc, string source, string? id)method (default values being the global variables) which enables to have some more advanced placeholders (see next part).
Built-in placeholders
Important
This part is only relevant for JSON and Yaml formats enabling interpolation.
Normally, Cezanne supports all Yupiik BundleBee placeholders except jsr223 one which is Java specific:
alveolus.name: name of the alveolus the descriptor comes from,descriptor.name: name of the descriptor,bundlebee-strip:<value>: strips the provided value,bundlebee-strip-leading:<value>: strips the provided value at the beginning,bundlebee-strip-trailing:<value>: strips the provided value at the end,bundlebee-indent:<indent size>:<value>: indents a value with the provided space size, it is generally combined with another interpolation (file ones in particular) as value, ex{{bundlebee-indent:8:{{bundlebee-inline-file:myfile.txt}}}},bundlebee-inline-file:<file path>: load the file content as value,bundlebee-base64-file:<file path>: converts the file in base64 (useful to writedata:xxxx,<base64>values for ex keeping the raw file in the filesystem, very helpful for images),bundlebee-base64-decode-file:<file path>: decode the file from a base64 content,bundlebee-base64:<text>: encodes in base64 the text,bundlebee-base64-decode:<text>: decode from base64 to text the valuetext,bundlebee-digest:BASE64|HEX,<algorithm>,<text>: computes the digest of the text encoded in base64 or hexa format (useful to read files likeConfigMaporSecretand inject their digest value in aDeploymentannotations to force its reload for example),bundlebee-quote-escaped-inline-file:<file path>: load the file content as a quoted value,bundlebee-json-inline-file:<file path>: load the file content as a JSON string value - without quotes,bundlebee-json-string:content: escapes a string to be a JSON string (useful when you inject withbundlebee-json-inline-filea string in another string like in JSONConfigMapin a JSON configuration),bundlebee-maven-server-username:<server id>: extract from your mavensettings.xmla server username (see configuration for a custom settings.xml location),bundlebee-maven-server-password:<server id>: extract from your mavensettings.xmla server password (potentially deciphered),bundlebee-decipher: use Maven ciphering logic (AES/CBC/PKCS5Padding) to read a clear value. Syntax is as follow{{bundlebee-decipher:$masterKey,$cipheredValue}}withmasterKeyanother placeholder name which contains the master key andcipheredValuethe value to decipher. Indeed we recommend to reference the master key with another shared placeholder if you use a single one ({{bundlebee-decipher:{{myMasyterKey}},$cipheredValue}}). Last, since the value will be surrounded by{and}, we tolerate spaces before/after to avoid any confusion with the mustable like syntax we use for placeholders:{{bundlebee-decipher:{{my.master.key}}, {...base64....} }},bundlebee-kubernetes-namespace: the Kubernetes namespace defined in the HttpKubeClient,kubeconfig.cluster.<cluster name>.ip: extract cluster IP from your kubeconfig,timestamp: current time in milliseconds (since epoch),timestampSec: current time in seconds (since epoch),now:OffsetDateTime.now()value,date:<pattern>: formatOffsetDateTime.now()value using the provided pattern,nowUTC:OffsetDateTime.now()value with UTCZoneId,kubernetes.<namespace>.serviceaccount.<account name>.secrets.<secret name prefix>.data.<entry name>[.<timeout in seconds>]: secret value looked up through Kubernetes API.bundlebee-directory-json-key-value-pairs:/path/to/dir: creates a JSON object from a directory. The path can be either a directory path of a directory path with a glob pattern at the end (/path/to/dir/*.txtfor example). The keys are the filenames (simple) and____are converted to/. The values are the content of the files, filtered. Note it only works with exploded folders, not jar resources as of today.bundlebee-directory-json-key-value-pairs-content:/path/to/dir: same asbundlebee-directory-json-key-value-pairsbut dropping enclosing brackets to be able to embed the content in an existing object (likeannotations). Ex:{{bundlebee-directory-json-key-value-pairs-content:resources/app/annotations/*.txt}}.
Patching dependencies/descriptors
Since sometimes we import dependencies and they don't 100% do what we want, we can patch descriptors either directly from the recipe (useful when combined with next part) or from a HoR (Higer order Recipe - a recipe importing another recipe).
You just have to fill the patches array of the recipe which will contain a list of descriptor name associated with a JSON-Patch:
{
"name": "root-recipe",
"descriptors": [
{
"name": "root/descriptor.json"
}
],
"patches": [
{
"descriptorName": "root/descriptor.json",
"includeIf": {
"conditions": [
{
"type": "ENV",
"key": "PATCH_ROOT_DESCRIPTOR",
"value": "true"
}
]
},
"patch": [
{
"op": "add",
"path": "/metadata/labels/patched",
"value": "true"
}
]
}
]
}
This recipe will install root/descriptor.json and if the environment variable PATCH_ROOT_DESCRIPTOR is set to true then the label patched will be set to true in the descriptor.
Indeed you can use any JSON-Patch operation including replace or remove.
Tip
You can patch a descriptor without any condition as well - it is common for transitive dependencies.
Conditions
We saw a bit in previous part but it can make sense to have some conditions on patches but also on deploying descriptors or dependencies.
This is why descriptor and dependency definitions can have an includeIf object as well.
It works exactly as for patches but either on descriptors or for dependencies installation.
More on manifest.json documentation.
Bundling and sharing your recipes
The recipes can be written in an exploded folder when passing the manifest location explicitly - it is often the case for final deployable recipes but more rarely for dependencies.
However, if you want to bundle your recipes you can do it in a zip and expose it in an Apache Maven repository (plain http server) or NuGet server.
Learn more about it visiting the packaging documentation.
Going further
Now you can move to commands documentation to see how to deploy (apply your application).