canonical / canonical/multipass
RFC: `multipass config/get/set` structure and user experience
- Dominant language
- C++
- Stars
- 9.2k
- Forks
- 828
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 43
Description
#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] []
# 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
# prints the value of the requested configuration key
multipass set [= …]
# 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.
```yaml
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](https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-optional-targetmember) 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
Contributor guide
Research direction
Start by reviewing the proposed `multipass config`, `get`, and `set` command behavior and the YAML structure in this issue, then compare the custom repository details in #307. Done would require an agreed configuration model, command semantics, atomicity and validation behavior; this RFC does not identify implementation files or tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- yaml
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100