Once you have several hosts managed with propellor, you'll probably find yourself making changes to config.hs, that might affect multiple hosts.
You can manually run propellor --spin $HOST
for each affected host in
turn. But that can get old. Time to automate it.
There are two approaches you can follow:
Set up a centralized git repository, and make your hosts check it for updates using cron. Then you can
git commit -S
andgit push
changes that affect any number of hosts.Set up a conductor host. When propellor is run on this host, it will automatically spin the other hosts.
We'll start with a centralized git repository and cron, because that's the easiest thing to set up, and it's a good idea to have one as a backup. Especially if you have co-maintainers, you'll obviously want to use a centralized repository to allow collaboration.
where to put the central repository
The central repository does not need to be trusted; it can be hosted anywhere, and propellor will only accept verified gpg signed git commits from it. See security for details, but this means you can put it on github without github being able to 0wn your propellor driven hosts, for example.
Or, you can just add some properties to one of your hosts to make it
serve the central repository. Using Propellor.Property.Git.daemonRunning
for example.
how to set up the central repository
You can add a central git repository to your existing propellor setup easily:
Push propellor's git repository to a central server (github or your own):
cd ~/.propellor/; git remote add origin ssh://git.example.com/propellor.git; git push -u origin master
Configure the url your hosts should use for the git repository, if it differs from the url above, by setting up a remote named "deploy":
cd ~/.propellor/; git remote add deploy git://git.example.com/propellor.git
Add a cron job property to your hosts, which will make them periodically check for changes that were committed to the central repository:
Cron.runPropellor (Cron.Times "*/30 * * * *")
Let your hosts know about the changed configuration (including the url to the central repository), by running
propellor --spin $HOST
for each of your hosts.
Now the hosts will automatically update every 30 minutes, and you can
git commit -S
and git push
changes that affect any number of
hosts.
setting up a conductor host
When propellor is run on a conductor host, it will automatically spin some other hosts.
Using a conductor host has many benefits over a centralized git repository and cron:
- Private data, set with
propellor --set
, is gpg encrypted, and hosts cannot decrypt it when their cron job pulls changes from the central repository. So after updating the private data of a host, you still need to manually runpropellor --spin $HOST
. A conductor avoids this problem. - You have to wait a while for a change you commit to be deployed by cron. It would be nice to be able to run "propellor" once and have it update all your hosts immediately.
- When there's a problem, a cron job can hide it, while if you're running propellor yourself, you can notice the problem more easily.
- You might want to update hosts in a specific order. For example, update your dns server last. Cron jobs can't do this, but conductors can.
Conductors are configured using the Propellor.Property.Conductor module.
If you decide to go this route, pick the host you want to make a conductor, and add some properties to it:
mylaptop = host "mylaptop.example.com"
& conducts [somehost, otherhost, lasthost]
& Ssh.userKeys (User "root")
[(SshEd25519, somelongstring)]
The Ssh.userKeys is used to give the root user on the conductor a known ssh public key. You'll need to feed the private ssh key into propellor's privdata store (see security).
Each of the hosts that is being conducted needs to have its ssh host key specified as well. This is needed so that the conductor can ssh into the hosts.
somehost = host "somehost.example.com"
-- This sets the private key as well, so it will need to
-- be fed into propellor's privdata store.
& Ssh.hostKeys hostContext [(SshEd25519, somelongstring)]
lasthost = host "lasthost.example.com"
-- This way indicates the public key, but doesn't change
-- the actual host configuration.
& Ssh.hostPubKey SshEd25519, somelongstring
Also, make this change:
- main = defaultMain hosts
+ main = defaultMain (orchestrate hosts)
Give each of the hosts you changed one last manual --spin, to set things up for the conductor.
Now you're ready to use the conductor. When you spin the conductor host, it will in turn spin each of the hosts it's conducting.
This simple conductor configuration can be easily adapted to better meet your needs. For example, if you have a host that should only be spinned once all the other hosts have successfully been updated, the conductor can be configured to do that:
& conducts [somehost, otherhost]
`before` conducts lasthost
Other possibilities include chains of conductors spinning other conductors that spin hosts, etc.
spin
orspin'
functions to do that from forked threads or processes, with the master process doing the spin commit, but I may be overlooking some potential issues...Yes, that was the main reason to add Propellor.Property.Concurrent
It should be able to parallelize any properties using the combinators in there. Including
Propellor.Property.Conductor.conducts
For example:
Or, something like this to conduct a whole list of hosts in parallel (have not tried to compile it, may need minor tweaking):
Note that concurrent output will be serialized, so you'll probably see propellor running live on the first host and then the ones that were conducted in the background will have their output dumped the console later on.