This document shows an example of using declix to setup a simple unix system.
Define a target in declpi-target.pkl
:
amends "../target/Target.pkl"
target = new SshConfig {
user = "mike"
address = "declpi:22"
privateKey = "/home/mike/.ssh/id_ed25519"
}
Define system configuration in declpi.pkl
.
First let's make sure that all the packages that
we need are installed:
amends "../resources/Resources.pkl"
import "../resources/apt/Apt.pkl"
resources = new Listing {
...Apt.install(new Listing {
"tmux" "vim""git" "mc" "nnn" "ncdu"
"btop" "htop" "iftop"
"kitty-terminfo"
"ssh" "syncthing"
})
}
We can evaluate the config by running
pkl eval local/declpi.pkl
to see what does it expand into:
resources {
new {
type = "apt"
name = "tmux"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "vim"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "git"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "mc"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "nnn"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "ncdu"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "btop"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "htop"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "iftop"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "kitty-terminfo"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "ssh"
state = "installed"
updateBeforeInstall = false
}
new {
type = "apt"
name = "syncthing"
state = "installed"
updateBeforeInstall = false
}
}
From here we can check the state of the target system:
$ go run main.go state -t local/declix-target.pkl -r local/declix.pkl
Target: &{declix:22 mike /home/mike/.ssh/id_ed25519}
Resource Id | Current State | Expected State
-------------------------------------------------------
apt:tmux | missing | installed
apt:vim | missing | installed
apt:git | missing | installed
apt:mc | missing | installed
apt:nnn | missing | installed
apt:ncdu | missing | installed
apt:btop | missing | installed
apt:htop | missing | installed
apt:iftop | missing | installed
apt:kitty-terminfo | missing | installed
apt:ssh | 1:9.2p1-2+deb12u2 | installed
apt:syncthing | missing | installed
And we can update the system:
$ go run main.go apply -t local/declix-target.pkl -r local/declix.pkl
✓ +apt:tmux
✓ +apt:vim
✓ +apt:git
✓ +apt:mc
✓ +apt:nnn
✓ +apt:ncdu
✓ +apt:btop
✓ +apt:htop
✓ +apt:iftop
✓ +apt:kitty-terminfo
✓ +apt:syncthing
This sections shows the power of pkl language and not the declix itself.
We can define modules to encapsulate more complicated scenarious.
E.g. installing packages from a different apt source.
zerotier.pkl
:
import "../resources/filesystem/FileSystem.pkl" as FS
import "../resources/apt/Apt.pkl"
import "../resources/Resources.pkl"
import "../content/Content.pkl"
url = "https://download.zerotier.com/"
codename = "bookworm"
function install(): Listing<Resources.Resource> = new Listing {
new FS.File {
path = "/etc/apt/trusted.gpg.d/zerotier.com.gpg"
content = new Content.File {
file = "modules/zerotier.com.gpg"
}
owner = "root"
group = "root"
permissions = "644"
}
new FS.File {
path = "/etc/apt/sources.list.d/zerotier.list"
owner = "root"
group = "root"
permissions = "644"
content = "deb [signed-by=/etc/apt/trusted.gpg.d/zerotier.com.gpg] \(url)debian/\(codename) \(codename) main"
}
new Apt.Package {
name = "zerotier-one"
state = "installed"
updateBeforeInstall = true
}
}
function remove() : Listing<Resources.Resource> = new Listing {
new Apt.Package {
name = "zerotier-one"
state = "missing"
updateBeforeInstall = true
}
new FS.File {
path = "/etc/apt/sources.list.d/zerotier.list"
state = new FS.Missing {}
}
new FS.File {
path = "/etc/apt/trusted.gpg.d/zerotier.com.gpg"
state = new FS.Missing {}
}
}
function enable(): Listing<Resources.Resource> = new Listing {
...install()
// todo
}
And now add it to our resource file:
import "../modules/zerotier.pkl"
resources = new Listing {
...Apt.install(new Listing {
// tools
"tmux" "vim""git" "mc" "nnn" "ncdu"
"btop" "htop" "iftop"
"kitty-terminfo"
// services
"ssh" "syncthing"
})
...zerotier.enable()
}
Let's check the state:
Target: &{hamd:22 mike /home/mike/.ssh/id_ed25519}
Resource Id | Current State | Expected State
-----------------------------------------------------------------------------------------
apt:tmux | 3.3a-3 | installed
apt:vim | 2:9.0.1378-2 | installed
apt:git | 1:2.39.2-1.1 | installed
apt:mc | 3:4.8.29-2 | installed
apt:nnn | 4.7-1 | installed
apt:ncdu | 1.18-0.2 | installed
apt:btop | 1.2.13-1 | installed
apt:htop | 3.2.2-2 | installed
apt:iftop | 1.0~pre4-9 | installed
apt:kitty-terminfo | 0.26.5-5 | installed
apt:ssh | 1:9.2p1-2+deb12u2 | installed
apt:syncthing | 1.19.2~ds1-1+b4 | installed
file:/etc/apt/trusted.gpg.d/zerotier.com.gpg | missing | 4b9b800c root:root 644
file:/etc/apt/sources.list.d/zerotier.list | missing | b71efca1 root:root 644
apt:zerotier-one | missing | installed
Let's introduce a new command actions
that
displays only what needs to be done:
+file:/etc/apt/trusted.gpg.d/zerotier.com.gpg
+file:/etc/apt/sources.list.d/zerotier.list
+apt:zerotier-one
And apply:
Applying...
✓ +file:/etc/apt/trusted.gpg.d/zerotier.com.gpg
✓ +file:/etc/apt/sources.list.d/zerotier.list
✓ +apt:zerotier-one
Now rerun actions
to see that the system is fully
up-to-date:
Target: declpi:22
No actions necessary