sprinkle 0.4.2 → 0.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +54 -0
- data/README.markdown +178 -166
- data/Rakefile +4 -28
- data/bin/sprinkle +14 -1
- data/lib/sprinkle.rb +5 -1
- data/lib/sprinkle/actors/actors.rb +20 -5
- data/lib/sprinkle/actors/capistrano.rb +62 -36
- data/lib/sprinkle/actors/dummy.rb +127 -0
- data/lib/sprinkle/actors/local.rb +59 -17
- data/lib/sprinkle/actors/ssh.rb +189 -107
- data/lib/sprinkle/actors/vlad.rb +51 -32
- data/lib/sprinkle/configurable.rb +2 -1
- data/lib/sprinkle/deployment.rb +22 -2
- data/lib/sprinkle/errors/pretty_failure.rb +41 -0
- data/lib/sprinkle/errors/remote_command_failure.rb +24 -0
- data/lib/sprinkle/errors/transfer_failure.rb +28 -0
- data/lib/sprinkle/installers/apt.rb +17 -16
- data/lib/sprinkle/installers/binary.rb +23 -8
- data/lib/sprinkle/installers/brew.rb +17 -10
- data/lib/sprinkle/installers/bsd_port.rb +10 -6
- data/lib/sprinkle/installers/deb.rb +3 -10
- data/lib/sprinkle/installers/freebsd_pkg.rb +5 -11
- data/lib/sprinkle/installers/freebsd_portinstall.rb +8 -2
- data/lib/sprinkle/installers/gem.rb +9 -3
- data/lib/sprinkle/installers/group.rb +28 -4
- data/lib/sprinkle/installers/installer.rb +58 -7
- data/lib/sprinkle/installers/mac_port.rb +13 -6
- data/lib/sprinkle/installers/npm.rb +42 -0
- data/lib/sprinkle/installers/openbsd_pkg.rb +4 -11
- data/lib/sprinkle/installers/opensolaris_pkg.rb +7 -13
- data/lib/sprinkle/installers/package_installer.rb +33 -0
- data/lib/sprinkle/installers/pacman.rb +5 -13
- data/lib/sprinkle/installers/pear.rb +40 -0
- data/lib/sprinkle/installers/push_text.rb +18 -5
- data/lib/sprinkle/installers/rake.rb +7 -2
- data/lib/sprinkle/installers/reconnect.rb +29 -0
- data/lib/sprinkle/installers/replace_text.rb +11 -2
- data/lib/sprinkle/installers/rpm.rb +8 -6
- data/lib/sprinkle/installers/runner.rb +41 -16
- data/lib/sprinkle/installers/smart.rb +6 -17
- data/lib/sprinkle/installers/source.rb +22 -10
- data/lib/sprinkle/installers/thor.rb +7 -0
- data/lib/sprinkle/installers/transfer.rb +62 -41
- data/lib/sprinkle/installers/user.rb +34 -4
- data/lib/sprinkle/installers/yum.rb +10 -10
- data/lib/sprinkle/installers/zypper.rb +4 -15
- data/lib/sprinkle/package.rb +81 -98
- data/lib/sprinkle/policy.rb +11 -4
- data/lib/sprinkle/utility/log_recorder.rb +33 -0
- data/lib/sprinkle/verifiers/directory.rb +1 -1
- data/lib/sprinkle/verifiers/executable.rb +1 -1
- data/lib/sprinkle/verifiers/file.rb +11 -2
- data/lib/sprinkle/verifiers/package.rb +2 -14
- data/lib/sprinkle/verifiers/permission.rb +40 -0
- data/lib/sprinkle/verifiers/symlink.rb +2 -2
- data/lib/sprinkle/verifiers/test.rb +21 -0
- data/lib/sprinkle/verify.rb +3 -3
- data/lib/sprinkle/version.rb +3 -0
- data/spec/fixtures/my_file.txt +1 -0
- data/spec/sprinkle/actors/capistrano_spec.rb +16 -3
- data/spec/sprinkle/actors/local_spec.rb +24 -6
- data/spec/sprinkle/actors/ssh_spec.rb +38 -0
- data/spec/sprinkle/installers/apt_spec.rb +23 -2
- data/spec/sprinkle/installers/binary_spec.rb +22 -14
- data/spec/sprinkle/installers/brew_spec.rb +4 -4
- data/spec/sprinkle/installers/installer_spec.rb +36 -7
- data/spec/sprinkle/installers/npm_spec.rb +16 -0
- data/spec/sprinkle/installers/pear_spec.rb +16 -0
- data/spec/sprinkle/installers/push_text_spec.rb +23 -1
- data/spec/sprinkle/installers/rpm_spec.rb +5 -0
- data/spec/sprinkle/installers/runner_spec.rb +27 -11
- data/spec/sprinkle/installers/smart_spec.rb +60 -0
- data/spec/sprinkle/installers/source_spec.rb +4 -4
- data/spec/sprinkle/installers/transfer_spec.rb +31 -16
- data/spec/sprinkle/package_spec.rb +10 -2
- data/spec/sprinkle/policy_spec.rb +6 -0
- data/spec/sprinkle/verify_spec.rb +18 -4
- data/sprinkle.gemspec +22 -158
- metadata +178 -96
- data/TODO +0 -56
- data/VERSION +0 -1
- data/lib/sprinkle/verifiers/apt.rb +0 -21
- data/lib/sprinkle/verifiers/brew.rb +0 -21
- data/lib/sprinkle/verifiers/rpm.rb +0 -21
- data/lib/sprinkle/verifiers/users_groups.rb +0 -33
@@ -1,4 +1,8 @@
|
|
1
1
|
module Sprinkle
|
2
|
+
# Installers are where the bulk of the work in Sprinkle happens. Installers are
|
3
|
+
# the building blocks of packages. Typically each unique type of install
|
4
|
+
# command has it's own installer class.
|
5
|
+
#
|
2
6
|
module Installers
|
3
7
|
# The base class which all installers must subclass, this class makes
|
4
8
|
# sure all installers share some general features, which are outlined
|
@@ -8,9 +12,16 @@ module Sprinkle
|
|
8
12
|
#
|
9
13
|
# With all installation methods you have the ability to specify multiple
|
10
14
|
# pre/post installation hooks. This gives you the ability to specify
|
11
|
-
# commands to run before and after an installation takes place.
|
12
|
-
#
|
13
|
-
|
15
|
+
# commands to run before and after an installation takes place.
|
16
|
+
# There are three ways to specify a pre/post hook.
|
17
|
+
|
18
|
+
# Note about sudo:
|
19
|
+
# When using the Capistrano actor all commands by default are run using
|
20
|
+
# sudo (unless your Capfile includes "set :use_sudo, false"). If you wish
|
21
|
+
# to use sudo periodically with "set :user_sudo, false" or with an actor
|
22
|
+
# other than Capistrano then you can just append it to your command. Some
|
23
|
+
# installers (transfer) also support a :sudo option, so check each
|
24
|
+
# installer for details.
|
14
25
|
#
|
15
26
|
# First, a single command:
|
16
27
|
#
|
@@ -45,11 +56,37 @@ module Sprinkle
|
|
45
56
|
|
46
57
|
def initialize(package, options = {}, &block) #:nodoc:
|
47
58
|
@package = package
|
48
|
-
@options = options
|
59
|
+
@options = options || {}
|
49
60
|
@pre = {}; @post = {}
|
50
61
|
self.instance_eval(&block) if block
|
51
62
|
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
def subclasses
|
66
|
+
@subclasses ||= []
|
67
|
+
end
|
68
|
+
|
69
|
+
def api(&block)
|
70
|
+
Sprinkle::Package::Package.class_eval &block
|
71
|
+
end
|
72
|
+
|
73
|
+
def verify_api(&block)
|
74
|
+
Sprinkle::Verify.class_eval &block
|
75
|
+
end
|
52
76
|
|
77
|
+
def inherited(base)
|
78
|
+
subclasses << base
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def sudo_cmd
|
83
|
+
"sudo " if sudo?
|
84
|
+
end
|
85
|
+
|
86
|
+
def sudo?
|
87
|
+
options[:sudo] or package.sudo?
|
88
|
+
end
|
89
|
+
|
53
90
|
def pre(stage, *commands)
|
54
91
|
@pre[stage] ||= []
|
55
92
|
@pre[stage] += commands
|
@@ -61,6 +98,15 @@ module Sprinkle
|
|
61
98
|
@post[stage] += commands
|
62
99
|
@post[stage] += [yield] if block_given?
|
63
100
|
end
|
101
|
+
|
102
|
+
def per_host?
|
103
|
+
return false
|
104
|
+
@per_host
|
105
|
+
end
|
106
|
+
|
107
|
+
# Called right before an installer is exected, can be used for logging
|
108
|
+
# and announcing what is about to happen
|
109
|
+
def announce; end
|
64
110
|
|
65
111
|
def process(roles) #:nodoc:
|
66
112
|
assert_delivery
|
@@ -71,12 +117,11 @@ module Sprinkle
|
|
71
117
|
end
|
72
118
|
|
73
119
|
unless Sprinkle::OPTIONS[:testing]
|
74
|
-
logger.
|
75
|
-
@delivery.
|
120
|
+
logger.debug " --> Running #{self.class.name} for roles: #{roles}"
|
121
|
+
@delivery.install(self, roles, :per_host => per_host?)
|
76
122
|
end
|
77
123
|
end
|
78
124
|
|
79
|
-
protected
|
80
125
|
# More complicated installers that have different stages, and require pre/post commands
|
81
126
|
# within stages can override install_sequence and take complete control of the install
|
82
127
|
# command sequence construction (eg. source based installer).
|
@@ -84,6 +129,12 @@ module Sprinkle
|
|
84
129
|
commands = pre_commands(:install) + [ install_commands ] + post_commands(:install)
|
85
130
|
commands.flatten
|
86
131
|
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
def log(t, level=:info)
|
136
|
+
logger.send(level, t)
|
137
|
+
end
|
87
138
|
|
88
139
|
# A concrete installer (subclass of this virtual class) must override this method
|
89
140
|
# and return the commands it needs to run as either a string or an array.
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Sprinkle
|
2
2
|
module Installers
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# The Port installer installs macports ports.
|
3
|
+
# The MacPort installer installs macports ports.
|
6
4
|
#
|
7
5
|
# == Example Usage
|
8
6
|
#
|
@@ -13,17 +11,26 @@ module Sprinkle
|
|
13
11
|
# end
|
14
12
|
#
|
15
13
|
# == Notes
|
14
|
+
#
|
16
15
|
# Before MacPorts packages can be installed, the PATH
|
17
16
|
# environment variable probably has to be changed so
|
18
|
-
#
|
17
|
+
# Sprinkle can find the /opt/local/bin/port executable
|
19
18
|
#
|
20
19
|
# You must set PATH in ~/.ssh/environment on the remote
|
21
20
|
# system and enable 'PermitUserEnvironment yes' in /etc/sshd_config
|
21
|
+
#
|
22
22
|
class MacPort < Installer
|
23
|
+
|
24
|
+
api do
|
25
|
+
def mac_port(port, options={}, &block)
|
26
|
+
install Sprinkle::Installers::MacPort.new(self, port, options, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
23
30
|
attr_accessor :port #:nodoc:
|
24
31
|
|
25
|
-
def initialize(parent, port, &block) #:nodoc:
|
26
|
-
super parent, &block
|
32
|
+
def initialize(parent, port, options = {}, &block) #:nodoc:
|
33
|
+
super parent, options, &block
|
27
34
|
@port = port
|
28
35
|
end
|
29
36
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# = Npm package Installed
|
4
|
+
#
|
5
|
+
# Installs an npm module
|
6
|
+
#
|
7
|
+
# == Example Usage
|
8
|
+
#
|
9
|
+
# package :magic_beans do
|
10
|
+
# npm 'grunt'
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# verify { has_npm 'grunt' }
|
14
|
+
class Npm < Installer
|
15
|
+
|
16
|
+
attr_accessor :package_name
|
17
|
+
|
18
|
+
api do
|
19
|
+
def npm(package, &block)
|
20
|
+
install Sprinkle::Installers::Npm.new(self, package, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
verify_api do
|
25
|
+
def has_npm(package)
|
26
|
+
@commands << "npm --global list | grep \"#{package}@\""
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(parent, package_name, &block) #:nodoc:
|
31
|
+
super parent, &block
|
32
|
+
@package_name = package_name
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def install_commands #:nodoc:
|
37
|
+
"npm install --global #{@package_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Sprinkle
|
2
2
|
module Installers
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# The Pkg package installer installs OpenBSD packages.
|
3
|
+
# The OpenBSD package installer installs OpenBSD packages.
|
6
4
|
#
|
7
5
|
# == Example Usage
|
8
6
|
#
|
@@ -27,14 +25,9 @@ module Sprinkle
|
|
27
25
|
#
|
28
26
|
# For help on PKG_PATH see section 15.2.2 of the OpenBSD FAQ
|
29
27
|
# (http://www.openbsd.org/faq/faq15.html)
|
30
|
-
class OpenbsdPkg <
|
31
|
-
|
32
|
-
|
33
|
-
def initialize(parent, packages, &block) #:nodoc:
|
34
|
-
super parent, &block
|
35
|
-
packages = [packages] unless packages.is_a? Array
|
36
|
-
@packages = packages
|
37
|
-
end
|
28
|
+
class OpenbsdPkg < PackageInstaller
|
29
|
+
|
30
|
+
auto_api
|
38
31
|
|
39
32
|
protected
|
40
33
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Sprinkle
|
2
2
|
module Installers
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# The Pkg package installer installs OpenSolaris packages.
|
3
|
+
# The OpenSolaris package installer installs OpenSolaris packages.
|
6
4
|
#
|
7
5
|
# == Example Usage
|
8
6
|
#
|
@@ -21,16 +19,12 @@ module Sprinkle
|
|
21
19
|
# == Note
|
22
20
|
# If you are using capistrano as the deployment method
|
23
21
|
# you will need to add the following lines to your deploy.rb
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
super parent, &block
|
31
|
-
packages = [packages] unless packages.is_a? Array
|
32
|
-
@packages = packages
|
33
|
-
end
|
22
|
+
#
|
23
|
+
# set :sudo, 'pfexec'
|
24
|
+
# set :sudo_prompt, ''
|
25
|
+
class OpensolarisPkg < PackageInstaller
|
26
|
+
|
27
|
+
auto_api
|
34
28
|
|
35
29
|
protected
|
36
30
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# This is a abstract class installer that most all the package installers
|
4
|
+
# inherit from (deb, *BSD pkg, rpm, etc)
|
5
|
+
class PackageInstaller < Installer
|
6
|
+
|
7
|
+
attr_accessor :packages #:nodoc:
|
8
|
+
|
9
|
+
def initialize(parent, *packages, &block) #:nodoc:
|
10
|
+
options = packages.extract_options!
|
11
|
+
super parent, options, &block
|
12
|
+
@packages = [*packages].flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.auto_api
|
16
|
+
method_name = self.to_s.underscore.split("/").last
|
17
|
+
class_name = self.to_s
|
18
|
+
api do
|
19
|
+
method="def #{method_name}(*names, &block)
|
20
|
+
install #{class_name}.new(self, *names, &block)
|
21
|
+
end"
|
22
|
+
eval(method)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# called by subclasses of PackageInstaller
|
27
|
+
def install_package(*names, &block) #:nodoc:
|
28
|
+
install Sprinkle::Installers::class.new(self, *names, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,21 +1,13 @@
|
|
1
1
|
module Sprinkle
|
2
2
|
module Installers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
super parent, options, &block
|
8
|
-
|
9
|
-
packages = [packages] unless packages.is_a?(Array)
|
10
|
-
packages.flatten!
|
11
|
-
|
12
|
-
|
13
|
-
@packages = packages
|
14
|
-
end
|
3
|
+
# The pacman installer installs Pacman packages
|
4
|
+
class Pacman < PackageInstaller
|
5
|
+
|
6
|
+
auto_api
|
15
7
|
|
16
8
|
protected
|
17
9
|
|
18
|
-
def install_commands
|
10
|
+
def install_commands #:nodoc:
|
19
11
|
"pacman -Sy #{@packages.join(' ')} --no-confirm --needed"
|
20
12
|
end
|
21
13
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# = Pear package installed
|
4
|
+
#
|
5
|
+
# Installs the specified pear package
|
6
|
+
#
|
7
|
+
# == Example Usage
|
8
|
+
#
|
9
|
+
# package :php_stuff do
|
10
|
+
# pear 'PHP_Compat'
|
11
|
+
# verify { has_pear 'PHP_Compat' }
|
12
|
+
# end
|
13
|
+
class Pear < Installer
|
14
|
+
attr_accessor :package_name
|
15
|
+
|
16
|
+
api do
|
17
|
+
def pear(package, &block)
|
18
|
+
install Sprinkle::Installers::Pear.new(self, package, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
verify_api do
|
23
|
+
def has_pear(package)
|
24
|
+
@commands << "pear list | grep \"#{package}\" | grep \"stable\""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(parent, package_name, &block) #:nodoc:
|
29
|
+
super parent, &block
|
30
|
+
@package_name = package_name
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
def install_commands #:nodoc:
|
35
|
+
"pear install --alldeps #{@package_name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,9 +2,7 @@ module Sprinkle
|
|
2
2
|
module Installers
|
3
3
|
# Beware, strange "installer" coming your way.
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# This installer pushes simple configuration into a file.
|
5
|
+
# This push text installer pushes simple configuration into a file.
|
8
6
|
#
|
9
7
|
# == Example Usage
|
10
8
|
#
|
@@ -22,14 +20,29 @@ module Sprinkle
|
|
22
20
|
# end
|
23
21
|
#
|
24
22
|
# A special verify step exists for this very installer
|
25
|
-
# its known as file_contains
|
23
|
+
# its known as +file_contains+, it will test that a file indeed
|
26
24
|
# contains a substring that you send it.
|
27
25
|
#
|
26
|
+
# package :magic_beans do
|
27
|
+
# push_text 'magic_beans', '/etc/apache2/apache2.conf'
|
28
|
+
# verify do
|
29
|
+
# file_contains '/etc/apache2/apache2.conf', 'magic_beans'
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
28
33
|
class PushText < Installer
|
29
34
|
attr_accessor :text, :path #:nodoc:
|
35
|
+
|
36
|
+
api do
|
37
|
+
def push_text(text, path, options = {}, &block)
|
38
|
+
install Sprinkle::Installers::PushText.new(self, text, path, options, &block)
|
39
|
+
end
|
40
|
+
end
|
30
41
|
|
31
42
|
def initialize(parent, text, path, options={}, &block) #:nodoc:
|
32
43
|
super parent, options, &block
|
44
|
+
# by default we would not want to push the same thing over and over
|
45
|
+
options.reverse_merge!(:idempotent => true)
|
33
46
|
@text = text
|
34
47
|
@path = path
|
35
48
|
end
|
@@ -37,7 +50,7 @@ module Sprinkle
|
|
37
50
|
protected
|
38
51
|
|
39
52
|
def install_commands #:nodoc:
|
40
|
-
"#{"#{
|
53
|
+
"#{"#{sudo_cmd}grep \"^#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}$\" #{@path} || " if option?(:idempotent) }/bin/echo -e '#{@text.gsub("'", "'\\\\''").gsub("\n", '\n')}' |#{sudo_cmd}tee -a #{@path}"
|
41
54
|
end
|
42
55
|
|
43
56
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Sprinkle
|
2
2
|
module Installers
|
3
|
-
# = Rake Installer
|
4
|
-
#
|
5
3
|
# This installer runs a rake command.
|
6
4
|
#
|
7
5
|
# == Example Usage
|
@@ -20,6 +18,13 @@ module Sprinkle
|
|
20
18
|
# end
|
21
19
|
|
22
20
|
class Rake < Installer
|
21
|
+
|
22
|
+
api do
|
23
|
+
def rake(name, options = {}, &block)
|
24
|
+
install Sprinkle::Installers::Rake.new(self, name, options, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
23
28
|
def initialize(parent, commands, options = {}, &block) #:nodoc:
|
24
29
|
super parent, options, &block
|
25
30
|
@commands = commands
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# Disconnects and reconnects the remote SSH session, you might want to do this
|
4
|
+
# after pushing a file that would affect the local shell environment
|
5
|
+
#
|
6
|
+
# == Example Usage
|
7
|
+
#
|
8
|
+
# package :download_with_proxy do
|
9
|
+
# push_text proxy_config, "/etc/environment", :sudo => true
|
10
|
+
# reconnect
|
11
|
+
# source "http://someurlthatneedstheproxy.com/installer.tar.gz"
|
12
|
+
# end
|
13
|
+
class Reconnect < Installer
|
14
|
+
|
15
|
+
api do
|
16
|
+
def reconnect(options={}, &block)
|
17
|
+
install Sprinkle::Installers::Reconnect.new(self, options, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# :RECONNECT is a symbol that the actors understand to mean to drop
|
22
|
+
# and reestablish any SSH conncetions they have open
|
23
|
+
def install_commands #:nodoc:
|
24
|
+
:RECONNECT
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|