Chisel v1.0.0 has been released recently! It comes with a bunch of cool features. This post describes the new features along with a basic explanation. (Jump to the new stuff.)
Basics
What is Chisel again?
Chisel can derive a minimal Ubuntu-like Linux distribution using a release database that defines “slices” of existing packages. Slices enable developers to cherry-pick just the files they need from the Ubuntu archives, and combine them to create a new filesystem which can be packaged into an OCI-compliant container image or similar.
In summary, with Chisel, you get to choose which files you want to have in your rootfs and extract only those from the packages.
That sounds cool! How do I use it?
Using Chisel is super easy. First, install it the way you want –
-
Compile with
go
:go install github.com/canonical/chisel/cmd/chisel@latest
-
Install the snap:
snap install chisel --candidate
-
Download the pre-built binaries from the release
page:# Download the Chisel tarball. wget https://github.com/canonical/chisel/releases/download/v1.0.0/chisel_v1.0.0_linux_amd64.tar.gz # Download the hash and validate. wget https://github.com/canonical/chisel/releases/download/v1.0.0/chisel_v1.0.0_linux_amd64.tar.gz.sha384 sha384sum -c chisel_v1.0.0_linux_amd64.tar.gz.sha384 # Install the Chisel binary. tar -C /usr/bin -xf chisel_v1.0.0_linux_amd64.tar.gz
Check your installation with the following:
$ chisel version
v1.0.0
Finally, you can install your slice(s) like the following:
mkdir rootfs
chisel cut --release ubuntu-24.04 --arch amd64 --root rootfs/ hello_bins
This should install the hello binaries in your rootfs. You can check if it works with the following:
$ chroot rootfs/ hello
Hello, world!
What is a slice?
A Debian/Ubuntu package is a collection of different types of files – binaries, libraries, configuration files, copyright, docs etc. The philosophy of Chisel is that you may not need all the files from each package.
Chisel introduces a slice as a (partial) group of package files. Thus, a package can have many slices and they can overlap as well! In fact, you can have an unlimited amount of slices per package, Chisel won’t complain.
Well, where are these slices then?
Chisel uses slice definition files to describe the slices for a package. The collection of these files with a configuration file named chisel.yaml
is called a chisel release. The chisel-releases repository contains a number of releases for various Ubuntu versions.
To use any of these releases, pass the branch name as the argument to the --release
option in the cut
command. For example, to use the Focal (20.04) release, you would do something like this:
chisel cut --release ubuntu-20.04 --root rootfs/ hello_bins
The --root
option is required and is used to specify a directory where chisel should install the files.
How do I know which slices are there?
Check out the chisel-releases repository! The structure of the files are as follows:
chisel.yaml
slices/pkg-name.yaml
slices/...
The slices for a particular package foo
typically can be found at slices/foo.yaml
.
Or, use the new find
command!
Can I have my own slices?
Sure! The --release
option accepts a path to your custom release directory. You can edit, add, remove slices in your local release directory as you want. See the README.
We would love it if you kindly contribute your slices to the upstream chisel-releases repository! See CONTRIBUTING.
Commands
Chisel commands can be classified as follows:
-
Basic (general operations):
find
: Find existing slicesinfo
: Show information about package sliceshelp
: Show help about a commandversion
: Show version details
-
Action (make things happen):
cut
: Cut a tree with selected slices
The cut
command
The cut
command is the only action command which installs the selected slices along with their dependencies, for a particular chisel-release (specified by --release
, optional) and a particular package architecture (specified by --arch
, optional) in a particular directory (specified by --root
).
Usage:
chisel cut [cut-OPTIONS] [<slice names>...]
If the --release
and/or --arch
options are not provided, they are inferred from the host system Chisel is runing on.
The find
command
Chisel v0.10.0 introduced the find
command which can be used to reduce the hassle of going to each branch and checking if there exists slices for a package.
$ chisel find hello_* 2>logs
Slice Summary
hello_bins -
hello_copyright -
Read more about the find
command in this discourse post.
The info
command (NEW)
The info
command complements the find
command. Whereas the find
command searches for existing slices, the info
command takes in slice or package name(s) and displays the slice definition(s).
Here’s an example:
$ chisel info hello_bins 2>logs
package: hello
archive: ubuntu
slices:
bins:
essential:
- hello_copyright
- libc6_libs
contents:
/usr/bin/hello: {}
Unlike the find
command, the info
command does not accept wildcards. But it does take package names as query strings and it can process multiple queries at once. Different YAML documents are separated by three dashes ---
.
$ chisel info hello libgcc-s1_libs 2>logs
package: hello
archive: ubuntu
slices:
bins:
essential:
- hello_copyright
- libc6_libs
contents:
/usr/bin/hello: {}
copyright:
contents:
/usr/share/doc/hello/copyright: {}
---
package: libgcc-s1
archive: ubuntu
slices:
libs:
essential:
- libgcc-s1_copyright
- libc6_libs
contents:
/usr/lib/*-linux-*/libgcc_s.so.*: {}
It takes only one option, --release
which is similar to the find
and cut
commands. For more info, see chisel help info
.
Chisel manifest (NEW)
This is a much anticipated feature of Chisel which has been in the works througout different releases. Chisel manifest is a ZSTD-compressed file which lists the metadata about installed packages, slices and most importantly, files.
The generate
keyword
v1.0.0 also introduced a new generate
keyword for file paths in slice definitions. The generate
keyword currently accepts only one non-empty value: manifest
. And the generate
keyword can be specified on certain type of glob paths in the following format:
/path/to/dir/**: {generate: manifest}
The path must be a directory and must end with double-asterisks (**
) which matches everything inside that directory. Additionally the path must not contain any other wildcard characters (*
, ?
) e.g. the /path/to/dir
part must not have any wildcard. These are treated as typical globs while checking for conflict and these directories and everything inside those are generated by Chisel.
If a slice with a generate: manifest
-specified path is selected to be installed, Chisel writes the manifest to a manifest.wall
file inside the specified directory. In the above example, the manifest path would /path/to/dir/manifest.wall
. If there are multiple generate: manifest
-specified paths, the manifest is generated at each of those places.
The base-files_chisel
slice
By default, chisel-release provides a base-files_chisel
slice for convenience in each of its releases, which contains a generate: manifest
-specified path.
package: base-files
essential:
- base-files_copyright
slices:
# Dedicated slice for generating the Chisel manifest.
chisel:
essential:
- base-files_var
contents:
/var/lib/chisel/**: {generate: manifest}
...
Thus, if you install the base-files_chisel
along with other slices, a manifest file would be generated at /var/lib/chisel/manifest.wall
. This file is ZSTD-compressed.
$ chisel cut --root rootfs/ hello_bins base-files_chisel
...
...
2024/10/17 11:49:07 Generating manifest at /var/lib/chisel/manifest.wall...
Manifest format
The uncompressed file is in jsonwall-format – a collection of JSON objects, one-per-line. The lines except the header are sorted in lexicographical-order for better compression.
jsonwall Header
The header is a single JSON object on the first line in the following format:
{"jsonwall":"1.0","schema":"1.0","count":84}
This JSON object has the following attributes:
Field | Type | Required | Description |
---|---|---|---|
jsonwall |
str |
required | the version of jsonwall. |
schema |
str |
required | the schema version that Chisel manifest uses. |
count |
int |
required | the number of JSON entries (or, lines) in this file, including the header itself. |
Packages
For each package installed in the file system, a JSON object with "kind":"package"
will be present in the manifest.
{"kind":"package","name":"foo","version":"1.0-2","sha256":"abcd...","arch":"amd64"}
These JSON objects have the following attributes:
Field | Type | Required | Description |
---|---|---|---|
kind |
str |
required | type of JSON object – the value will always be package . |
name |
str |
required | name of the package. |
version |
str |
required | version of the package. |
sha256 |
str |
required | digest of the package (in hex format). |
arch |
str |
required | architecture of the package. |
Slices
For each slice installed in the file system, a JSON object with "kind":"slice"
will be present in the manifest.
{"kind":"slice","name":"foo_bar"}
These JSON objects have two attributes:
Field | Type | Required | Description |
---|---|---|---|
kind |
str |
required | type of JSON object – the value will always be slice . |
name |
str |
required | name of the slice, in the pkg_slice format. |
Paths
For each path defined in the slice definitions that Chisel installs in the file system, a JSON object with "kind":"path"
will be present in the manifest.
{"kind":"path","path":"/etc/","mode":"0755","slices":["foo_bar","abc_xyz"]}
{"kind":"path","path":"/etc/foo","mode":"0644","sha256":"abcd...","final_sha256":"abcd...","size":1234,"slices":["foo_bar"]}
{"kind":"path","path":"/etc/bar","mode":"0777","link":"/etc/foo","slices":["foo_bar"]}
These JSON objects have the following attributes:
Field | Type | Required | Description |
---|---|---|---|
kind |
str |
required | type of JSON object – the value will always be path . |
path |
str |
required | location of the path. |
mode |
str |
required | the permissions of the path, in an octal value format. |
slices |
list |
required | the slices that have added this path. |
sha256 |
str |
optional | the original checksum of the file as in the package (in hex format). This attribute is required for all regular files, except the manifest.wall file itself, which is an exception. |
final_sha256 |
str |
optional | the checksum of the file after it has been modified during installation (in hex format). This attribute is required only for files that have been mutated. |
size |
int |
optional | the final size of the file, in bytes. This attribute is required for regular files, except the manifest.wall file itself, which is an exception. |
link |
str |
optional | the target, if the file is a symbolic link. |
Note: unless explicitly included in the requested slices, the parent directories that are implicitly created in the filesystem will not be written to the Chisel manifest. In the example above, it is assumed that /etc/
is explicitly defined, with the same properties, in both foo_bar
and abc_xyz
.
List of Paths under a Slice
To state the path that a slice has added/changed, JSON objects with "kind":"content"
are used.
{"kind":"content","slice":"foo_bar","path":"/etc/foo"}
It has the following attributes:
Field | Type | Required | Description |
---|---|---|---|
kind |
str |
required | type of JSON object – the value will always be content . |
slice |
str |
required | name of the slice. |
path |
str |
required | location of the path. |
New features in v1.0.0
That’s all for now. Please let me know in comments if you have any questions.