Puppet Twitch
Puppet Twitch provides a lightweight http interface for remotely triggering puppet runs. That's it! The plan was to not use more resources than it had to, and also not open up any security holes.
Currently this project is definitely in the beta phase, there are probably a few kinks to work out, and it's worth highlighting that at the moment puppet twitch doesn't use a "proper" http server, it's just a TCP socket with an http shaped regex. This makes it lightweight, but there might be a whole bunch of compatibility/security issues with it, so I'm looking to swap that out (see ToDo section)
Triggering puppet
To trigger a puppet run on a node running puppet-twitch, simply hit the /puppet/twitch
endpoint over http. For example, to trigger puppet on node01.example.com
running puppet-twitch on the default port:
$ curl http://node01.example.com:2023/puppet/twitch
Puppet-twitch will respond with one of the following (<status code>, <body>
):
202, Triggered puppet run
- The puppet run has been triggered asynchronously409, Puppet already running
- Either the server is already processing a request, or puppet's lock file exists indicating the agent is already running500, <error message>
- Something went wrong!
Async
You can use the async
parameter (default true
) to make the request wait for the puppet run to finish before returning. Note that this can be a few minutes, so timeouts etc will need to be taken into account:
$ curl http://node01.example.com:2023/puppet/twitch?async=false
In this case puppet-twitch will respond with 200, Puppet run complete
Puppet Install
There is a puppet module called twitch
in the source that can be used to setup and install puppet-twitch. The module isn't in the forge yet so copy and paste for now.
To use the defaults on Linux, simply include twitch
. This will:
- Create a user and group called
twitch
- Add specific puppet commands to
/etc/sudoers.d/twitch
(seeinit.pp
in thetwitch
module for the list of commands) - Create the
/var/run/puppet-twitch
directory that is writable by thetwitch
user - Install the gem
- Start the server as the
twitch
user on the default port (2023)
The twitch
class is parameterized, so user, port, run directory, etc can be configured if necessary
Prerequisites
- ruby 1.9.3 or above as
require_relative
isn't supported by lower versions - Open the port to incoming connections, the
twitch
module will not change any network configuration. - Add
/etc/sudoers.d
to thesudoers
file. This is a common configuration and is done by default on some VMs (e.g.Vagrant boxes, EC2 instances) but thetwitch
module assumes this is already setup.
Manual Install:
$ gem install puppet-twitch
(run as root so that it's available to all users)
Use the puppet-twitch
command to start/stop the server
$ puppet-twitch {start|stop|restart|status} dir=</path/to/run/dir> [-- [bind=<binding>] [port=<port>]]
For example to start the server on a custom port
$ puppet-twitch start dir=/var/run/puppet-twitch -- port=8090
And then stop it
$ puppet-twitch stop dir=/var/run/puppet-twitch
Prerequisites
- All prerequisites from the puppet install apply
- Create a user to run the server (one that doesn't have root permissions)
- Allow user to run the necessary puppet commands with
sudo
and without password (seeinit.pp
for commands) - Create a run directory for pids and logs (writable by the user)
Args:
dir
: Absolute path for directory that will store the pids and logs. The directory must be manually created and must be writable by the user that is running the server. It must be provided for every action (start
/stop
/status
etc), there is a ToDo to fix thisbind
: The IP address to bind to, defaults to0.0.0.0
port
: The port to listen on, defaults to2023
Running from source (for development/testing)
You can use rake install
to build and install the gem locally (I recommend using rvm
gemsets to isolate gem installs), but you can also run the code direct from source in two ways:
ruby lib/puppet-twitch/controller.rb [bind=<binding>] [port=<port>]
ruby lib/puppet-twitch/server.rb dir=</path/to/run/dir> [-- [bind=<binding>] [port=<port>]]
The difference is that running controller.rb
will run the server in the foreground and output will be logged to STDOUT
. Running server.rb
will start the server as a daemon and output will be logged to a logfile in the run directory
If you want to run the server in single threaded mode (i.e. the server doesn't fork threads for incoming connections) then set async = false
in controller.rb
, this is useful for debugging issues that aren't related to threading.
Vagrant
To test in a more isolated environment you can use vagrant, just spin up the boxes (see nodes.json
), ssh into a node, then run puppet. After that, puppet-twitch should be running on the node and you can test by hitting the endpoint:
[user@localhost] $ vagrant up
[user@localhost] $ vagrant ssh node01
[vagrant@node01] $ sudo puppet agent -t
[vagrant@node01] $ curl http://localhost:2023/puppet/twitch
The puppet class that's applied to the nodes (twitch::test::vagrant
) inherits from the normal twitch
class but also:
- Gives the
twitch
user a password (set totwitch
) and a shell (/bin/bash
) so you can switch to thetwitch
user for debugging - Installs the gem from the project's
pkg
directory that is mounted by vagrant, rather than downloading the gem from rubygems
In order to get local changes running on the vagrant machines, use the rake latest
command which builds the gem as normal, but then also creates a symlink called puppet-twitch-latest.gem
to the built gem. This is the file that the twitch::test::vagrant
class looks for so that you don't have to specify a version
ToDo:
- Proper http server - I tried using Sinatra but it wasn't playing nice with Daemons and logging
- Auth key/password - At the moment anyone with network access to the port can trigger a run, not a massive deal as all they can do is trigger a puppet run, but it might be nice to add a configurable key/phrase/password
- Add support for Windows - It should be easy(-ish) to configure, I tried to write everything in an agnostic manner, but need to test and fix
- Cache run directory - It's annoying having to provide the run directory for each action