Skip to content

RFC: multipass config/get/set structure and user experience #756

@Saviq

Description

@Saviq

#307 has some details on specifics of the "custom repository" feature, but I wanted to collect the user experience and the imaginable extent of the configuration structure here.

The CLI

Three new commands would be introduced, all operating on YAML-formatted data, with . (periods) separating depth levels. All operations need to be atomic and return validation errors.

multipass config [--no-defaults] [--expand] [<subtree>]
# opens an editor in interactive mode
# or prints the YAML of the subtree to standard out in non-interactive mode
# if `--no-defaults` given, only explicitly configured values will be shown
# some parts of the tree may not be shown by default (think remote and instance configuration), passing `--expand` would mean that the whole tree is shown
# on errors returns to the editor with annotation about validation or configuration issues

multipass get <key>
# prints the value of the requested configuration key

multipass set [<key>=<value> …]
# sets the values of the given configuration keys
# or accepts a YAML of a subset of the configuration tree on standard input

The structure

To avoid unnecessary nesting, I propose top-level keys to only be: client and remote names, with remote configuration nested under the remote name.

client:
  default-remote: local       # name of one of the remotes configured below
  primary-name: primary       # name of the primary instance

  launch-defaults:
    image: default            # see `multipass find` for available images or use file:// or http:// URLs
    cpus: 1                   # recommended at most one below your host's cores
    disk: 5GB                 # this is the maximum the instance can use, so go big
    memory: 2GB               # maximum the instance can use, shared between host and instances
    cloud-init: {}            # see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
    mounts:
      /local/path: remote/path
      /other/path:
        target: other/remote/path
        uid_maps:
          *: default          # "*" for "all", "default" for the default user's UID inside the instance,…
        gid_maps:
          *: default          # …or the numeric ID

  images:
    example:                  # this is the image name to use for `multipass launch`
      aliases: [ex]           # an optional list of alternative names
      image: default          # defaults to this image's key above
      cpus: 2                 # overrides the default launch options from above
 
    lts:                      # because the `lts` alias exists on the remote…
      disk: 25GB              # …this only overrides the disk size when doing `multipass launch lts`

local:                        # configured on first start
  address: unix:/run/multipass_socket  # platform default
  
  # below is remote configuration
  listen-address: unix:/run/multipass_socket
  driver: qemu                # one of qemu, hyperkit, hyper-v, libvirt - remote platform default
  network: 10.0.6.0/24        # needs extending for bridging and IPv6
  proxy:
    http: proxy://address     # will be used in the instances unless overridden with `--cloud-init`
    https: proxy://address

  default-stream: release     # when using this remote, this will be the default stream used to find images
  streams:                    # base URLs to image remotes
    release: https://cloud-images.ubuntu.com/releases          # may need to be expanded when v3 streams exist
    daily: https://cloud-images.ubuntu.com/daily
    minimal: https://cloud-images.ubuntu.com/minimal/releases

  images:                     # this has the same format as `client.images` above, with lower precedence
    core:
      aliases: [core16]
      image: http://cdimage.ubuntu.com/ubuntu-core/16/stable/current/ubuntu-core-16-amd64.img.xz
    core18:
      aliases: [core18]
      image: http://cdimage.ubuntu.com/ubuntu-core/18/stable/current/ubuntu-core-18-amd64.img.xz
    snapcraft:core16:
      description: "Snapcraft builder for Core 16"
      aliases: [snapcraft:core]
      image: http://cloud-images.ubuntu.com/minimal/releases/xenial/release/ubuntu-16.04-minimal-cloudimg-amd64-disk1.img
      kernel: http://cloud-images.ubuntu.com/releases/xenial/release/unpacked/ubuntu-16.04-server-cloudimg-amd64-vmlinuz-generic
      initrd: http://cloud-images.ubuntu.com/releases/xenial/release/unpacked/ubuntu-16.04-server-cloudimg-amd64-initrd-generic
    snapcraft:core18:
      description: "Snapcraft builder for Core 18"
      image: http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64-disk1.img
      kernel: http://cloud-images.ubuntu.com/releases/bionic/release/unpacked/ubuntu-18.04-server-cloudimg-amd64-vmlinuz-generic
      initrd: http://cloud-images.ubuntu.com/releases/bionic/release/unpacked/ubuntu-18.04-server-cloudimg-amd64-initrd-generic

  # below are configured instance definitions
  instance-name:             # this is a configured instance on the `local` remote
    name: ~                  # this allows renaming the instance
    cpus: 4                  # this changes the number of CPUs configured for the instance
    memory: 2GB              # this changes the amount of memory available to the instance
    gpus: 1                  # give the instance a single random available GPU

other-remote:
  address: 1.2.3.4:1234      # connecting to a remote might require providing passphrase
  listen-address: 0.0.0.0:1234
  driver: hyperkit           # platform default, changing may not be possible

  instance-name:             # this is a configured instance on the `local` remote
    cpus: 4                  # this changes the number of CPUs configured for the instance
    memory: 2GB              # this changes the amount of memory available to the instance
    gpus:                    # give the instance the listed GPUs
    - 0:1:1                  # GPU identifier, format platform-dependent
    - 0:1:2

That's the extent of configuration that I can come up with right now, please have a look through for errors, missing bits or plain stupidity. ;)

Doubts:

  • mounts would be stricter if it was target_path: source_path to ensure key (and hence, mount target) uniqueness, and allow the same source mounted in multiple places; but could be confusing
  • we could treat streams the same way LXD does, and make them remotes (of different types)
  • putting instances top-level under remotes means name clashes with configuration keys; putting them under remote.instances.instance means more typing, unless we maybe define remote:instance as a shortcut for it; along with default-remote this could arguably be shortened to multipass config name, which gets expanded to $default-remote:instance if name not found, and then to $default-remote.instances.instance
  • again taking LXD for inspiration, they have streams support both in the client and in the daemon (see their API for launching containers), question is how that idea affects the above configuration
  • what does removing a configuration entry mean?
    • no-op or reset to default
    • YAML's null (~) could be used to reset instead

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions