Pantavisor 019 Features: Groups

Pantavisor Container Groups

In this post, we are going to talk about how to make use of the container groups feature included in the Pantavisor 019 release. Starting from the default group configuration and moving into a customized one, taking a fictitious set of network equipment devices as an example.

We are going to begin by understanding how the default groups are configured. Groups are defined as a JSON, and the default groups JSON looks like this:

[
  {
    "name":"data",
    "description":"Containers which volumes we want to mount but not to be started",
    "restart_policy": "system",
    "timeout": 30,
    "status_goal": "MOUNTED"
  },
  {
    "name":"root",
    "description":"Container or containers that are in charge of setting network connectivity up for the board",
    "restart_policy": "system",
    "timeout": 30,
    "status_goal": "STARTED"
  },
  {
    "name":"platform",
    "description": "Middleware and utility containers",
    "restart_policy": "system",
    "timeout": 30,
    "status_goal": "STARTED"
  },
  {
    "name":"app",
    "description": "Application level containers",
    "restart_policy": "container",
    "timeout": 30,
    "status_goal": "STARTED"
  }
]

As you can see, there are four default groups: data, root, platform and app. Each group can contain a description plus a default restart policy and default status goal/timeout for the containers in the group that does not have those explicitly configured.

Besides the default values for the member containers, groups will also define the start up order of the revision, as all containers from each group will not pass the BLOCKED status until each container from the previous group (if any) has reached its status goal.

This setup is good enough for our initial devices containers but what happens if we need something more complex?

A Use Case

In this made-up example, our software needs to support a variety of home and enterprise network devices (routers, switches, access points, firewalls…). There is a part of that software that is shared in between all of the devices, while other part is specific to each hardware, and we also want to have the option to personalize them with 3rd party or self-made applications by the final user.

This software has been traditionally integrated into monolithic systems, but we think it is time to upgrade to a containerized solution that will improve our workflow in many levels: independent parts that can be developed by independent teams/people, easily integrable into CI scripts, easily reusable across different hardware and manufacturers, etc.

In the design stage of our new software version, we dissect our legacy monolithic solution and identify the following groups of containers:

  • devtools: Development Containers that will not be included in production devices. For example: pvr-sdk and pv-avahi
  • net: The container that sets up the basic device network connectivity
  • hw-specific: Containers that might vary depending on the hardware we are running. For example: router, mesh, firewall, access-point, switch…
  • cloud: Containers that will enable the communication between the device and the cloud for new updates, metadata exchange, logs, etc.
  • ui: Containers that will provide the configuration user in interface. For example: webserver
  • app: 3rd party or self-made application containers that are installable by the user. For example: vpn, net-monitoring, home assistant…

Taking this high level design as an example, we are going to see how to set it up on a Pantavisor-enabled device in the next post.

How to Set Up Custom Groups

We need to override the default group configuration, this can be done in the device.json at the top level of your revision. To do so, first clone your device:

pvr clone https://pvr.pantahub.com/anibal/home_rpi64_latest

The device.json file has to look like this to accommodate the groups that we have identified in the previous post:

$ cd home_rpi64_latest
$ cat device.json
{
    "disks": [...],
    "groups": [
        {
            "description": "Development Containers that will not be included in production devices",
            "name": "devtools",
            "restart_policy": "container",
            "status_goal": "STARTED",
            "timeout": 10
        },
        {
            "description": "The container that sets up the basic device network connectivity",
            "name": "net",
            "restart_policy": "system",
            "status_goal": "READY",
            "timeout": 10
        },
        {
            "description": "Containers that will vary depending on the hardware we are running",
            "name": "hw-specific",
            "restart_policy": "system",
            "status_goal": "READY",
            "timeout": 10
        },
        {
            "description": "Containers that will enable the communication between the device and the cloud",
            "name": "cloud",
            "restart_policy": "container",
            "status_goal": "STARTED",
            "timeout": 30
        },
        {
            "description": "Containers that will provide the configuration user in interface",
            "name": "ui",
            "restart_policy": "condainer",
            "status_goal": "STARTED",
            "timeout": 30
        },
        {
            "description": "3rd party or self-made application containers installable by the user",
            "name": "app",
            "restart_policy": "container",
            "status_goal": "STARTED",
            "timeout": 60
        }
    ],
    "volumes": {...}
}

Notice that we cannot post this new JSON yet, as the existing containers might be still pointing to the legacy groups. We will see how to solve that in further posts in this thread.

Modifying the Groups of a Container

As in the restart policy and status goal posts, we are going to modify the group of an existing container by manually editing the src.json file in our previously cloned device:

cd home_rpi64_latest
ls

In here, among others, we have the pvr-sdk container, which is linked to the legacy platform group. Inside that directory, we will find the src.json and the run.json files. The first one will be used by pvr, while the second one will be parsed by Pantavisor itself. Let us first take a look at the configured group in the run.json:

$ jq .group pvr-sdk/run.json 
"platform"

As we can see, the pvr-sdk is still part of the platform group, but we want to modify this, as the new device.json does not have such group. If we want to change that without having to reinstall the container, we have to do it in the src.json, as run.json is not meant to be directly edited:

$ jq .args.PV_GROUP pvr-sdk/src.json 
"platform"

And manually edit it to look like this:

$ jq .args.PV_GROUP pvr-sdk/src.json 
"devtools"

Now, if we use the pvr app install command:

pvr app install homeassistant

The pvr tool will take the value from the src.json file and apply it at the run.json:

$ jq .group pvr-sdk/run.json 
"devtools"

After we have repeated this operation with all existing containers and make sure they are all pointing to the new groups, you can commit and post the changes to make them effective in your device:

pvr add .
pvr commit
pvr post -m "change legacy groups to new ones"

Setting a Group to a Container

Finally, we are going to add a new WireGuard container to the app group on the previous example device:

cd home_rpi64_latest
ls

To add the container linked to the group, we can use the pvr app add command with the --group option:

pvr app add --from registry.gitlab.com/pantacor/pv-platforms/ph-wireguard --group app wg
pvr add .
pvr commit
pvr post -m "add a new wireguard container to the app group"

And that is it. The new container will be started after all ui group containers have reached their status goals. It will also be set to the default parameters (restart policy, status goal and timeout) of the app group as we did not override any of the values when installing it.