Skip to content

Commit

Permalink
docs: add versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
koehlma committed May 10, 2024
1 parent 8b19e40 commit 2ce1813
Show file tree
Hide file tree
Showing 25 changed files with 11,045 additions and 5 deletions.
9 changes: 8 additions & 1 deletion www/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const config: Config = {
onBrokenMarkdownLinks: "warn",

// We do not care about old browsers not supporting SVG.
favicon: "img/logo.svg",
favicon: "/img/logo.svg",

organizationName: "silitics",
projectName: "rugpi",
Expand All @@ -27,6 +27,7 @@ const config: Config = {
({
docs: {
sidebarPath: require.resolve("./sidebars.js"),
lastVersion: '0.6',
editUrl: "https://github.com/silitics/rugpi/tree/main/www/",
},
blog: {
Expand Down Expand Up @@ -73,6 +74,12 @@ const config: Config = {
label: "Docs",
},
{ to: "/blog", label: "Blog", position: "left" },
{
type: 'docsVersionDropdown',
position: 'right',
// dropdownItemsAfter: [{to: '/versions', label: 'All versions'}],
dropdownActiveClassDisabled: true,
},
{
href: "https://github.com/silitics/rugpi",
position: "right",
Expand Down
8 changes: 4 additions & 4 deletions www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
"fmt": "prettier --write \"**/*.{ts,tsx,md,mdx,js,json,css}\""
},
"dependencies": {
"@docusaurus/core": "^3.1.1",
"@docusaurus/preset-classic": "^3.1.1",
"@docusaurus/core": "^3.3.2",
"@docusaurus/preset-classic": "^3.3.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.1.1",
"@docusaurus/tsconfig": "^3.1.1",
"@docusaurus/module-type-aliases": "^3.3.2",
"@docusaurus/tsconfig": "^3.3.2",
"@types/react": "^18.2.29",
"prettier": "^3.1.0",
"typescript": "~5.2.2"
Expand Down
10,284 changes: 10,284 additions & 0 deletions www/pnpm-lock.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions www/versioned_docs/version-0.6/cookbook/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Cookbook",
"position": 20,
"link": {
"type": "doc",
"id": "index"
}
}
12 changes: 12 additions & 0 deletions www/versioned_docs/version-0.6/cookbook/discoverability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Discoverability

Sometimes an embedded device should be automatically discoverable in a network.
For instance, automatic discovery allows service technicians to scan for devices and users to find and access devices more easily.

To facility discovery, [Avahi](https://www.avahi.org/) can be used.
To install Avahi, you can use the [Avahi Recipe](https://github.com/silitics/rugpi-extra/tree/main/recipes/avahi) available in the [`rugpi-extra` repository](https://github.com/silitics/rugpi-extra/tree/main).

Installing Avahi makes it possible to reach the system under the local domain name `<hostname>.local` in the local network via [Multicast DNS](https://datatracker.ietf.org/doc/html/rfc6762).
As a result, it is easy to access the Raspberry Pi simply by entering its hostname followed by `.local`, e.g., in the navigation bar of a browser.
In addition, the recipe allows making SSH, SFTP, and an HTTP interface discoverable via [DNS-SD](https://datatracker.ietf.org/doc/html/rfc6763).
To this end, use the recipe's parameters.
3 changes: 3 additions & 0 deletions www/versioned_docs/version-0.6/cookbook/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Cookbook

A collection of guides and recipes for reoccurring problems.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Network Configuration
103 changes: 103 additions & 0 deletions www/versioned_docs/version-0.6/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
sidebar_position: 1
---

# Getting Started 🚀

Rugpi consists of two components, _Rugpi Bakery_ for building customized images, and _Rugpi Ctrl_ for maintaining and managing a Rugpi system.
This quick-start guide takes you through the steps necessary to build a custom Rugpi image with Rugpi Bakery.

## Building an Image

You can [build images locally](#building-an-image-locally) or [using a CI system like GitHub Actions](#using-github-actions).

### Using GitHub Actions

By far the fastest and easiest way to get a working image is to use [GitHub Actions](https://github.com/features/actions).
Simply [create a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template#creating-a-repository-from-a-template) from our [Rugpi template](https://github.com/silitics/rugpi-template) and GitHub Actions will build the image automatically from your repository.
Any modifications you push to your repository will trigger GitHub Actions to rebuild the image with your customizations.
Please be aware that building an image is a rather resource-heavy process and may quickly consume your CI minutes, if the repository is private.

That was easy! Nevertheless, we recommend reading the next section, so you understand how it works under the hood.

### Building an Image Locally

First, obtain a local copy of the [Rugpi template](https://github.com/silitics/rugpi-template), for instance by [downloading its contents](https://github.com/silitics/rugpi-template/archive/refs/heads/main.zip) or cloning it:

```shell
git clone https://github.com/silitics/rugpi-template
```

Note that Rugpi Bakery is distributed as a Docker image (for `arm64` and `amd64`) because it relies on various Linux tools and facilities to build the image.
Building images outside of Docker is fundamentally only possible on Linux and not officially supported.
So, to build the image locally, a working Docker installation is required.
On MacOS, make sure to use the [MacOS virtualization framework and VirtioFS](https://docs.docker.com/desktop/settings/mac/#general) (the default with recent versions of Docker Desktop).
The template ships with a `run-bakery` shell script (for Linux and MacOS) to run Rugpi Bakery in a temporary container.
For Windows, please run Rugpi Bakery inside WSL.

To print the usage instructions of Rugpi Bakery, in the root directory of the template, run:

```shell
./run-bakery help
```

On a non-`arm64` system, you need to configure [`binfmt_misc`](https://en.wikipedia.org/wiki/Binfmt_misc) to emulate `arm64` (analogously for `armhf`).
The easiest way to do so, and as we are already using Docker anyway, is by running the following command:

```shell
docker run --privileged --rm tonistiigi/binfmt --install arm64
```

Building an image is then generally achieved by the commend:

```shell
./run-bakery bake image <image name> build/image.img
```

The configuration file `rugpi-bakery.toml` defines the available images.
For instance, to build an image for Raspberry Pi 4 including the necessary firmware update for the `tryboot` boot mechanism, run:

```shell
./run-bakery bake image pi4 build/image-pi4.img
```

The images specified in the template use the `customized` *layer* defined in `layers/customized.toml`.
A layer specifies the base system and the modification which should be done to it.

When you build an image, internally, Rugpi Bakery does the following steps:

1. First, it downloads and extracts a base image of Raspberry Pi OS.
This is achieved via the following directive:

```toml title="layers/customized.toml"
parent = "core/raspios-bookworm"
```

This will tell Rugpi Bakery to use the layer `raspios-bookworm` provided by Rugpi itself as a basis for the `customized` layer.
Note that you can define your own base layers.
They simply contain an URL of the base image to use.

2. Next, the recipes defined in the layer are applied.
A *recipe* describes modifications to be made to the system.
For instance, the `core/ssh` recipe enables SSH.
Recipes can have parameters.
For instance, the `root_authorized_keys` parameter of the `core/ssh` recipe sets `authorized_keys` for the `root` user.
To be able to login as `root` via SSH later, you should replace the existing key with your public key.
In addition to the builtin recipes, you can supply your own recipes.
In case of the template, the `hello-world` recipe in the `recipes` directory installs a static website which is served by Nginx.
For further information about recipes, checkout the [user guide's section on System Customization](./guide/system-customization).

3. Finally, after applying all customizations, an image is produced.
The resulting image is ready to be written to an SD card, e.g., using Raspberry Pi Imager.
Note that you cannot use Raspberry Pi Imager to apply any configurations like passwords or WiFi settings.
The template also defines images for other boards than Raspberry Pi 4.
For further images, we refer to the `rugpi-bakery.toml` configuration file and the [Supported Boards](./guide/supported-boards.md) section of the user guide.

On the first boot, Rugpi Ctrl will repartition the SD card and then boot into the actual system.
Once the system is running, you should be able to visit the static website via the system's IP address and connect via SSH.

Congratulations! You built your first image with Rugpi Bakery. 🙌

Feel free, to change the website in `recipes/hello-world/html` and experiment with the recipes.
As a next step, we recommend reading the [user guide](./guide).
It covers all the details on system customization, state management, and over-the-air updates.
8 changes: 8 additions & 0 deletions www/versioned_docs/version-0.6/guide/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "User Guide",
"position": 10,
"link": {
"type": "doc",
"id": "index"
}
}
3 changes: 3 additions & 0 deletions www/versioned_docs/version-0.6/guide/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# User Guide

This user guide assumes that you already read the [quick-start guide](../getting-started) and know how to build a customized image.
137 changes: 137 additions & 0 deletions www/versioned_docs/version-0.6/guide/over-the-air-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
sidebar_position: 3
---

# Over-the-Air Updates

Rugpi supports robust *over-the-air* (OTA) updates with rollback support to the previous version.
OTA updates comprise the full system including firmware files and the boot configuration.
Rugpi uses an A/B approach ensuring that a working copy of the previous version is always kept.
This approach drastically reduces the likelihood of bricking devices in the field due to corrupted software caused by a failed or incomplete update, thereby reducing any related support effort.
In addition, it has the following advantages:

- OTA updates can almost completely run in the background, without adversely affecting any users of a device.
The only service interruption is caused, when the device reboots into the new version.
Rebooting to finalize an update can happen at the discretion of users and, if all goes well, does not take longer than any normal reboot, minimizing any inconveniences.
- As the previous version is kept, a rollback to the old version is possible if users experience any problems with the new version.[^1]

[^1]: This requires application support.


### A/B Update Scheme

The A/B update scheme uses two sets of system and boot partitions, the A set and the B set.
We call the currently booted set *hot set* and to the other *cold set*.
The usual partition layout of a Rugpi installation comprises seven partitions:

- Partition 1: Contains the bootloader configuration for switching between the A and B set.
- Partition 2: The `/boot` partition of the A set.
- Partition 3: The `/boot` partition of the B set.
- Partition 4: The extended MBR for the additional partitions.
- Partition 5: The root partition of the A set.
- Partition 6: The root partition of the B set.
- Partition 7: Contains any persistent state (see [State Management](./state-management)).

The bootloader configuration specifies the default set of partitions.
We call the other, non-default set, the *spare set*.
An update is only possible if the hot set is also the default set.
That way, if anything goes wrong while installing the update, the system will boot into the previous known-good version by default.
The Rugpi update mechanism installs the update to the cold spare set of partitions.
After installing the update, it tries booting into the newly installed version, crucially without changing the default set.
Hence, if anything goes wrong, the system automatically reboots into the previous version by default.
Only after booting successfully into the newly installed system, by which the set of partitions with the new version becomes the hot set, and verifying that everything is in working order, the update is made permanent by making the hot set the default set.

## Updating a System

To update a system, first an image needs to be [build using Rugpi Bakery](/docs/getting-started).
Further, this image needs to find its way[^2] onto the Raspberry Pi running Rugpi.
The image is then installed to the cold spare set of partitions with:

```shell
rugpi-ctrl update install <path to the artifact>
```

This command will also automatically try rebooting into the new version after it has been installed.
To prevent this from happening, use the `--no-reboot` command line flag.
Note that this command will not make the update permanent in any way.

When using the `--no-reboot` flag, a reboot to the cold spare can later be triggered with:

```shell
rugpi-ctrl system reboot --spare
```

Note that a persistent overlay that may exist for the spare partition is deleted prior to installing the update (see [State Management](./state-management.md)).
To avoid the overlay from being discarded, use the `--keep-overlay` option when installing the update.
Please be aware that this may lead to incompatibilities between the overlay and the freshly installed system.

Rugpi also has experimental support for streaming updates directly to the SD card instead of first storing the image and then installing it.
To this end, use the option `--stream`.
With `--stream`, you can use `-` as artifact path to install an image streamed via stdin to Rugpi Ctrl.
This also allows using compressed images.
For instance, to download, decompress, and install an image on-the-fly, use:

```shell
curl <url to the image> | xz -d | rugpi-ctrl update install --stream -
```

In case the internet connection is unstable, you may also want to use

```shell
wget -q -t 0 -O - <url to the image> | rugpi-ctrl update install --stream -
```

to retry downloading indefinitely. For further details, we refer to the manpage of `wget`.


Streaming an image is faster because the data only has to be written to the SD card once.
Furthermore, it has the advantage that the image does not take up precious space on the data partition during the installation.

[^2]: How this happens is outside the scope of Rugpi's core functionality.

### Committing an Update

After rebooting into the new version and verifying that everything is in working order, the update is made permanent with:

```shell
rugpi-ctrl system commit
```

Note that this command always makes the hot set of partitions, i.e., the currently booted system, the default set.
Hence, it must be run from within the updated version.
To prevent breaking the system, it is impossible to make the cold set the default set.[^3]

Committing an update is up to the concrete update workflow of the application.
If you want to automatically commit the hot set during the boot process, you may enable the `rugpi-auto-commit` recipe.
Note that this recipe installs an equally named Systemd service which will also commit an old version if booted into with the rollback feature (see bellow).

[^3]: Using the `rugpi-ctrl` command line tool.

### Performing a Rollback

Like updating, performing a rollback is a two-step process.
A full rollback consists of first rebooting into the spare set (containing the previous version) and then committing the rollback after verifying that it is in proper working order.

To boot into the spare set, run:

```shell
rugpi-ctrl system reboot --spare
```

Then, after rebooting, commit the rollback with:

```shell
rugpi-ctrl system commit
```

### On Atomicity of Commits

Note that commits are the only critical operation because they modify the default set.
This is done by temporarily remounting the bootloader configuration partition such that it is writeable.
For the `tryboot` and U-Boot boot flow, the `autoboot.txt` file and `bootpart.default.env` file are then replaced as suggested by [Raspberry Pi's documentation on the `tryboot` mechanism](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#fail-safe-os-updates-tryboot).
As the filesystem is FAT32, the automitcity of this operation cannot be guaranteed.
Still, Rugpi Ctrl does its best by first creating a new file and then replacing the old one with the new one by renaming it, and, the Linux kernel does guarantee atomicity for renaming.
However, should the system crash during this process, the FAT32 filesystem may still be corrupted.
We think that this is an acceptable risk as the likelihood of it happening is very low and any alternatives, like swapping the MBR, may be problematic for other reasons.[^4]

[^4]: If you have any suggestions, please share them with us.
18 changes: 18 additions & 0 deletions www/versioned_docs/version-0.6/guide/rugpi-admin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
sidebar_position: 4
---

# Rugpi Admin

*Rugpi Admin* is a web-based admin interface for managing a Rugpi system.
In particular, it allows users to update a system simply by uploading an image and it offers direct access to state management features.

🚧 **While everything works, the UX is far from optimal. For now, Rugpi Admin is very much a proof of concept.**

🚧 For now, if you install an update by uploading an image, you will not get any kind of progress indication from the UI and it may seem as nothing is happening. Uploading the image and installing it may take some time. Check your browsers loading indicator to verify whether the image is uploaded and installed. We plan to improve the UI/UX once Rugpi's core is production-ready.

By default, Rugpi Admin is served on port `8088`.

Screenshot:

![Rugpi Admin Screenshot](/img/screenshots/rugpi-admin.png)
Loading

0 comments on commit 2ce1813

Please sign in to comment.