This charm and the accompanying appcraft
utility allow you to wrap with Juju an existing OCI image built using Cloud Native Buildpacks and to use relation data to render configurations for the wrapped application.
The user experience is centered over appcraft
, which is a wrapper around charmcraft
that customizes the generic Cloud Native Buildpack charm provided in this repository:
$ appcraft <manifest_path>
The manifest_path
is the file path for a manifest file, or -
if the manifest is piped into appcraft
over standard input.
To get started, clone this repository, or download the latest and greatest appcraft
from the Releases page.
The manifest allows you to parametrize the charm to be generated. If contains the following configurations:
name
: name of the resulting charm; this value is passed verbatim to thename
property of the charm'smetadata.yaml
summary
: a short summary of the resulting charm; this value is passed verbatim to thesummary
property of the charm'smetadata.yaml
description
: description of the resulting charm; this value is passed verbatim to thedescription
property of the charm'smetadata.yaml
consumes
: details which relations are consumed by this charm; this value is passed verbatim to theconsumes
property of the charm'smetadata.yaml
, but is also used to calculate the template evaluation environment used to evaluate theenvironment
andfiles
configurations. At runtime, the charm will stay in statusBlocked
as long as one or more of the declared consumed relations are unfulfilled.environment
: this configuration allows to specify environment variables that are set on the application at runtime. Each environment variable is specified as an object with two properties:name
andtemplate
. The value ofname
is going to be used verbatim as the name of the environment variable; the value of the environment variable is specified via thetemplate
property, which contains a Jinja 3 template that is evaluated at charm's runtime and that can access globals based on which relations are declared in the manifest, and which data bags are exposed by those relations at runtime. The Cloud Native Buildpack charm provides two actions,dump-template-globals
andevaluate-template
, that are useful to understand which data is available to the templates.files
: this configuration allows to specify files that are created in the application container at runtime, before the application is started. Each file is specified as an object with two properties:path
andtemplate
. The value ofpath
is going to be used as the absolute path of the file inside the container; the content of the file is specified via thetemplate
property, which contains a Jinja 3 template that is evaluated at charm's runtime and that can access globals based on which relations are declared in the manifest, and which data bags are exposed by those relations at runtime.
The Cloud Native Buildpack charm provides two actions, dump-template-globals
and evaluate-template
, that are useful to understand which data is available to the templates.
Each relation added to a deployment of this charm will expose data to render environment variables and files in your application container.
The charm can optionally declare one or more relations it needs to have at runtime before the application is started.
For example, the following snippet of a manifest.yaml
declares that the charm needs a relation, called datasource
, using the mongodb-datasource
interface:
name: cnb
requires:
datasource: # Relation name, to be used in your templates to identify the relation
interface: mongodb-datasource # Relation interfaces are codified in the charms that provide them
The mongodb-datasource
interface is exposed by the mongodb-k8s charm.
Therefore, in order for the cnb
charm to be able to bootstrap the application inside its OCI image, a mongodb-k8s
charm must be deployed inside the same Juju controller as the cnb
charm, and a relation must be established between the cnb
and mongodb-k8s
charms.
The cnb
charm will remain in status Blocked
as long as the datasource
relation is unfulfilled.
The same applies to cases when multiple required relations are declared: the charm stays in Blocked
status as long as any of the required relations is unfulfilled.
The environment
and files
properties of the manifest
allow to specify any number of environment and files to be made available to the application inside the OCI image, respectively.
The values of those environment variables, and the content of the files, is specified using templates.
The templates are Jinja 3 templates, and they are evaluated at runtime by the charm using an evaluation environment containing the data bags provided at runtime by the required relations.
Consider the following example:
name: my_charm
relations:
datasource: # No hyphen, no cry
interface: mongodb
environment:
- name: SPRING_DATA_MONGODB_URI
value: '{{relations.consumes.datasource.app.replica_set_uri}}'
The manifest above declared one relation, called datasource
, and uses the property replica_set_uri
of the relation's application bag, accessed via the .app
accessor.
Note: The prefix relations.consumes.
must be appended to the templates, because in the future the charm may additionally expose other types of relations, e.g., provides ones, as well as data that is not related to relations alone.
Avoid using hyphens in the relation name, as that makes it rather awkward consuming the data in templates.
For example, if we take the example above and rename the datasource
relation to data-source
, the template gets decidedly more awkward:
name: my_charm
relations:
data-source: # Mind the hyphen!
interface: mongodb
environment:
- name: SPRING_DATA_MONGODB_URI
value: '{{relations.consumes["data-source"].app.replica_set_uri}}'
The same applies to relation properties.
If the mongodb
relation exposed in the application data bag a replica-set-uri
property, rather than replica_set_uri
, the template would become:
{{relations.consumes.datasource.app["replica-set-uri]}}
The YAML parser of appcraft
will take offense if you write a manifest like the following:
name: my_charm
relations:
datasource:
interface: mongodb
environment:
- name: SPRING_DATA_MONGODB_URI
value: {{relations.consumes.datasource.app.replica_set_uri}}
Note the lack of quotes of other ways of denoting literals as strings in YAML.
In this case, the parser thinks that the first {
of the value opens an object, and immediately finding another {
makes the YAML document invalid.
Instead, you can either quote the template, or use YAML multiline strings:
name: my_charm
relations:
datasource: # No hyphen, no cry!
interface: mongodb
environment:
- name: SPRING_DATA_MONGODB_URI
value: |
{{relations.consumes.datasource.app.replica_set_uri}}
Multiline strings are also very handy for templating non-trivial files, or using advanced Jinja capabilities like iterations and conditionals.
The following globals will be accessible in all the templates for environment variables and files:
{
"relations": {
"consumes": {
<relation_name>: {
"app": {
<other_application_properties>
},
"units": [
{
<other_unit_properties>
},
...
]
}
}, ...
}
}
For example, if the application has a mongodb
relation, which exposes only one unit, this is how the globals may look like:
{
"relations": {
"consumes": {
"mongodb": {
"app": {
"password": "3rtmQaj0VjCeDPP5",
"provider_data": {
"provides": {
"mongodb": "4.4.1"
},
"ready": true
},
"replica_set_name": "rs0",
"replica_set_uri": "mongodb://user-1:[email protected]:27017/admin",
"replicated": true,
"username": "user-1"
},
"units": [{
# This unit exposes no data over the relation
}]
}
}
}
}
With such an evaluation environment, a charm built using the following manifest:
name: my_charm
relations:
datasource: # No hyphen, no cry!
interface: mongodb
environment:
- name: SPRING_DATA_MONGODB_URI
value: |
{{relations.consumes.datasource.app.replica_set_uri}}
will have the SPRING_DATA_MONGODB_URI=mongodb://user-1:[email protected]:27017/admin
set on the application.
To access the current stastus of the template globals as seen by a particular unit, or try to evaluate a template without actually modifying the configuration of the charm, you can use the dump-template-globals
and evaluate-template
actions, respectively.