puppet-twitch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'puppet'
7
+ gem 'rake'
8
+ end
9
+
10
+ group :test do
11
+ gem 'rspec', '~> 3.2.0'
12
+ end
@@ -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.
@@ -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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'puppet_twitch/server'
@@ -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,3 @@
1
+ module PuppetTwitch
2
+ VERSION = '0.1.0'
3
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'rspec'
2
+
3
+ RSpec.configure do |c|
4
+ c.color = true
5
+ end
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