At the dawn of 2021 Foundries.io delivered a new simple yet powerful way to manage your FoundriesFactory devices. We call it "device groups" and as a name suggests it basically allows to categorize devices belonging to a single Factory into user defined groups.
Device Groups in essence solve two major tasks. The first one is quite naive - it allows a user to assign a group (aka a label) to a device. However, we at Foundries.io wanted to give this intuitive mechanism more power. Thus, the second task it solves is an ability to provision a specific configuration to a dedicated group of devices, complementing a previously existing ability to define factory-wide or per-device configurations.
Device Groups can be defined in your Factory using a newly added set of fioctl commands:
fioctl config device-group [command]. For example,
fioctl config device-group create canary "Canary tests" will create a new Device Group named "canary", that can later be used in other commands.
A device can be added into an existing group using a new
fioctl device config group <device-name> <group-name> command; and it can be removed from a group using a
fioctl device config group <device-name> --unset command.
A natural limitation of Device Groups is that a device can only belong to at most one group, that makes a lot of sense when thinking about the next topic.
Managing per-Group Configuration
As you might already know from a previous post our fioconfig tool allows to securely provision a device configuration; and there is a set of commands in Fioctl to manage a Factory-wide (
fioctl config) or a per-device (
fioctl device config) configuration.
Now, a new Device Groups feature adds one more element into this equation: an ability to define a per-Group configuration using
fioctl config -g <group> set of commands. This configuration overrides Factory-wide and is overridden by a per-device one.
As a practical example lets assume that a user has configured a factory-wide updates config like this:
fioctl config updates --tags=master. Now, lets assume that a user wants canary builds to accept updates from development branch which were specially tagged as "canary". Re-using a "canary" group from above this can be done using this simple command:
fioctl config updates -g canary --tags=canary. Furthermore, if there is a single device "test-device" which a user wants to permanently switch to update to development builds - that can be done like this:
fioctl device config updates test-device --tags=devel.
An above simple 3-level configuration hierarchy allows our Foundries.io users to build quite powerful configuration management patterns solving contemporary business needs.
How configuration layering works
Lets try to answer this question: how does Fioconfig know how to merge together a factory-wide, a per-group, and a per-device configuration?
Internally, we store all configuration as so-called files. Those files' names can be set explicitly in one case e.g. as a parameter to a
fioctl config set <filename>=<config value> command, or might be specified implicitly in the other case e.g. a
fioctl config updates --tags=master internally creates a file named "z-50-fioctl.toml". Now it should be apparent that we used the most simple yet the most reliable form of a configurations merge algorithm: rewrite files in higher-level configs with files in lower-level configs.
For example, lets assume the following:
- There are "foo.cfg", "bar.cfg" and "baz.cfg" files defined Factory-wide;
- There are "bar.cfg" and "baz.cfg" files defined for a Device Group "canary";
- There are "baz.cfg" and "dev.cfg" files defined for a device "canary-dev";
Now, when a device "canary-dev" is added into a Device Group "canary" - it will receive the following configuration files:
- "foo.cfg" from a Factory-wide configuration;
- "bar.cfg" from a "canary" Device Group configuration;
- "baz.cfg" and "dev.cfg" from a "canary-dev" device configuration;
As one can see, it's indeed a simple overlay where lower-level config files overwrite higher-level config files where file names match.
How to remove a configuration
Now, another interesting question is this: what happens if I remove specific config files from per-group or per-device configuration(s)?
Well, an answer might be unexpected albeit logical: in this case a configuration for this specific file falls back to a Factory-wide or per-Group configuration respectively (i.e. taken from higher layers). If ones intention is to really override e.g. a Factory-wide config file at per-Group or per-device level - that file must be specified per-Group (or per-device). And if one wants to "remove" that file - need to supply an empty file instead, because a literal file removal means "fall back to default" as described above.
In the next blog post I will talk about other interesting use cases for Device Groups we envision here at Foundries.io.