Charmcraft bases & provider support

Abstract

Charmcraft is introducing the concept of bases into charmcraft.yaml, where a base is defined as the combination of: operating system name, channel (version), and architectures.

This bases configuration encapsulates both build-time and run-time environment configuration, allowing the charm developer to specify the environment required to build a charm, and the environment(s) the charm are compatible with.

This specification describes Charmcraft’s behaviors with the introduction of the bases configuration in charmcraft.yaml. Charmcraft may now output multiple charms per charmcraft pack command, using either LXD container or Multipass VM instances to pack them in their respective environments (as defined by build-on), regardless of the user’s operating system & version.

charmcraft clean will also be introduced to clean project build artifacts as a result of the integration with the providers.

Specifications

Bases syntax

In the charmcraft.yaml, support is being added for a new top-level bases: configuration. bases is a list of base configurations specifying the environments charms must be built and run on. Each item in the list must be a base object or have the following properties:

  • build-on: A list of compatible base environments this charm should be built in.
  • run-on: A list of compatible base environments this charm may run on.

A base object has the following properties:

  • name: name of base (e.g. “ubuntu”)
  • channel: name of channel (e.g. “20.04”)
  • architectures: [list of architecture strings], defaults to [machine-architecture] (e.g. [“amd64”,“riscv64”])

There are two forms allowed in bases, the short-form and long-form. The short-form is simply a base object, implying build-on: <base> and run-on: <base>. For example, the following two stanzas are equivalent:

  1. Short-form:
bases:
  - name: ubuntu
    channel: "20.04"
  1. Expanded, long-form:
bases:
  - build-on:
      - name: ubuntu
        channel: "20.04"
    run-on:
      - name: ubuntu
        channel: "20.04"

charmcraft pack will pack one charm for each bases entry, assuming the build-environment is valid for the given platform (for example, architecture of the machine needs to match the architecture in a build-on entry, if specified).

To build two charms, one for Ubuntu 18.04 and another for Ubuntu 20.04:

bases:
  - name: ubuntu
    channel: "18.04"
  - name: ubuntu
    channel: "20.04"

To build amd64-only charms (one for each Ubuntu version):

bases:
  - name: ubuntu
    channel: "18.04"
    architectures: [amd64]
  - name: ubuntu
    channel: "20.04"
    architectures: [amd64]

To build a multi-arch charm on amd64:

bases:
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64, arm64, riscv64]

Output Charm Filenames

Once building multiple charms, Charmcraft’s existing naming scheme will cause multiple output names to collide. To ensure uniqueness and allow the user to quickly identify the targets for each Charm, the output charm file names will be updated to the following format:

<charm-name>_<run-on-base-1>[<run-on-base-2>...].charm

Where “run-on-base” is of the format:

<run-on-base-name>-<run-on-base-channel>-<run-on-base-arch1>[-<arch2>...]

This format may result in verbose paths for complex cases with a large combination of run-on targets, but is simple enough for the majority of expected cases. Future work may further tweak the output name format, as deemed necessary.

See below for more concrete examples.

Build Providers

To support bases, Charmcraft shall be adding three build provider options:

  1. LXD
  2. Multipass
  3. Host (“destructive mode”)

The default build provider will depend on the host operating system:

  • Linux: LXD
  • MacOS: Multipass
  • Windows: Multipass

Note: Initial work will focus on Linux support with LXD with longer-term goals to expand platform support.

Required Deprecation Notices

Missing charmcraft.yaml

Without charmcraft.yaml present, Charmcraft shall issue a deprecation message warning the user:

charmcraft.yaml will be required in a future release. See <todo-url> for more information.

Missing bases configuration

Without bases present in a user’s charmcraft configuration, Charmcraft shall:

  1. Issue a deprecation message warning the user:

Bases configuration will be required in a future release. See <todo-url> for more information.

  1. Set the default values for bases to have a build-on/run-on Ubuntu 20.04. This is to match the behavior currently seen when using the current (stable, strict) snap after transitioning to the new classic snap.

Snap Requirement

If not running as the snap on Linux, Charmcraft shall error with the message: For a supported user experience, please use the Charmcraft snap. For more information, check out https://snapcraft.io/charmcraft

Note: For development use, CHARMCRAFT_DEVELOPER=1 in the environment will bypass the error.

Build Provider Environment Matching

When using LXD or Multipass build providers, Charmcraft must determine the set of VMs/containers (hereafter, “managed-environment(s)”) it will instantiate to build the charms.

There will be one output Charm for each bases configuration entry. However, not all systems can build all bases, typically due to architecture requirements.

For each bases.build-on configuration, Charmcraft will iterate through the entries until it finds a suitable environment that can be instantiated using LXD/Multipass. Initial support shall require:

  1. name is “ubuntu”

  2. channel is one of: “18.04”, “20.04”

  3. architectures is empty or includes a match for the host machine’s architecture, as defined by:

    charmcraft.utils.get_host_architecture()

For each bases configuration, if there is no suitable build-on entry, Charmcraft shall warn with:

No suitable build-on environments found in bases[<index>] configuration.

If Charmcraft cannot create a suitable environment for any of the configured bases, Charmcraft shall error after the above warnings with:

No suitable 'build-on' environments found in any 'bases' configuration.

Destructive-Mode Environment Matching

When using the host in destructive-mode, Charmcraft will build charm(s) for bases configurations in which the host is compatible with bases.build-on:

  • Base’s name matches ID in /etc/os-release
  • Base’s channel matches VERSION_ID in /etc/os-release
  • Base’s architectures contains a match for charmcraft.utils.get_host_architecture()

Snap Environment Configuration

Charmcraft will add support for snap-wide configuration settings. This will be done using a snap configure script and snapctl. For more information, see https://snapcraft.io/docs/adding-snap-configuration.

The initial configuration support will include:

  • provider = <provider string>
    • valid values:
      • lxd
      • multipass
    • default=lxd
  • provider.lxd.project = <project string>
    • default=charmcraft
  • provider.lxd.remote = <project string>
    • default=local
  • provider.lxd.use-snapshots = <bool string>
    • valid values:
      • false
      • true
    • default=true

Example user configuration:

sudo snap set charmcraft provider=lxd
sudo snap set charmcraft provider.lxd.project=default
sudo snap set charmcraft provider.lxd.use-snapshots=false

Additional Command Line Options

Some additional command-line parameters will be added to the pack command:

  • –destructive-mode to run in destructive mode (build on host).
  • –debug shall shell into the environment if the build fails.
  • –shell shall shell into the environment in lieu of the step to run.
  • –shell-after shall shell into the environment after the step has run.
  • –bases-index <index> shall limit build outputs according to the specified index in bases configuration. Without this parameter, Charmcraft will attempt to build for all given bases configurations. May be repeated for multiple indices. Starts at zero.

Charmcraft Bases Example #1: single entry, single arch

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "18.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "18.04"
        architectures: [amd64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environment:

    • Ubuntu 18.04 amd64
  • Yielding the outputs:

    • mycharm_ubuntu-18.04-amd64.charm

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 18.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-18.04-amd64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #2: multiple entry, single arch

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "18.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "18.04"
       architectures: [amd64]
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [riscv64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [riscv64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environments:

    • Ubuntu 18.04 amd64
    • Ubuntu 20.04 amd64
  • Yielding the outputs:

    • mycharm_ubuntu-18.04-amd64.charm
    • mycharm_ubuntu-20.04-amd64.charm

When building on riscv64 with LXD/Multipass:

  • Charmcraft will create the following managed-environments:

    • Ubuntu 20.04 riscv64
  • Yielding the outputs:

    • mycharm_ubuntu-20.04-riscv64.charm

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 18.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-18.04-amd64.charm

When building on Ubuntu 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-amd64.charm

When building on Ubuntu 20.04 riscv64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-riscv64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #3: single entry, multiple arch

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64, riscv64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environment:

    • Ubuntu 20.04 amd64
  • Yielding the outputs:

    • mycharm_ubuntu-20.04-amd64-riscv64.charm

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-amd64-riscv64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #4: multiple entry, with cross-arch

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [riscv64]
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environments:

    • Ubuntu 20.04 amd64 #1
    • Ubuntu 20.04 amd64 #2
  • Yielding the outputs:

    • mycharm_ubuntu-20.04-riscv64.charm
    • mycharm_ubuntu-20.04-amd64.charm

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-riscv64.charm
  • mycharm_ubuntu-20.04-amd64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #5: multiple run-on

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
       channel: "18.04"
        architectures: [amd64]
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64, riscv64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environments:

    • Ubuntu 20.04 amd64
  • Yielding the outputs:

    • mycharm_ubuntu-18.04-amd64_ubuntu-20.04-amd64-riscv64.charm

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-18.04-amd64_ubuntu-20.04-amd64-riscv64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #6: multiple build-on

Consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "18.04"
        architectures: [amd64]
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]

LXD/Multipass Expected Outputs

When building on amd64 with LXD/Multipass:

  • Charmcraft will create the following managed-environments:

    • Ubuntu 18.04 amd64
  • Yielding the outputs:

    • mycharm_ubuntu-20.04-amd64.charm

Note: Ubuntu 18.04 was chosen as it is the first entry in the list and a viable build environment. If the first entry was not viable, the list would continue to be enumerated until one was, erroring if none was found.

When building on any other architecture, Charmcraft shall error.

Destructive-Mode Expected Outputs

When building on Ubuntu 18.04 or 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-amd64.charm

When building on any other host, Charmcraft shall error.

Charmcraft Bases Example #7: redundant outputs

When multiple environments are instantiated, each with their own set of output charms, it is possible two environments could output the same charm.

For example, consider the charmcraft.yaml example:

bases:
  - build-on:
      - name: ubuntu
        channel: "18.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
  - build-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]
    run-on:
      - name: ubuntu
        channel: "20.04"
        architectures: [amd64]

Charmcraft shall detect when this will occur (upon loading the charmcraft.yaml configuration) and error accordingly:

Multiple bases have identical run-on configurations. If this is intentional, please consolidate bases[0] and bases[1].

Destructive-Mode Expected Outputs

When building on Ubuntu 18.04 or 20.04 amd64 with destructive-mode will result in the following outputs:

  • mycharm_ubuntu-20.04-amd64.charm

When building on any other host, Charmcraft shall error.

LXD-Specific Provider Options & Behaviors

When using the LXD provider, either as the default or specified provider, there will be some LXD-specific behaviors.

Initialization

LXD containers will be created with the format for name:

charmcraft-<charm-name>-<bases-index>-<build-on-index>-<arch>

Installation

If LXD is not found in environment, the environment is not a container, and tty is interactive, the user will be prompted with:

Do you wish to install LXD and configure with it the defaults? [yn]

If not, error with:

LXD not installed. Please visit https://linuxcontainers.org/lxd/getting-started-cli/#installation for directions on installing LXD for your platform

If failure occurs during installation, error with:

LXD failed to install (reason, if available). Please visit https://linuxcontainers.org/lxd/getting-started-cli/#installation for directions on installing Multipass for your platform.

Project

Charmcraft will create a LXD project called “charmcraft” for instantiating managed-environments.

This will be managed with the snap configurable setting provider.lxd.project as detailed in the Snap Configuration section above

If provider.lxd.project != “charmcraft” and it does not exist, error with:

LXD project 'foo' not found.

If provider.lxd.project == “charmcraft” and it does not exist, create & configure it.

Snapshots

Charmcraft will create image snapshots if specified by the user.

This will be managed with the snap configuration setting provider.lxd.use-snapshots as detailed in the Snap Configuration section above.

Multipass-Specific Provider Options & Behaviors

Installation

If Multipass is not found in environment, the environment is not a container, and tty is interactive, the user will be prompted with:

Do you wish to install Multipass and configure it with the defaults? [yn]

If the user specifies yes, Multipass will be installed with craft-providers.

If not, error with:

Multipass not installed. Please visit https://multipass.run/ for directions on installing Multipass for your platform.

If failure occurs during installation, error with:

Multipass failed to install (<reason, if available>). Please visit https://multipass.run/ for directions on installing Multipass for your platform.

Managed Environment Configuration

Initialization

Building on top of the craft-provider’s buildd image configuration, charmcraft will additionally ensure that:

  1. python3-pip is installed (until craft-parts ensures it is installed when required)
  2. Charmcraft snap is installed. The host’s charmcraft snap will be injected to ensure a matching version is used for both.
    • If charmcraft is already installed in the managed-environment, the host’s running version will be compared to the managed-environment’s by executing charmcraft version. If versions do not match, the host’s snap will be re-injected into the target.

Multipass VMs will be created with the format:

charmcraft-<charm-name>-<bases-index>-<build-on-index>-<arch>

Environment Pass-through

By default, these environment variables will be passed through to the managed-environment(s):

  • http_proxy
  • https_proxy
  • no_proxy

These environment variables will be set in /etc/environment (via craft-providers) to ensure applicability to all processes.

Debug Environment

The –shell[-after] options will launch a Bash shell in the managed-environment(s) with the /root working directory.

The charmcraft snap in the managed-environment will be configured to run in managed mode. As such, the project directory will always be assumed to be /root/project rather than the current working directory.

“charmcraft clean” Command

Charmcraft needs to be able to clean up:

  • Caches/Downloads
  • Multipass VMs
  • LXD containers
  • LXD snapshot image(s)
  • “charmcraft” LXD project

To support this, a clean command shall be introduced for Charmcraft. When run in the context of a Charm project directory, this will clean all project-related Charmcraft-created LXD/Multipass VMs, determined via regex: charmcraft-<charm-name>-.*-.*

charmcraft clean will also support a parameter –purge-local-lxd-project, which will purge the entire local “charmcraft” LXD project, including all containers and snapshots.

When run outside of a Charm project directory, charmcraft clean will error with Charm project not found if not using the –purge-lxd-project option.

Additional clean functionality will be expanded in a future spec.

Expected Tasks

  • [x] Error if not running as snap with bases on Linux.
    • [x] Support CHARMCRAFT_DEVELOPER=1 to bypass error.
  • [x] Enable managed mode via environment CHARMCRAFT_MANAGED_MODE=1
    • To be replaced with snap set managed=true in future milestones.
  • [x] Update output charm name format to account for run-on scenarios.
  • [x] Pack charms for each bases entry with the first matching build-on.
    • [x] Log each non-matching build-on.
    • [x] Warn for each entry without a matching build-on.
    • [x] Error if no entries match.
  • [x] Update manifest.yaml metadata with run-on metadata.
  • [in progress] Add –bases-index parameter to specify a specific bases config index (repeated arg).
  • [x] Enable debug logging from craft-providers library.
  • [x] Create “charmcraft” LXD project, if not present.
  • [x] Configure buildd image remote, if not present.
  • [x] Charmcraft buildd base configuration.
  • [x] Enumerate buildable environments (error if none) with applicable warnings/errors and spin up required containers, if not present.
    • Assume provider.lxd.use-snapshots=true
    • Assume provider.lxd.project=charmcraft
    • Assume provider.lxd.remote=local
    • Configure environment passthroughs for managed-environment.
  • [x] Inject Charmcraft snap to managed-environments (if uninstalled or version mismatch).
  • [x] Execute pack commands in each managed environment.
  • [x] Add charmcraft clean to clean project containers.
  • [x] Add deprecation notices:
    • [x] missing charmcraft.yaml
    • [x] missing bases
  • [x] Default bases value (build-on/run-on = focal) if not present.
  • [x] Add –destructive-mode option for pack to build charms in destructive mode.
  • [ ] Add snapctl python module to read/write runtime configuration using snapctl to craft-providers.
  • [ ] Add snap configure script to read/write snap configuration file
  • [ ] Read/write support for provider=lxd|multipass configuration.
  • [ ] Multipass support for managed environments.
  • [ ] Read/write support for provider.lxd configuration.
  • [ ] Debug shell on failure with –debug.
  • [ ] Debug shell instead of pack command with –shell.
  • [ ] Debug shell after pack with –shell-after.
  • [ ] Install Charmcraft snap from Snap Store if host is not using snap.
  • [x] Add LXD installation support.
  • [ ] Add Multipass installation support.
4 Likes

there’s a space missing between “in” and “bases” here and in other parts.

What is going to happen to the series entry in metadata.yaml? I feel we are currently duplicating the information about the operating system a charm runs on. Is there a plan to deprecate it in metadata.yaml?

1 Like

@heitor series is being deprecated in favor of bases support:

3 Likes

Is there a way to exclude architectures? e.g. architectures: [!i386, !armhf]?