puppet-twitch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +102 -0
- data/bin/puppet-twitch +4 -0
- data/lib/puppet_twitch/basic_http_server.rb +85 -0
- data/lib/puppet_twitch/controller.rb +52 -0
- data/lib/puppet_twitch/logger.rb +43 -0
- data/lib/puppet_twitch/param_parser.rb +27 -0
- data/lib/puppet_twitch/puppet.rb +30 -0
- data/lib/puppet_twitch/server.rb +16 -0
- data/lib/puppet_twitch/version.rb +3 -0
- data/puppet-twitch.gemspec +24 -0
- data/spec/puppet_twitch/basic_http_server_spec.rb +25 -0
- data/spec/puppet_twitch/param_parser_spec.rb +44 -0
- data/spec/spec_helper.rb +5 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MmY4YzJlMTY4ZTZkYjAwOTRmNDdiMzdhNGE2Yjc5NGNiZDNhYWY2Zg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzQ3NDFlYThmYzNkMmMwZTNlMDE2ZjhiNjVlY2M4NzUyM2Y5NGQ5YQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ODQzNzQ0ODEzNGU5NWZhNzYxNGM1NjAwZTE2ZmUzYjNiNGM0MDA0OWIwNTEz
|
10
|
+
N2Q2YjgzMTY2ZWMwZDQ4NDM4ZjBkMjM5N2E2ZWE2MWVjZmM2YjY2MjdhNDJh
|
11
|
+
Mzk5ZTFkYWU2YWQ4MGNhYTkyZDk5MmM2MjhhNjA2ZWU4ZTRjOTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Mzc5MDVjMmYwOWYxNmUwMjMwY2Q5NTAyOWM3ODZmMzRmYTI1MzVkNThiODlj
|
14
|
+
NGQyMjYwZjAzM2M4ZWVmMWJkY2E3MWZjNGZmZWNhMzkwMDdlNTQ4Yzk0NDdl
|
15
|
+
YWEwOGRkMDE1YzhkODkwOGRiMjczMWRlYzg1MzJjNDlmOTMyMzk=
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 TomPoulton
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Puppet Twitch
|
2
|
+
|
3
|
+
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.
|
4
|
+
|
5
|
+
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)
|
6
|
+
|
7
|
+
### Triggering puppet
|
8
|
+
|
9
|
+
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:
|
10
|
+
|
11
|
+
`$ curl http://node01.example.com:2023/puppet/twitch`
|
12
|
+
|
13
|
+
Puppet-twitch will respond with one of the following (`<status code>, <body>`):
|
14
|
+
- `202, Triggered puppet run` - The puppet run has been triggered asynchronously
|
15
|
+
- `409, Puppet already running` - Either the server is already processing a request, or puppet's lock file exists indicating the agent is already running
|
16
|
+
- `500, <error message>` - Something went wrong!
|
17
|
+
|
18
|
+
##### Async
|
19
|
+
|
20
|
+
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:
|
21
|
+
|
22
|
+
`$ curl http://node01.example.com:2023/puppet/twitch?async=false`
|
23
|
+
|
24
|
+
In this case puppet-twitch will respond with `200, Puppet run complete`
|
25
|
+
|
26
|
+
### Puppet Install
|
27
|
+
|
28
|
+
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.
|
29
|
+
|
30
|
+
To use the defaults on Linux, simply `include twitch`. This will:
|
31
|
+
- Create a user and group called `twitch`
|
32
|
+
- Add specific puppet commands to `/etc/sudoers.d/twitch` (see `init.pp` in the `twitch` module for the list of commands)
|
33
|
+
- Create the `/var/run/puppet-twitch` directory that is writable by the `twitch` user
|
34
|
+
- Install the gem
|
35
|
+
- Start the server as the `twitch` user on the default port (2023)
|
36
|
+
|
37
|
+
The `twitch` class is parameterized, so user, port, run directory, etc can be configured if necessary
|
38
|
+
|
39
|
+
##### Prerequisites
|
40
|
+
- **ruby 1.9.3** or above as `require_relative` isn't supported by lower versions
|
41
|
+
- Open the port to incoming connections, the `twitch` module will not change any network configuration.
|
42
|
+
- Add `/etc/sudoers.d` to the `sudoers` file. This is a common configuration and is done by default on some VMs (e.g.Vagrant boxes, EC2 instances) but the `twitch` module assumes this is already setup.
|
43
|
+
|
44
|
+
### Manual Install:
|
45
|
+
|
46
|
+
`$ gem install puppet-twitch` (run as root so that it's available to all users)
|
47
|
+
|
48
|
+
Use the `puppet-twitch` command to start/stop the server
|
49
|
+
|
50
|
+
`$ puppet-twitch {start|stop|restart|status} dir=</path/to/run/dir> [-- [bind=<binding>] [port=<port>]]`
|
51
|
+
|
52
|
+
For example to start the server on a custom port
|
53
|
+
|
54
|
+
`$ puppet-twitch start dir=/var/run/puppet-twitch -- port=8090`
|
55
|
+
|
56
|
+
And then stop it
|
57
|
+
|
58
|
+
`$ puppet-twitch stop dir=/var/run/puppet-twitch`
|
59
|
+
|
60
|
+
##### Prerequisites
|
61
|
+
- All prerequisites from the puppet install apply
|
62
|
+
- Create a user to run the server (one that doesn't have root permissions)
|
63
|
+
- Allow user to run the necessary puppet commands with `sudo` and without password (see `init.pp` for commands)
|
64
|
+
- Create a run directory for pids and logs (writable by the user)
|
65
|
+
|
66
|
+
##### Args:
|
67
|
+
- `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 this
|
68
|
+
- `bind`: The IP address to bind to, defaults to `0.0.0.0`
|
69
|
+
- `port`: The port to listen on, defaults to `2023`
|
70
|
+
|
71
|
+
### Running from source (for development/testing)
|
72
|
+
|
73
|
+
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:
|
74
|
+
- `ruby lib/puppet-twitch/controller.rb [bind=<binding>] [port=<port>]`
|
75
|
+
- `ruby lib/puppet-twitch/server.rb dir=</path/to/run/dir> [-- [bind=<binding>] [port=<port>]]`
|
76
|
+
|
77
|
+
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
|
78
|
+
|
79
|
+
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.
|
80
|
+
|
81
|
+
##### Vagrant
|
82
|
+
|
83
|
+
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:
|
84
|
+
|
85
|
+
```
|
86
|
+
[user@localhost] $ vagrant up
|
87
|
+
[user@localhost] $ vagrant ssh node01
|
88
|
+
[vagrant@node01] $ sudo puppet agent -t
|
89
|
+
[vagrant@node01] $ curl http://localhost:2023/puppet/twitch
|
90
|
+
```
|
91
|
+
|
92
|
+
The puppet class that's applied to the nodes (`twitch::test::vagrant`) inherits from the normal `twitch` class but also:
|
93
|
+
- Gives the `twitch` user a password (set to `twitch`) and a shell (`/bin/bash`) so you can switch to the `twitch` user for debugging
|
94
|
+
- Installs the gem from the project's `pkg` directory that is mounted by vagrant, rather than downloading the gem from rubygems
|
95
|
+
|
96
|
+
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
|
97
|
+
|
98
|
+
### ToDo:
|
99
|
+
- Proper http server - I tried using Sinatra but it wasn't playing nice with Daemons and logging
|
100
|
+
- 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
|
101
|
+
- 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
|
102
|
+
- Cache run directory - It's annoying having to provide the run directory for each action
|
data/bin/puppet-twitch
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require_relative 'logger'
|
3
|
+
require_relative 'param_parser'
|
4
|
+
|
5
|
+
module PuppetTwitch
|
6
|
+
|
7
|
+
class BasicHttpServer
|
8
|
+
|
9
|
+
attr_accessor :logger
|
10
|
+
|
11
|
+
def initialize(bind, port, multiple_threads = true)
|
12
|
+
@bind = bind
|
13
|
+
@port = port
|
14
|
+
@multiple_threads = multiple_threads
|
15
|
+
@logger = PuppetTwitch::Logger.new()
|
16
|
+
@logger.level = PuppetTwitch::Logger::DEBUG
|
17
|
+
@actions = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def start()
|
21
|
+
@logger.info 'Starting server ...'
|
22
|
+
server = TCPServer.new(@bind, @port)
|
23
|
+
@logger.info "Listening on #{@bind}:#{@port}"
|
24
|
+
|
25
|
+
server_loop = proc { |socket|
|
26
|
+
begin
|
27
|
+
process_request socket
|
28
|
+
rescue => e
|
29
|
+
@logger.error e.to_s
|
30
|
+
socket.puts form_response(500, 'Unexpected error')
|
31
|
+
ensure
|
32
|
+
socket.close
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
loop do
|
37
|
+
if @multiple_threads
|
38
|
+
Thread.fork(server.accept, &server_loop)
|
39
|
+
else
|
40
|
+
server_loop.call server.accept
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_request(socket)
|
46
|
+
client_ip = socket.peeraddr[3]
|
47
|
+
client_hostname = socket.peeraddr[2]
|
48
|
+
request = socket.gets
|
49
|
+
|
50
|
+
endpoint, params = get_endpoint_and_params(request)
|
51
|
+
@logger.info "Connection from: #{client_hostname} (#{client_ip}) | Endpoint: #{endpoint} | Params: #{params}"
|
52
|
+
|
53
|
+
response = @actions.has_key?(endpoint) ? @actions[endpoint].call(params) : [400, "Unrecognised endpoint: #{endpoint}"]
|
54
|
+
@logger.debug "#{response[0]}: #{response[1]}"
|
55
|
+
|
56
|
+
socket.puts form_response(response[0], response[1])
|
57
|
+
@logger.debug "Closing connection from #{client_ip}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def endpoint(endpoint, &block)
|
61
|
+
@actions[endpoint] = block
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_endpoint_and_params(request)
|
65
|
+
match = request.match(/^\s*(?<verb>GET|POST|PUT|DELETE) (?<endpoint>[\w\-\/]*)(\?(?<params>\S*)\s*|\s*)HTTP.*/)
|
66
|
+
return match['endpoint'], parse_params(match['params'])
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_params(param_string)
|
70
|
+
PuppetTwitch::ParamParser.parse(param_string.to_s.strip.split('&'))
|
71
|
+
end
|
72
|
+
|
73
|
+
def form_response(status, body)
|
74
|
+
<<-EOF
|
75
|
+
HTTP/1.1 #{status}
|
76
|
+
Content-Type: text/plain
|
77
|
+
Content-Length: #{body.bytesize}
|
78
|
+
Connection: close
|
79
|
+
|
80
|
+
#{body}
|
81
|
+
EOF
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative 'basic_http_server'
|
4
|
+
require_relative 'param_parser'
|
5
|
+
require_relative 'puppet'
|
6
|
+
require_relative 'version'
|
7
|
+
|
8
|
+
module PuppetTwitch
|
9
|
+
|
10
|
+
args = ParamParser.parse(ARGV)
|
11
|
+
|
12
|
+
bind = args[:bind] || '0.0.0.0'
|
13
|
+
port = args[:port] || 2023
|
14
|
+
threads = true
|
15
|
+
|
16
|
+
@processing_request = Mutex.new
|
17
|
+
http_server = PuppetTwitch::BasicHttpServer.new(bind, port, threads)
|
18
|
+
|
19
|
+
http_server.endpoint '/puppet/twitch' do |params|
|
20
|
+
response = []
|
21
|
+
if @processing_request.try_lock
|
22
|
+
begin
|
23
|
+
if PuppetTwitch::Puppet.is_running?
|
24
|
+
response = [409, 'Puppet already running']
|
25
|
+
else
|
26
|
+
async = (params[:async].to_s != 'false')
|
27
|
+
PuppetTwitch::Puppet.run_puppet(async)
|
28
|
+
response = async ? [202, 'Triggered puppet run'] : [200, 'Puppet run complete']
|
29
|
+
end
|
30
|
+
rescue => e
|
31
|
+
http_server.logger.error e.to_s
|
32
|
+
response = [500, 'Error running puppet']
|
33
|
+
ensure
|
34
|
+
@processing_request.unlock
|
35
|
+
end
|
36
|
+
else
|
37
|
+
response = [409, 'Puppet already running']
|
38
|
+
end
|
39
|
+
response
|
40
|
+
end
|
41
|
+
|
42
|
+
http_server.endpoint '/version' do
|
43
|
+
[200, PuppetTwitch::VERSION]
|
44
|
+
end
|
45
|
+
|
46
|
+
http_server.endpoint '/info' do
|
47
|
+
[200, 'Puppet Twitch - A simple endpoint for triggering puppet']
|
48
|
+
end
|
49
|
+
|
50
|
+
http_server.start
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module PuppetTwitch
|
2
|
+
|
3
|
+
# Logger just prints to STDOUT.
|
4
|
+
# When the server is running in the foreground (not Daemonized),
|
5
|
+
# all output is visible in the terminal.
|
6
|
+
# When the server is running as a Daemon,
|
7
|
+
# STDOUT is redirected to a logfile,
|
8
|
+
# so this simple logger works for both scenarios
|
9
|
+
|
10
|
+
class Logger
|
11
|
+
|
12
|
+
DEBUG = 0
|
13
|
+
INFO = 1
|
14
|
+
WARN = 2
|
15
|
+
ERROR = 3
|
16
|
+
|
17
|
+
attr_accessor :level
|
18
|
+
|
19
|
+
def initialize(level = INFO)
|
20
|
+
@level = level
|
21
|
+
end
|
22
|
+
|
23
|
+
def debug(message)
|
24
|
+
log DEBUG, "Log [DEBUG]: #{message}"
|
25
|
+
end
|
26
|
+
def info(message)
|
27
|
+
log INFO, "Log [INFO]: #{message}"
|
28
|
+
end
|
29
|
+
def warn(message)
|
30
|
+
log WARN, "Log [WARN]: #{message}"
|
31
|
+
end
|
32
|
+
def error(message)
|
33
|
+
log ERROR, "Log [ERROR]: #{message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def log(level, message)
|
39
|
+
puts message if level >= @level
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PuppetTwitch
|
2
|
+
|
3
|
+
class ParamParser
|
4
|
+
|
5
|
+
# Takes an array of parameter strings and
|
6
|
+
# returns a hash of the parameters and their values where
|
7
|
+
# the keys are the parameter names as symbols.
|
8
|
+
#
|
9
|
+
# Parameter strings can have the following formats:
|
10
|
+
# 'name=value' --> { :name => 'value' }
|
11
|
+
# 'name' --> { :name => true }
|
12
|
+
#
|
13
|
+
def self.parse(param_strings)
|
14
|
+
params = {}
|
15
|
+
param_strings.each { |pair|
|
16
|
+
key_value = pair.split('=')
|
17
|
+
if key_value.size > 2 || pair[-1] == '='
|
18
|
+
raise StandardError, "Invalid parameter format: #{pair}"
|
19
|
+
else
|
20
|
+
params[key_value[0].to_sym] = (key_value.size == 1) ? true : key_value[1]
|
21
|
+
end
|
22
|
+
}
|
23
|
+
params
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module PuppetTwitch
|
2
|
+
|
3
|
+
class Puppet
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def is_running?
|
7
|
+
lock_file_path_cmd = 'puppet config print agent_catalog_run_lockfile'
|
8
|
+
lock_file_path = `#{sudo_if_required lock_file_path_cmd}`.strip
|
9
|
+
if $?.to_i != 0
|
10
|
+
raise StandardError, "Failed to find puppet lock file path: #{lock_file_path}"
|
11
|
+
end
|
12
|
+
File.exists?(lock_file_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_puppet(async = true)
|
16
|
+
command = async ? 'puppet agent --onetime' : 'puppet agent --onetime --no-daemonize'
|
17
|
+
output = `#{sudo_if_required command}`
|
18
|
+
exit_code = $?
|
19
|
+
unless [0, 2].include?(exit_code.to_i) # 2 indicates a puppet change, 3 is a failure
|
20
|
+
raise StandardError, "Error running puppet: #{exit_code}: #{output}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def sudo_if_required(command)
|
25
|
+
"sudo #{command}" unless Gem.win_platform?
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'daemons'
|
2
|
+
require_relative 'param_parser'
|
3
|
+
|
4
|
+
args = PuppetTwitch::ParamParser.parse(ARGV.reject {|arg| arg == '--'})
|
5
|
+
run_dir = args[:dir]
|
6
|
+
abort 'Please provide a run directory' unless run_dir
|
7
|
+
|
8
|
+
# Pids and log files will be written to run_dir directory
|
9
|
+
options = {
|
10
|
+
:dir_mode => :normal,
|
11
|
+
:dir => run_dir,
|
12
|
+
:log_output => true,
|
13
|
+
:monitor => true,
|
14
|
+
}
|
15
|
+
|
16
|
+
Daemons.run(File.expand_path('../controller.rb', __FILE__), options)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'puppet_twitch/version'
|
6
|
+
|
7
|
+
excude = ['.gitignore', 'Rakefile', 'puppet/.*', 'nodes.json', 'Vagrantfile']
|
8
|
+
|
9
|
+
Gem::Specification.new do |gem|
|
10
|
+
gem.name = 'puppet-twitch'
|
11
|
+
gem.version = PuppetTwitch::VERSION
|
12
|
+
gem.description = 'Trigger a puppet run remotely'
|
13
|
+
gem.summary = 'http trigger for puppet'
|
14
|
+
gem.author = 'Tom Poulton'
|
15
|
+
gem.license = 'MIT'
|
16
|
+
gem.homepage = 'http://github.com/Accuity/puppet-twitch'
|
17
|
+
|
18
|
+
gem.files = `git ls-files`.split($/).reject { |file| file =~ /^(#{excude.join('|')})$/ }
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ['lib']
|
22
|
+
|
23
|
+
gem.add_dependency('daemons', '~> 1.2.2')
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/puppet_twitch/basic_http_server'
|
3
|
+
|
4
|
+
module PuppetTwitch
|
5
|
+
|
6
|
+
describe BasicHttpServer do
|
7
|
+
|
8
|
+
describe '#get_endpoint_and_params' do
|
9
|
+
|
10
|
+
let(:basic_http_server) { BasicHttpServer.new('0.0.0.0', 1234) }
|
11
|
+
|
12
|
+
it 'returns endpoint' do
|
13
|
+
endpoint, params = basic_http_server.get_endpoint_and_params('GET /foo HTTP')
|
14
|
+
expect(endpoint).to eq '/foo'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns endpoint containing punctuation' do
|
18
|
+
endpoint, params = basic_http_server.get_endpoint_and_params('GET /foo_bar/baz-baa HTTP')
|
19
|
+
expect(endpoint).to eq '/foo_bar/baz-baa'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/puppet_twitch/param_parser'
|
3
|
+
|
4
|
+
module PuppetTwitch
|
5
|
+
|
6
|
+
describe ParamParser do
|
7
|
+
|
8
|
+
describe '.parse' do
|
9
|
+
|
10
|
+
it 'converts param keys to symbols' do
|
11
|
+
params = ParamParser.parse ['foo=bar']
|
12
|
+
expect(params).to have_key :foo
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'parses multiple key value params' do
|
16
|
+
params = ParamParser.parse ['foo=bar', 'baz=baa']
|
17
|
+
expect(params).to eq( {:foo => 'bar', :baz => 'baa'} )
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'empty params are set to true' do
|
21
|
+
params = ParamParser.parse ['foo', 'bar']
|
22
|
+
expect(params).to eq( {:foo => true, :bar => true} )
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns empty hash for empty input array' do
|
26
|
+
params = ParamParser.parse []
|
27
|
+
expect(params).to eq( {} )
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises error when param is malformed' do
|
31
|
+
expect {
|
32
|
+
ParamParser.parse ['foo=bar=baz']
|
33
|
+
}.to raise_error StandardError, /Invalid parameter format/
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises error when param value is missing' do
|
37
|
+
expect {
|
38
|
+
ParamParser.parse ['foo=']
|
39
|
+
}.to raise_error StandardError, /Invalid parameter format/
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puppet-twitch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Poulton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: daemons
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.2.2
|
27
|
+
description: Trigger a puppet run remotely
|
28
|
+
email:
|
29
|
+
executables:
|
30
|
+
- puppet-twitch
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE.txt
|
36
|
+
- README.md
|
37
|
+
- bin/puppet-twitch
|
38
|
+
- lib/puppet_twitch/basic_http_server.rb
|
39
|
+
- lib/puppet_twitch/controller.rb
|
40
|
+
- lib/puppet_twitch/logger.rb
|
41
|
+
- lib/puppet_twitch/param_parser.rb
|
42
|
+
- lib/puppet_twitch/puppet.rb
|
43
|
+
- lib/puppet_twitch/server.rb
|
44
|
+
- lib/puppet_twitch/version.rb
|
45
|
+
- puppet-twitch.gemspec
|
46
|
+
- spec/puppet_twitch/basic_http_server_spec.rb
|
47
|
+
- spec/puppet_twitch/param_parser_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
homepage: http://github.com/Accuity/puppet-twitch
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.4.5
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: http trigger for puppet
|
73
|
+
test_files:
|
74
|
+
- spec/puppet_twitch/basic_http_server_spec.rb
|
75
|
+
- spec/puppet_twitch/param_parser_spec.rb
|
76
|
+
- spec/spec_helper.rb
|