Pattern: using resources to support offline deployments

Many charms use resources to enable deployments to proceed without access to the Internet. For example, the ~containers/kubernetes-worker charm uses snap to install kubectl, kubelet and other necessary software.

This pattern is not limited to snaps, however. Resources allow source code to be updated and managed independently from charm upgrades.

Reactive charms

Using charms.reactive, this process is relatively straightforward. The charm author defines layer.yaml, that includes a description of the snap(s) to be downloaded by the charm:

# layer.yaml
includes:
  - layer:basic
  - layer:snap
options:
  snap:
    kubectl:
      channel: stable

Within the charm’s metadata.yaml, parallel resources are defined:

# metadata.yaml
resources:
  kubectl:
    type: file
    filename: kubectl.snap
    description: kubectl snap

Operator Framework

Within the Operator Framework, the ideal process is still being worked through. While the design decisions are still being made, we can mimic the behaviour of the snap layer within our own charm:

# src/charm.py
# ...
res_path = resource_get(snapname)
if res_path is False:
  install_from_the_charmstore(snapname)
else:
  install_from_the_resource(res_path)

To get this to work, a few helper functions are required:

from pathlib import Path
from subprocess import run

def resource_get(resource_name:str) -> Path:
    result = run(['resource-get', resource_name], output=True, text=True)
    return Path(result.stdout.strip())

def install_from_the_resource(path:Path):
    return run(['snap', 'install', '--dangerous', path])
    #                               --dangerous is required because the snap's 
    #                               asserts are not available


def install_from_the_charmstore(snap, channel='stable'):
    return run(['snap', 'install', f'--channel={channel}', snap])
3 Likes