marionetta 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ coverage/*
6
+ spec/vagrant/.vagrant
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Marionetta
2
+
3
+ A small ruby library for executing remote commands on a number
4
+ of servers. Comes with puppet mastery built in.
5
+
6
+ ## Defining a group of servers
7
+
8
+ Marionetta allows you to describe and manipulate a number of
9
+ servers in parallel via SSH. First you need to define a group
10
+ of servers:
11
+
12
+ ``` ruby
13
+ require 'marionetta'
14
+
15
+ servers = Marionetta::Group.new
16
+
17
+ servers.add_server do |s|
18
+ s[:hostname] = 'ubuntu@example.com'
19
+ end
20
+
21
+ servers.add_server do |s|
22
+ s[:hostname] = 'another@host.com'
23
+ s[:ssh][:flags] = ['-i', 'keys/private.key']
24
+ end
25
+ ```
26
+
27
+ ## Looping over a group
28
+
29
+ Continuing on from our example of defining a group of servers
30
+ above, we will now iterate over the servers:
31
+
32
+ ``` ruby
33
+ servers.each_server do |s|
34
+ # Run command on each server in parallel
35
+ Marionetta::SSH.new(s).run('whoami')
36
+ end
37
+ ```
38
+
39
+ ## Playing puppet master
40
+
41
+ Instead of running a puppet master server you can use
42
+ Marionetta to orchestrate a number instances puppet manifests.
43
+
44
+ ``` ruby
45
+ require 'marionetta'
46
+
47
+ servers = Marionetta::Group.new
48
+
49
+ servers.add_server |s|
50
+ s[:hostname] = 'ubuntu@example.com'
51
+ s[:puppet][:manifest] = 'puppet/manifest.pp'
52
+ s[:puppet][:modules] = 'puppet/modules'
53
+ end
54
+
55
+ # Install and update puppet on each server according to
56
+ # each servers puppet settings
57
+ servers.manipulate_each_server(:puppet, :update)
58
+ ```
59
+
60
+ ## Using Marionetta in your Rakefile
61
+
62
+ Marionetta provides an easy mechanism to generate rake tasks
63
+ for each of your groups.
64
+
65
+ In your Rakefile you can do something like so:
66
+
67
+ ``` ruby
68
+ require 'marionetta/rake_helper'
69
+
70
+ staging = Marionetta::Group.new(:staging)
71
+
72
+ staging.add_server |s|
73
+ s[:hostname] = 'staging.example.com'
74
+ s[:puppet][:manifest] = 'puppet/manifest.pp'
75
+ end
76
+
77
+ Marionetta::RakeHelper.new.install_group_tasks(staging)
78
+ ```
79
+
80
+ The tasks `staging:puppet:install` and `staging:puppet:update`
81
+ will be installed.
82
+
83
+ ## Author
84
+
85
+ Luke Morton a.k.a. DrPheltRight
86
+
87
+ ## License
88
+
89
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,54 @@
1
+ module Marionetta
2
+ class Group
3
+ attr_reader :name, :groups
4
+
5
+ def initialize(name = nil)
6
+ @name = name
7
+ @groups = []
8
+ @servers = []
9
+ end
10
+
11
+ def add_group(group)
12
+ @groups << group
13
+ end
14
+
15
+ def add_server()
16
+ server = Marionetta.default_server
17
+ yield server
18
+ @servers << server
19
+ end
20
+
21
+ def add_servers(range)
22
+ range.each do |i|
23
+ server = Marionetta.default_server
24
+ yield server, i
25
+ @servers << server
26
+ end
27
+ end
28
+
29
+ def servers()
30
+ servers = @servers
31
+
32
+ groups.each do |g|
33
+ servers.concat(g.servers)
34
+ end
35
+
36
+ return servers
37
+ end
38
+
39
+ def each_server()
40
+ servers.each do |s|
41
+ UnitOfWork.new.async.work do
42
+ yield s
43
+ end
44
+ end
45
+ end
46
+
47
+ def manipulate_each_server(manipulator_name, method_name)
48
+ each_server do |s|
49
+ manipulator = Manipulators[manipulator_name].new(s)
50
+ manipulator.method(method_name).call()
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,103 @@
1
+ module Marionetta
2
+ module Manipulators
3
+ class PuppetManipulator
4
+ def self.tasks()
5
+ [:install, :update]
6
+ end
7
+
8
+ attr_reader :server
9
+
10
+ def initialize(server)
11
+ @server = server
12
+ end
13
+
14
+ def ssh()
15
+ @ssh ||= SSH.new(server)
16
+ end
17
+
18
+ def install_deb_repo()
19
+ deb_file = 'puppetlabs-release-stable.deb'
20
+
21
+ repo_install_cmd = [
22
+ "wget http://apt.puppetlabs.com/#{deb_file}",
23
+ "sudo dpkg -i #{deb_file}",
24
+ "rm #{deb_file}",
25
+ ].join(' && ')
26
+
27
+ repo_check_cmd = "test -f /etc/apt/sources.list.d/puppetlabs.list"
28
+
29
+ ssh.run("#{repo_check_cmd} || { #{repo_install_cmd}; }")
30
+ end
31
+
32
+ def install_deb()
33
+ install_cmd = [
34
+ 'sudo aptitude update',
35
+ 'sudo aptitude install -y puppet'
36
+ ].join(' && ')
37
+
38
+ ssh.run("which puppet || { #{install_cmd}; }")
39
+ end
40
+
41
+ def install()
42
+ install_deb_repo
43
+ install_deb
44
+ end
45
+
46
+ def installed?()
47
+ ssh.run('which puppet')
48
+ end
49
+
50
+ def archive_files()
51
+ cmds = [
52
+ 'rm -rf /tmp/puppet',
53
+ 'mkdir /tmp/puppet',
54
+ "cp #{server[:puppet][:manifest]} /tmp/puppet/manifest.pp",
55
+ ]
56
+
57
+ if server[:puppet].has_key?(:modules)
58
+ cmds << "cp -r #{server[:puppet][:modules]} /tmp/puppet/modules"
59
+ end
60
+
61
+ cmds << 'cd /tmp'
62
+ cmds << 'tar cvfz puppet.tar.gz puppet'
63
+
64
+ system(cmds.join(' && '))
65
+ end
66
+
67
+ def send_archive()
68
+ ssh.rsync('/tmp/puppet.tar.gz', "#{server[:hostname]}:/tmp")
69
+ end
70
+
71
+ def apply_archive()
72
+ cmds = [
73
+ 'cd /tmp',
74
+ 'tar xvfz puppet.tar.gz',
75
+ 'cd puppet',
76
+ ]
77
+
78
+ puppet_cmd = 'sudo puppet apply '
79
+
80
+ if server[:puppet].has_key?(:modules)
81
+ puppet_cmd += '--modulepath=/tmp/puppet/modules '
82
+ end
83
+
84
+ puppet_cmd += 'manifest.pp'
85
+
86
+ if server[:puppet].has_key?(:options)
87
+ puppet_cmd += " #{server[:puppet][:options]}"
88
+ end
89
+
90
+ cmds << puppet_cmd
91
+
92
+ ssh.run(cmds.join(' && '))
93
+ end
94
+
95
+ def update()
96
+ install unless installed?
97
+ archive_files
98
+ send_archive
99
+ apply_archive
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,15 @@
1
+ module Marionetta
2
+ module Manipulators
3
+ require_relative 'manipulators/puppet_manipulator'
4
+
5
+ def self.all()
6
+ {
7
+ :puppet => PuppetManipulator,
8
+ }
9
+ end
10
+
11
+ def self.[](key)
12
+ all[key]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'marionetta'
2
+ require 'rake'
3
+
4
+ module Marionetta
5
+ class RakeHelper
6
+ include ::Rake::DSL if defined?(::Rake::DSL)
7
+
8
+ def install_group_tasks(group)
9
+ Manipulators.all.each do |manipulator_name, Manipulator|
10
+ Manipulator.tasks.each do |method_name|
11
+ task(task_name(group, manipulator_name, method_name)) do
12
+ group.manipulate_each_server(manipulator_name, method_name)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def task_name(group, manipulator_name, method_name)
21
+ task_name_parts = [manipulator_name, method_name]
22
+
23
+ if group.name
24
+ task_name_parts.unshift(group.name)
25
+ end
26
+
27
+ return task_name_parts.join(':')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ module Marionetta
2
+ class SSH
3
+ attr_reader :server
4
+
5
+ def initialize(server)
6
+ @server = server
7
+ end
8
+
9
+ def get(local_dir, file)
10
+ rsync("#{server[:hostname]}", local_dir)
11
+ end
12
+
13
+ def put(remote_path, base_name = File.basename(remote_path))
14
+ require 'tempfile'
15
+
16
+ Tempfile.open(base_name) do |fp|
17
+ fp.puts yield
18
+ fp.flush
19
+ rsync(fp.path, "#{server[:hostname]}:#{remote_path}")
20
+ end
21
+ end
22
+
23
+ def rsync(from, to)
24
+ rsync_cmd = [server[:rsync][:command]]
25
+
26
+ if server[:rsync].has_key?(:flags)
27
+ rsync_cmd << server[:rsync][:flags]
28
+ end
29
+
30
+ rsync_cmd << [from, to]
31
+
32
+ system(*rsync_cmd.flatten)
33
+ end
34
+
35
+ def run(command)
36
+ ssh_cmd = [server[:ssh][:command]]
37
+
38
+ if server[:ssh].has_key?(:flags)
39
+ ssh_cmd << server[:ssh][:flags]
40
+ end
41
+
42
+ ssh_cmd << server[:hostname]
43
+ ssh_cmd << command
44
+
45
+ system(*ssh_cmd.flatten)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ require 'celluloid'
2
+
3
+ module Marionetta
4
+ class UnitOfWork
5
+ include Celluloid
6
+
7
+ def work()
8
+ yield
9
+ end
10
+ end
11
+ end
data/lib/marionetta.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Marionetta
2
+ VERSION = '0.1.0'
3
+ DESCRIPTION = 'For lightweight puppet mastery. Organise
4
+ multiple machines via rsync and SSH rather
5
+ than using puppet master'
6
+
7
+ require_relative 'marionetta/ssh'
8
+ require_relative 'marionetta/manipulators'
9
+ require_relative 'marionetta/unit_of_work'
10
+ require_relative 'marionetta/group'
11
+
12
+ def self.default_server()
13
+ {
14
+ :ssh => {
15
+ :command => 'ssh',
16
+ :flags => [],
17
+ },
18
+ :rsync => {
19
+ :command => 'rsync',
20
+ :flags => ["-azP", "--delete"],
21
+ },
22
+ }
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__)+'/lib/marionetta'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "marionetta"
5
+ s.version = Marionetta::VERSION
6
+ s.homepage = 'https://github.com/DrPheltRight/marionetta'
7
+ s.authors = ["Luke Morton"]
8
+ s.email = ["lukemorton.dev@gmail.com"]
9
+ s.summary = Marionetta::DESCRIPTION
10
+ s.description = Marionetta::DESCRIPTION
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- spec/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map {|f| File.basename(f)}
15
+
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_dependency('celluloid')
19
+
20
+ s.add_development_dependency('rake')
21
+ s.add_development_dependency('rspec')
22
+ s.add_development_dependency('vagrant')
23
+ end
@@ -0,0 +1,46 @@
1
+ require 'marionetta'
2
+ require 'marionetta/group'
3
+
4
+ describe Marionetta::Group do
5
+ it 'should add server map' do
6
+ vagrant = Marionetta::Group.new
7
+
8
+ vagrant.add_server do |s|
9
+ s[:hostname] = 'vagrant@192.168.33.11'
10
+ s[:puppet] = {:manifest => File.dirname(__FILE__)+'/puppet/manifest.pp'}
11
+ end
12
+ end
13
+
14
+ it 'should add multiple server maps at once' do
15
+ production = Marionetta::Group.new
16
+
17
+ production.add_servers (1..2) do |s, i|
18
+ s[:hostname] = "vagrant@192.168.33.11"
19
+ s[:puppet] = {:manifest => File.dirname(__FILE__)+'/puppet/manifest.pp'}
20
+ end
21
+
22
+ production.servers.count.should == 2
23
+ end
24
+
25
+ it 'should add sub groups' do
26
+ staging = Marionetta::Group.new
27
+
28
+ staging.add_server do |s|
29
+ s[:hostname] = 'vagrant@192.168.33.11'
30
+ s[:puppet] = {:manifest => File.dirname(__FILE__)+'/puppet/manifest.pp'}
31
+ end
32
+
33
+ all = Marionetta::Group.new
34
+ all.add_group(staging)
35
+ end
36
+
37
+ it 'should iterate over all servers' do
38
+ end
39
+
40
+ it 'should iterate over all servers including those of sub groups' do
41
+ end
42
+
43
+ it 'should manipulate each server' do
44
+ # vagrant.manipulate_each_server(:puppet, :update)
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ require 'marionetta/manipulators'
2
+
3
+ describe Marionetta::Manipulators do
4
+ it 'should maintain a list of manipulators' do
5
+ Marionetta::Manipulators.all
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ require 'marionetta/manipulators'
2
+
3
+ describe Marionetta::Manipulators::PuppetManipulator do
4
+ it 'should manipulate one server map' do
5
+ ssh_key_path = File.dirname(__FILE__)+'/vagrant/key'
6
+
7
+ server = Marionetta.default_server
8
+ server[:hostname] = 'vagrant@192.168.33.11'
9
+ server[:ssh][:flags] = ['-i', ssh_key_path]
10
+ server[:rsync][:flags] = ['-azP', '-e', "ssh -i #{ssh_key_path}", '--delete']
11
+ server[:puppet] = {
12
+ :manifest => File.dirname(__FILE__)+'/puppet/manifest.pp',
13
+ }
14
+
15
+ Marionetta::Manipulators::PuppetManipulator.new(server).update
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'marionetta'
2
+ require 'vagrant'
3
+
4
+ env = Vagrant::Environment.new(:cwd => File.dirname(__FILE__)+'/vagrant')
5
+ env.cli('up')
6
+
7
+ describe Marionetta do
8
+ it 'should provide a default SSH map' do
9
+ Marionetta.default_server
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ file { '/etc/motd':
2
+ content => 'Hello sooty',
3
+ }
@@ -0,0 +1,6 @@
1
+ Vagrant::Config.run do |config|
2
+ config.vm.box = "precise64"
3
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
4
+
5
+ config.vm.network(:hostonly, "192.168.33.11")
6
+ end
data/spec/vagrant/key ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
3
+ w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
4
+ kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
5
+ hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
6
+ Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
7
+ yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
8
+ ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
9
+ Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
10
+ TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
11
+ iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
12
+ sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
13
+ 4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
14
+ cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
15
+ EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
16
+ CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
17
+ 3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
18
+ YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
19
+ 3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
20
+ dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
21
+ 6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
22
+ P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
23
+ llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
24
+ kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
25
+ +vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
26
+ NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
27
+ -----END RSA PRIVATE KEY-----
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marionetta
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Luke Morton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: celluloid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: vagrant
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: ! "For lightweight puppet mastery. Organise\n multiple
79
+ machines via rsync and SSH rather\n than using puppet master"
80
+ email:
81
+ - lukemorton.dev@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - README.md
89
+ - Rakefile
90
+ - lib/marionetta.rb
91
+ - lib/marionetta/group.rb
92
+ - lib/marionetta/manipulators.rb
93
+ - lib/marionetta/manipulators/puppet_manipulator.rb
94
+ - lib/marionetta/rake_helper.rb
95
+ - lib/marionetta/ssh.rb
96
+ - lib/marionetta/unit_of_work.rb
97
+ - marionetta.gemspec
98
+ - spec/marionetta_group_spec.rb
99
+ - spec/marionetta_manipulators_spec.rb
100
+ - spec/marionetta_puppet_manipulator_spec.rb
101
+ - spec/marionetta_spec.rb
102
+ - spec/puppet/manifest.pp
103
+ - spec/vagrant/Vagrantfile
104
+ - spec/vagrant/key
105
+ homepage: https://github.com/DrPheltRight/marionetta
106
+ licenses: []
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.23
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: For lightweight puppet mastery. Organise multiple machines via rsync and
129
+ SSH rather than using puppet master
130
+ test_files:
131
+ - spec/marionetta_group_spec.rb
132
+ - spec/marionetta_manipulators_spec.rb
133
+ - spec/marionetta_puppet_manipulator_spec.rb
134
+ - spec/marionetta_spec.rb
135
+ - spec/puppet/manifest.pp
136
+ - spec/vagrant/Vagrantfile
137
+ - spec/vagrant/key