Resources

A resource is an additional or some additional content that a charm can make use of, or may require to run. This can be especially useful where you need to include large blobs (perhaps a database, media file or otherwise) that may not need to be updated with the same cadence as the charm or workload itself. By keeping resources separate, you can control the lifecycle of these elements more carefully, and in some situations avoid the need for repeatedly downloading large files from Charmhub during routine upgrades/maintenance.

Defining resources

Resources are defined in a charm’s metadata.yaml and are therefore intrinsically linked to a charm. Because of this, there is no need to register them separately in Charmhub. Other charms may have resources with the same name, but this is not a problem; references to resources always contain the charm name and resource name.

Similarly with storage, resources is defined as a top-level YAML map, where each key in the map is the name of a resource, that can map to the following fields:

Field Type Default Description
type string file Type of the resource. Supported values are file or oci-image.
description string nil Description of the resource
filename string nil Name of the file resource

An example resource definition:

# ...
resources:
  someresource:
    type: file
    filename: superdb.bin
    description: the DB with needed info
  app-image:
    type: oci-image
    description: OCI image for app
# ...

Kubernetes charms must declare an oci-image resource for each container they define in the containers map.

Accessing resources from the charm

In order to make use of file resources in a charm, the Charmed Operator Framework provides a helper method to fetch the path of a file resource. Consider this simple resource definition:

resources:
  my-resource:
    type: file
    filename: somefile.txt
    description: test resource

In the charm code, we can now use Model.resources.fetch(<resource_name>) to get the path to the resource, then manipulate it as we need:

# ...
from ops.model import ModelError, BlockedStatus
# ...

def _on_config_changed(self, event):
    # Get the path to the file resource named 'my-resource'
    try:
        resource_path = self.model.resources.fetch("my-resource")
    except ModelError:
        self.unit.status = BlockedStatus("Missing 'my-resource' resource")
        return

    # Open the file and read it
    with open(resource_path, "r") as f:
        content = f.read()
    # do something

The fetch() method will raise a ModelError if the resource does not exist, and returns a Python Path object to the resource if it does.

During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. In the below snippet, we create a simple file with some text content, and pass it to the Juju controller to use in place of any published my-resource resource:

$ echo "TEST" > /tmp/somefile.txt
$ charmcraft pack
$ juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt

Uploading resources

A charm’s resources can be inspected using the charmcraft tool. The following examples will work on the assumption of the following resource definition:

# ...
resources:
  someresource:
    type: file
    filename: superdb.bin
    description: the DB with needed info
# ...

Once the resource has been defined, you can build the charm as normal, then upload it to Charmhub:

# Build the charm
$ charmcraft pack
Created 'my-super-charm.charm'.

# Upload the charm to Charmhub
$ charmcraft upload my-super-charm.charm
Revision 13 of 'my-super-charm' created

# Query Charmhub about the charm's resources
$ charmcraft resources my-super-charm
Name          Type    Revision    Optional
someresource  file    13          True

Now we’ve confirmed that the resource is associated with the charm, we can upload the resource to Charmhub.

Uploading file resources

# Upload the resource
$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin
Revision 1 created of resource 'someresource' for charm 'my-super-charm'

# Update the resource to a new version
$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin
Revision 2 created of resource 'someresource' for charm 'my-super-charm'

# Verify the revisions of the charm resource
$ charmcraft resource-revisions my-super-charm someresource
Revision    Created at    Size
2           2021-03-05  192.3K
1           2021-03-05  204.9K

Uploading oci-image resources

Uploading oci-image resources is similar to uploading file resources, but with a few different parameters to the command.

For example, if an oci-image is defined in metadata.yaml like so:

# ...

resources:
  redis-image:
    type: oci-image
    description: OCI image for Redis
# ...

It can be uploaded like so:

# charmcraft upload-resource --image <image reference> <charm name> <resource name>
#
# The image reference is how it is referred to on Docker Hub. For example:
# lts/nginx:1.18-20.04_beta

$ charmcraft upload-resource --image redis my-super-charm redis-image
Revision 1 created of resource 'redis-image' for charm 'my-super-charm'

Attach a resource to a release

Any uploaded versions of your charm’s resources can be associated with the charm at release time. This way, you can decide which version of the resource will work with each version of your charm.

$ charmcraft release my-super-charm --revision=13 --channel=stable --resource=someresource:2
Revision 13 of charm 'my-super-charm' released to stable (attaching resources: 'someresource' r2)

We can inspect the charm, and any associated resources at any time with charmcraft status:

$ charmcraft status my-super-charm
$ charmcraft status my-super-charm
Track       Channel    Version    Revision    Resources
latest      stable     7.4a       13          someresource (r1)
            candidate  ↑          ↑           ↑
            beta       7.4a       10
            edge       7.4a       14          someresource (r3)