Minimum viable bootstrapping for your infrastructure, with great observability
Buckle is an extremely lightweight infrastructure bootstrapping agent designed to operate as an alternative to more complex (and powerful) tools like Ansible, Puppet, Chef and SaltStack.
Where Buckle shines is in situations where you need to configure a host quickly, but really don't want to maintain all of the "fluff" that comes with a full blown configuration management system. A great example of this is using the CustomScriptExtension on Azure to bootstrap or upgrade a machine.
Buckle is written in Rust and takes advantage of the great OpenTelemetry integration provided by tracing.rs to provide centralized visibility into your deployments.
- Lightweight agent written in Rust.
- Exceptional execution tracing through OpenTelemetry.
- Dynamic configuration loading.
- Packages with dependency-driven execution ordering.
- File templating.
- No special knowledge required to operate (it's just running bash scripts).
Buckle is responsible for applying one or more configuration packages to your system. These packages
are discovered from your provided --config DIR
using a filesystem layout which looks like the following.
.
└── my-config/
│
├── config/
│ ├── defaults.env
│ └── azure-vmss.sh
│
├── secrets/
│ └── logging-keys.env
│
└── packages/
├── pkg1/
│ ├── config/
│ │ └── versions.env
│ ├── files/
│ │ ├── confd/
│ │ │ └── myapp.conf
│ │ └── systemd/
│ │ └── myapp.service
│ ├── scripts/
│ │ └── enable-service.sh
│ └── package.yml
│
├── pkg2/
│ ├── files/
│ │ └── confd/
│ │ └── logging.conf
│ └── package.yml
│
└── pkg3/
├── scripts/
│ └── install.sh
└── package.yml
There are several key directories which you fill find here:
Files within this directory will be read by Buckle and their contents exposed as environment variables within your scripts and templates. The file extension used will determine how the file is read, with the following file extensions currently supported:
.env
files are read line-by-line as a sequence ofKEY=value
pairs..sh
files are executed with the system'sbash
interpreter and their stdout parsed line-by-line as a sequence ofKEY=value
pairs..ps1
files are executed with the system'spwsh
interpreter and their stdout parsed line-by-line as a sequence ofKEY=value
pairs..bat
files are executed with the system'scmd.exe
interpreter and their stdout parsed line-by-line as a sequence ofKEY=value
pairs..cmd
files are executed with the system'scmd.exe
interpreter and their stdout parsed line-by-line as a sequence ofKEY=value
pairs.
This means that it is possible to write scripts which will retrieve information about the current environment, including calling local metadata services etc.
You can define config at the global level, as well as the package level. All packages will inherit the global config fields you provide and will overlay their own config on top of those.
DEBIAN_FRONTEND=noninteractive
This config script will attempt to retrieve the location, IP address and subnet that the Azure VMSS instance is running on.
LOCATION="$(curl -m 5 -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2020-09-01" 2>/dev/null | jq -r '.compute.location')"
IP_ADDRESS="$(curl -m 5 -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2020-09-01" 2>/dev/null | jq -r '.network.interface[0].ipv4.ipAddress[0].privateIpAddress')"
SUBNET="$(curl -m 5 -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2020-09-01" 2>/dev/null | jq -r '.network.interface[0].ipv4.subnet[0].address + "/" + .network.interface[0].ipv4.subnet[0].prefix')"
echo "LOCATION=$LOCATION"
echo "IP_ADDRESS=$IP_ADDRESS"
echo "SUBNET=$SUBNET"
Secrets behave identically to the config
directory, however their contents are not emitted by Buckle to your logging/telemetry.
This helps avoid inadvertently exposing those secrets, however Buckle does not strip secrets from your script output. The values
provided by secrets take precedence over their config counterparts (when a secret and config have the same name, the secret's value will
win).
Packages are the unit of bootstrapping that Buckle relies on. Packages are intended to be composable and self-contained so that they can easily be re-used between projects. At a high-level, a package is composed of some metadata describing the package and its dependencies, the configuration and secrets which apply to the package, the files the package will generate, and the scripts which will be executed to set up the package.
.
├── config/
│ └── versions.env
├── files/
│ ├── confd/
│ │ └── myapp.conf
│ └── systemd/
│ └── myapp.service
├── scripts/
│ └── enable-service.sh
└── package.yml
The metadata file describing the package should be a YAML file containing the following structure:
description: |
This is a description of what the package installs and anything that
an operator should be aware of when using the package.
# You can list other packages which should be installed before this one
needs:
- pkg1
- pkg2
# Here you provide the mapping between files in your package and the
# target host's filesystem.
files:
# Files within the ./files/confd/ directory should be placed in /etc/myservice.d/
confd: /etc/myservice.d
systemd: /etc/systemd/system
The files directory should contain a series of subdirectories which correspond to the
package.yml#files
map's keys. In the example above, we should expect to find two directories
with the names confd
and systemd
. Files within these directories will be placed on
the host filesystem in the directories listed in package.yml
. Rich directory structures
are also supported and will be accurately reflected on the host filesystem.
At times, it can be useful to generate the content of these files dynamically. Buckle supports
this use case for files that have the .tpl
file extension. These files will have the .tpl
extension stripped and their contents templated using Go's template/text
templating language. Any of your configuration variables will be accessible like this: {{ .IP_ADDRESS }}
.
The scripts directory should contain any scripts you wish to execute on the host system when applying this package. Scripts should use one of the supported file extensions below:
.sh
files are executed with the system'sbash
interpreter..ps1
files are executed with the system'spwsh
interpreters..bat
files are executed with the system'scmd.exe
interpreter..cmd
files are executed with the system'scmd.exe
interpreter.
Scripts are executed after files have been placed on the host.