mitchellh-sprinkle 0.1.5
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.
- data/CREDITS +16 -0
- data/History.txt +4 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +68 -0
- data/README.txt +224 -0
- data/Rakefile +4 -0
- data/bin/sprinkle +86 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/examples/merb/deploy.rb +5 -0
- data/examples/rails/README +15 -0
- data/examples/rails/deploy.rb +2 -0
- data/examples/rails/packages/database.rb +9 -0
- data/examples/rails/packages/essential.rb +6 -0
- data/examples/rails/packages/rails.rb +28 -0
- data/examples/rails/packages/search.rb +11 -0
- data/examples/rails/packages/server.rb +28 -0
- data/examples/rails/rails.rb +71 -0
- data/examples/sprinkle/sprinkle.rb +38 -0
- data/lib/sprinkle/actors/actors.rb +17 -0
- data/lib/sprinkle/actors/capistrano.rb +117 -0
- data/lib/sprinkle/actors/vlad.rb +65 -0
- data/lib/sprinkle/configurable.rb +27 -0
- data/lib/sprinkle/deployment.rb +73 -0
- data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
- data/lib/sprinkle/extensions/array.rb +5 -0
- data/lib/sprinkle/extensions/blank_slate.rb +5 -0
- data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
- data/lib/sprinkle/extensions/string.rb +10 -0
- data/lib/sprinkle/extensions/symbol.rb +7 -0
- data/lib/sprinkle/installers/apt.rb +51 -0
- data/lib/sprinkle/installers/gem.rb +61 -0
- data/lib/sprinkle/installers/installer.rb +120 -0
- data/lib/sprinkle/installers/rake.rb +30 -0
- data/lib/sprinkle/installers/rpm.rb +37 -0
- data/lib/sprinkle/installers/source.rb +179 -0
- data/lib/sprinkle/package.rb +127 -0
- data/lib/sprinkle/policy.rb +85 -0
- data/lib/sprinkle/script.rb +13 -0
- data/lib/sprinkle/verifiers/directory.rb +11 -0
- data/lib/sprinkle/verifiers/executable.rb +17 -0
- data/lib/sprinkle/verifiers/file.rb +11 -0
- data/lib/sprinkle/verifiers/symlink.rb +15 -0
- data/lib/sprinkle/verify.rb +55 -0
- data/lib/sprinkle/version.rb +9 -0
- data/lib/sprinkle.rb +32 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/sprinkle/actors/capistrano_spec.rb +170 -0
- data/spec/sprinkle/configurable_spec.rb +46 -0
- data/spec/sprinkle/deployment_spec.rb +80 -0
- data/spec/sprinkle/extensions/array_spec.rb +19 -0
- data/spec/sprinkle/extensions/string_spec.rb +21 -0
- data/spec/sprinkle/installers/apt_spec.rb +74 -0
- data/spec/sprinkle/installers/gem_spec.rb +75 -0
- data/spec/sprinkle/installers/installer_spec.rb +151 -0
- data/spec/sprinkle/installers/rpm_spec.rb +50 -0
- data/spec/sprinkle/installers/source_spec.rb +331 -0
- data/spec/sprinkle/package_spec.rb +422 -0
- data/spec/sprinkle/policy_spec.rb +126 -0
- data/spec/sprinkle/script_spec.rb +51 -0
- data/spec/sprinkle/sprinkle_spec.rb +25 -0
- data/spec/sprinkle/verify_spec.rb +137 -0
- data/sprinkle.gemspec +43 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- metadata +160 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# The base class which all installers must subclass, this class makes
|
4
|
+
# sure all installers share some general features, which are outlined
|
5
|
+
# below.
|
6
|
+
#
|
7
|
+
# = Pre/Post Installation Hooks
|
8
|
+
#
|
9
|
+
# With all intallation methods you have the ability to specify multiple
|
10
|
+
# pre/post installation hooks. This gives you the ability to specify
|
11
|
+
# commands to run before and after an installation takes place. All
|
12
|
+
# commands by default are sudo'd so there is no need to include "sudo"
|
13
|
+
# in the command itself. There are three ways to specify a pre/post hook.
|
14
|
+
#
|
15
|
+
# First, a single command:
|
16
|
+
#
|
17
|
+
# pre :install, 'echo "Hello, World!"'
|
18
|
+
# post :install, 'rm -rf /'
|
19
|
+
#
|
20
|
+
# Second, an array of commands:
|
21
|
+
#
|
22
|
+
# commands = ['echo "First"', 'echo "Then Another"']
|
23
|
+
# pre :install, commands
|
24
|
+
# post :install, commands
|
25
|
+
#
|
26
|
+
# Third, a block which returns either a single or multiple commands:
|
27
|
+
#
|
28
|
+
# pre :install do
|
29
|
+
# amount = 7 * 3
|
30
|
+
# "echo 'Before we install, lets plant #{amount} magic beans...'"
|
31
|
+
# end
|
32
|
+
# post :install do
|
33
|
+
# ['echo "Now... let's hope they sprout!", 'echo "Indeed they have!"']
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# = Other Pre/Post Hooks
|
37
|
+
#
|
38
|
+
# Some installation methods actually grant you more fine grained
|
39
|
+
# control of when commands are run rather than a blanket pre :install
|
40
|
+
# or post :install. If this is the case, it will be documented on
|
41
|
+
# the installation method's corresponding documentation page.
|
42
|
+
class Installer
|
43
|
+
include Sprinkle::Configurable
|
44
|
+
attr_accessor :delivery, :package, :options, :pre, :post #:nodoc:
|
45
|
+
|
46
|
+
def initialize(package, options = {}, &block) #:nodoc:
|
47
|
+
@package = package
|
48
|
+
@options = options
|
49
|
+
@pre = {}; @post = {}
|
50
|
+
self.instance_eval(&block) if block
|
51
|
+
end
|
52
|
+
|
53
|
+
def pre(stage, *commands)
|
54
|
+
@pre[stage] ||= []
|
55
|
+
@pre[stage] += commands
|
56
|
+
@pre[stage] += [yield] if block_given?
|
57
|
+
end
|
58
|
+
|
59
|
+
def post(stage, *commands)
|
60
|
+
@post[stage] ||= []
|
61
|
+
@post[stage] += commands
|
62
|
+
@post[stage] += [yield] if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
def process(roles) #:nodoc:
|
66
|
+
assert_delivery
|
67
|
+
|
68
|
+
if logger.debug?
|
69
|
+
sequence = install_sequence; sequence = sequence.join('; ') if sequence.is_a? Array
|
70
|
+
logger.debug "#{@package.name} install sequence: #{sequence} for roles: #{roles}\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
unless Sprinkle::OPTIONS[:testing]
|
74
|
+
logger.info "--> Installing #{package.name} for roles: #{roles}"
|
75
|
+
@delivery.process(@package.name, install_sequence, roles)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
# More complicated installers that have different stages, and require pre/post commands
|
81
|
+
# within stages can override install_sequence and take complete control of the install
|
82
|
+
# command sequence construction (eg. source based installer).
|
83
|
+
def install_sequence
|
84
|
+
commands = pre_commands(:install) + [ install_commands ] + post_commands(:install)
|
85
|
+
commands.flatten
|
86
|
+
end
|
87
|
+
|
88
|
+
# A concrete installer (subclass of this virtual class) must override this method
|
89
|
+
# and return the commands it needs to run as either a string or an array.
|
90
|
+
#
|
91
|
+
# <b>Overriding this method is required.</b>
|
92
|
+
def install_commands
|
93
|
+
raise 'Concrete installers implement this to specify commands to run to install their respective packages'
|
94
|
+
end
|
95
|
+
|
96
|
+
def pre_commands(stage) #:nodoc:
|
97
|
+
dress @pre[stage] || [], :pre
|
98
|
+
end
|
99
|
+
|
100
|
+
def post_commands(stage) #:nodoc:
|
101
|
+
dress @post[stage] || [], :post
|
102
|
+
end
|
103
|
+
|
104
|
+
# Concrete installers (subclasses of this virtual class) can override this method to
|
105
|
+
# specify stage-specific (pre-installation, post-installation, etc.) modifications
|
106
|
+
# of commands.
|
107
|
+
#
|
108
|
+
# An example usage of overriding this would be to prefix all commands for a
|
109
|
+
# certain stage to change to a certain directory. An example is given below:
|
110
|
+
#
|
111
|
+
# def dress(commands, stage)
|
112
|
+
# commands.collect { |x| "cd #{magic_beans_path} && #{x}" }
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# By default, no modifications are made to the commands.
|
116
|
+
def dress(commands, stage); commands; end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# = Rake Installer
|
4
|
+
#
|
5
|
+
# This installer runs a rake command.
|
6
|
+
#
|
7
|
+
# == Example Usage
|
8
|
+
#
|
9
|
+
# The following example runs the command "rake spec" on
|
10
|
+
# the remote server.
|
11
|
+
#
|
12
|
+
# package :spec do
|
13
|
+
# rake 'spec'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
class Rake < Installer
|
17
|
+
def initialize(parent, commands = [], &block) #:nodoc:
|
18
|
+
super parent, &block
|
19
|
+
@commands = commands
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def install_commands #:nodoc:
|
25
|
+
"rake #{@commands.join(' ')}"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# = RPM Package Installer
|
4
|
+
#
|
5
|
+
# The RPM package installer installs RPM packages.
|
6
|
+
#
|
7
|
+
# == Example Usage
|
8
|
+
#
|
9
|
+
# Installing the magic_beans RPM. Its all the craze these days.
|
10
|
+
#
|
11
|
+
# package :magic_beans do
|
12
|
+
# rpm 'magic_beans'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# You may also specify multiple rpms as an array:
|
16
|
+
#
|
17
|
+
# package :magic_beans do
|
18
|
+
# rpm %w(magic_beans magic_sauce)
|
19
|
+
# end
|
20
|
+
class Rpm < Installer
|
21
|
+
attr_accessor :packages #:nodoc:
|
22
|
+
|
23
|
+
def initialize(parent, packages, &block) #:nodoc:
|
24
|
+
super parent, &block
|
25
|
+
packages = [packages] unless packages.is_a? Array
|
26
|
+
@packages = packages
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def install_commands #:nodoc:
|
32
|
+
"rpm -Uvh #{@packages.join(' ')}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
# = Source Package Installer
|
4
|
+
#
|
5
|
+
# The source package installer installs software from source.
|
6
|
+
# It handles downloading, extracting, configuring, building,
|
7
|
+
# and installing software.
|
8
|
+
#
|
9
|
+
# == Configuration Options
|
10
|
+
#
|
11
|
+
# The source installer has many configuration options:
|
12
|
+
# * <b>prefix</b> - The prefix directory that is configured to.
|
13
|
+
# * <b>archives</b> - The location all the files are downloaded to.
|
14
|
+
# * <b>builds</b> - The directory the package is extracted to to configure and install
|
15
|
+
#
|
16
|
+
# == Pre/Post Hooks
|
17
|
+
#
|
18
|
+
# The source installer defines a myriad of new stages which can be hooked into:
|
19
|
+
# * <b>prepare</b> - Prepare is the stage which all the prefix, archives, and build directories are made.
|
20
|
+
# * <b>download</b> - Download is the stage which the software package is downloaded.
|
21
|
+
# * <b>extract</b> - Extract is the stage which the software package is extracted.
|
22
|
+
# * <b>configure</b> - Configure is the stage which the ./configure script is run.
|
23
|
+
# * <b>build</b> - Build is the stage in which `make` is called.
|
24
|
+
# * <b>install</b> - Install is the stage which `make install` is called.
|
25
|
+
#
|
26
|
+
# == Example Usage
|
27
|
+
#
|
28
|
+
# First, a simple package, no configuration:
|
29
|
+
#
|
30
|
+
# package :magic_beans do
|
31
|
+
# source 'http://magicbeansland.com/latest-1.1.1.tar.gz'
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Second, specifying exactly where I want my files:
|
35
|
+
#
|
36
|
+
# package :magic_beans do
|
37
|
+
# source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
|
38
|
+
# prefix '/usr/local'
|
39
|
+
# archives '/tmp'
|
40
|
+
# builds '/tmp/builds'
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Third, specifying some hooks:
|
45
|
+
#
|
46
|
+
# package :magic_beans do
|
47
|
+
# source 'http://magicbeansland.com/latest-1.1.1.tar.gz' do
|
48
|
+
# prefix '/usr/local'
|
49
|
+
#
|
50
|
+
# pre :prepare { 'echo "Here we go folks."' }
|
51
|
+
# post :extract { 'echo "I believe..."' }
|
52
|
+
# pre :build { 'echo "Cross your fingers!"' }
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# As you can see, setting options is as simple as creating a
|
57
|
+
# block and calling the option as a method with the value as
|
58
|
+
# its parameter.
|
59
|
+
class Source < Installer
|
60
|
+
attr_accessor :source #:nodoc:
|
61
|
+
|
62
|
+
def initialize(parent, source, options = {}, &block) #:nodoc:
|
63
|
+
@source = source
|
64
|
+
super parent, options, &block
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def install_sequence #:nodoc:
|
70
|
+
prepare + download + extract + configure + build + install
|
71
|
+
end
|
72
|
+
|
73
|
+
%w( prepare download extract configure build install ).each do |stage|
|
74
|
+
define_method stage do
|
75
|
+
pre_commands(stage.to_sym) + self.send("#{stage}_commands") + post_commands(stage.to_sym)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def prepare_commands #:nodoc:
|
80
|
+
raise 'No installation area defined' unless @options[:prefix]
|
81
|
+
raise 'No build area defined' unless @options[:builds]
|
82
|
+
raise 'No source download area defined' unless @options[:archives]
|
83
|
+
|
84
|
+
[ "mkdir -p #{@options[:prefix]}",
|
85
|
+
"mkdir -p #{@options[:builds]}",
|
86
|
+
"mkdir -p #{@options[:archives]}" ]
|
87
|
+
end
|
88
|
+
|
89
|
+
def download_commands #:nodoc:
|
90
|
+
[ "wget -cq --directory-prefix='#{@options[:archives]}' #{@source}" ]
|
91
|
+
end
|
92
|
+
|
93
|
+
def extract_commands #:nodoc:
|
94
|
+
[ "bash -c 'cd #{@options[:builds]} && #{extract_command} #{@options[:archives]}/#{archive_name}'" ]
|
95
|
+
end
|
96
|
+
|
97
|
+
def configure_commands #:nodoc:
|
98
|
+
return [] if custom_install?
|
99
|
+
|
100
|
+
command = "bash -c 'cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
|
101
|
+
|
102
|
+
extras = {
|
103
|
+
:enable => '--enable', :disable => '--disable',
|
104
|
+
:with => '--with', :without => '--without'
|
105
|
+
}
|
106
|
+
|
107
|
+
extras.inject(command) { |m, (k, v)| m << create_options(k, v) if options[k]; m }
|
108
|
+
|
109
|
+
[ command << " > #{@package.name}-configure.log 2>&1'" ]
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_commands #:nodoc:
|
113
|
+
return [] if custom_install?
|
114
|
+
[ "bash -c 'cd #{build_dir} && make > #{@package.name}-build.log 2>&1'" ]
|
115
|
+
end
|
116
|
+
|
117
|
+
def install_commands #:nodoc:
|
118
|
+
return custom_install_commands if custom_install?
|
119
|
+
[ "bash -c 'cd #{build_dir} && make install > #{@package.name}-install.log 2>&1'" ]
|
120
|
+
end
|
121
|
+
|
122
|
+
def custom_install? #:nodoc:
|
123
|
+
!! @options[:custom_install]
|
124
|
+
end
|
125
|
+
|
126
|
+
# REVISIT: must be better processing of custom install commands somehow? use splat operator?
|
127
|
+
def custom_install_commands #:nodoc:
|
128
|
+
dress @options[:custom_install], :install
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
# dress is overriden from the base Sprinkle::Installers::Installer class so that the command changes
|
134
|
+
# directory to the build directory first. Also, the result of the command is logged.
|
135
|
+
def dress(commands, stage)
|
136
|
+
commands.collect { |command| "bash -c 'cd #{build_dir} && #{command} >> #{@package.name}-#{stage}.log 2>&1'" }
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def create_options(key, prefix) #:nodoc:
|
142
|
+
@options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
|
143
|
+
end
|
144
|
+
|
145
|
+
def extract_command #:nodoc:
|
146
|
+
case @source
|
147
|
+
when /(tar.gz)|(tgz)$/
|
148
|
+
'tar xzf'
|
149
|
+
when /(tar.bz2)|(tb2)$/
|
150
|
+
'tar xjf'
|
151
|
+
when /tar$/
|
152
|
+
'tar xf'
|
153
|
+
when /zip$/
|
154
|
+
'unzip'
|
155
|
+
else
|
156
|
+
raise "Unknown source archive format: #{archive_name}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def archive_name #:nodoc:
|
161
|
+
name = @source.split('/').last
|
162
|
+
raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
|
163
|
+
name
|
164
|
+
end
|
165
|
+
|
166
|
+
def build_dir #:nodoc:
|
167
|
+
"#{@options[:builds]}/#{options[:custom_dir] || base_dir}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def base_dir #:nodoc:
|
171
|
+
if @source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
|
172
|
+
return $1
|
173
|
+
end
|
174
|
+
raise "Unknown base path for source archive: #{@source}, please update code knowledge"
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Package
|
3
|
+
PACKAGES = {}
|
4
|
+
|
5
|
+
def package(name, metadata = {}, &block)
|
6
|
+
package = Package.new(name, metadata, &block)
|
7
|
+
PACKAGES[name] = package
|
8
|
+
|
9
|
+
if package.provides
|
10
|
+
(PACKAGES[package.provides] ||= []) << package
|
11
|
+
end
|
12
|
+
|
13
|
+
package
|
14
|
+
end
|
15
|
+
|
16
|
+
class Package
|
17
|
+
include ArbitraryOptions
|
18
|
+
attr_accessor :name, :provides, :installer, :dependencies, :recommends, :verifications
|
19
|
+
|
20
|
+
def initialize(name, metadata = {}, &block)
|
21
|
+
raise 'No package name supplied' unless name
|
22
|
+
|
23
|
+
@name = name
|
24
|
+
@provides = metadata[:provides]
|
25
|
+
@dependencies = []
|
26
|
+
@recommends = []
|
27
|
+
@verifications = []
|
28
|
+
self.instance_eval &block
|
29
|
+
end
|
30
|
+
|
31
|
+
def apt(*names, &block)
|
32
|
+
@installer = Sprinkle::Installers::Apt.new(self, *names, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def rpm(*names, &block)
|
36
|
+
@installer = Sprinkle::Installers::Rpm.new(self, *names, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def gem(name, options = {}, &block)
|
40
|
+
@recommends << :rubygems
|
41
|
+
@installer = Sprinkle::Installers::Gem.new(self, name, options, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def source(source, options = {}, &block)
|
45
|
+
@recommends << :build_essential # Ubuntu/Debian
|
46
|
+
@installer = Sprinkle::Installers::Source.new(self, source, options, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def verify(description = '', &block)
|
50
|
+
@verifications << Sprinkle::Verify.new(self, description, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def process(deployment, roles)
|
54
|
+
return if meta_package?
|
55
|
+
|
56
|
+
# Run a pre-test to see if the software is already installed. If so,
|
57
|
+
# we can skip it, unless we have the force option turned on!
|
58
|
+
unless @verifications.empty? || Sprinkle::OPTIONS[:force]
|
59
|
+
begin
|
60
|
+
process_verifications(deployment, roles, true)
|
61
|
+
|
62
|
+
logger.info "--> #{self.name} already installed for roles: #{roles}"
|
63
|
+
return
|
64
|
+
rescue Sprinkle::VerificationFailed => e
|
65
|
+
# Continue
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@installer.defaults(deployment)
|
70
|
+
@installer.process(roles)
|
71
|
+
|
72
|
+
process_verifications(deployment, roles)
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_verifications(deployment, roles, pre = false)
|
76
|
+
if pre
|
77
|
+
logger.info "--> Checking if #{self.name} is already installed for roles: #{roles}"
|
78
|
+
else
|
79
|
+
logger.info "--> Verifying #{self.name} was properly installed for roles: #{roles}"
|
80
|
+
end
|
81
|
+
|
82
|
+
@verifications.each do |v|
|
83
|
+
v.defaults(deployment)
|
84
|
+
v.process(roles)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def requires(*packages)
|
89
|
+
@dependencies << packages
|
90
|
+
@dependencies.flatten!
|
91
|
+
end
|
92
|
+
|
93
|
+
def recommends(*packages)
|
94
|
+
@recommends << packages
|
95
|
+
@recommends.flatten!
|
96
|
+
end
|
97
|
+
|
98
|
+
def tree(depth = 1, &block)
|
99
|
+
packages = []
|
100
|
+
|
101
|
+
@recommends.each do |dep|
|
102
|
+
package = PACKAGES[dep]
|
103
|
+
next unless package # skip missing recommended packages as they can be optional
|
104
|
+
block.call(self, package, depth) if block
|
105
|
+
packages << package.tree(depth + 1, &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
@dependencies.each do |dep|
|
109
|
+
package = PACKAGES[dep]
|
110
|
+
raise "Package definition not found for key: #{dep}" unless package
|
111
|
+
block.call(self, package, depth) if block
|
112
|
+
packages << package.tree(depth + 1, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
packages << self
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_s; @name; end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def meta_package?
|
123
|
+
@installer == nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
3
|
+
module Sprinkle
|
4
|
+
module Policy
|
5
|
+
POLICIES = []
|
6
|
+
|
7
|
+
def policy(name, options = {}, &block)
|
8
|
+
p = Policy.new(name, options, &block)
|
9
|
+
POLICIES << p
|
10
|
+
p
|
11
|
+
end
|
12
|
+
|
13
|
+
class Policy
|
14
|
+
attr_reader :name, :packages
|
15
|
+
|
16
|
+
def initialize(name, metadata = {}, &block)
|
17
|
+
raise 'No name provided' unless name
|
18
|
+
raise 'No roles provided' unless metadata[:roles]
|
19
|
+
|
20
|
+
@name = name
|
21
|
+
@roles = metadata[:roles]
|
22
|
+
@packages = []
|
23
|
+
self.instance_eval(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def requires(package, options = {})
|
27
|
+
@packages << package
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s; name; end
|
31
|
+
|
32
|
+
def process(deployment)
|
33
|
+
all = []
|
34
|
+
|
35
|
+
cloud_info "--> Cloud hierarchy for policy #{@name}"
|
36
|
+
|
37
|
+
@packages.each do |p|
|
38
|
+
cloud_info "\nPolicy #{@name} requires package #{p}"
|
39
|
+
|
40
|
+
package = Sprinkle::Package::PACKAGES[p]
|
41
|
+
raise "Package definition not found for key: #{p}" unless package
|
42
|
+
package = select_package(p, package) if package.is_a? Array # handle virtual package selection
|
43
|
+
|
44
|
+
tree = package.tree do |parent, child, depth|
|
45
|
+
indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
all << tree
|
49
|
+
end
|
50
|
+
|
51
|
+
normalize(all) do |package|
|
52
|
+
package.process(deployment, @roles)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def cloud_info(message)
|
59
|
+
logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
|
60
|
+
end
|
61
|
+
|
62
|
+
def select_package(name, packages)
|
63
|
+
if packages.size <= 1
|
64
|
+
package = packages.first
|
65
|
+
else
|
66
|
+
package = choose do |menu|
|
67
|
+
menu.prompt = "Multiple choices exist for virtual package #{name}"
|
68
|
+
menu.choices *packages.collect(&:to_s)
|
69
|
+
end
|
70
|
+
package = Sprinkle::Package::PACKAGES[package]
|
71
|
+
end
|
72
|
+
|
73
|
+
cloud_info "Selecting #{package.to_s} for virtual package #{name}"
|
74
|
+
|
75
|
+
package
|
76
|
+
end
|
77
|
+
|
78
|
+
def normalize(all, &block)
|
79
|
+
all = all.flatten.uniq
|
80
|
+
cloud_info "\n--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}"
|
81
|
+
all.each &block
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Verifiers
|
3
|
+
module Executable
|
4
|
+
Sprinkle::Verify.register(Sprinkle::Verifiers::Executable)
|
5
|
+
|
6
|
+
def has_executable(path)
|
7
|
+
# Be smart: If the path includes a forward slash, we're checking
|
8
|
+
# an absolute path. Otherwise, we're checking a global executable
|
9
|
+
if path.include?('/')
|
10
|
+
@commands << "test -x #{path}"
|
11
|
+
else
|
12
|
+
@commands << "[ -n \"`which #{path}`\"]"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Verifiers
|
3
|
+
module Symlink
|
4
|
+
Sprinkle::Verify.register(Sprinkle::Verifiers::Symlink)
|
5
|
+
|
6
|
+
def has_symlink(symlink, file = nil)
|
7
|
+
if file.nil?
|
8
|
+
@commands << "test -L #{symlink}"
|
9
|
+
else
|
10
|
+
@commands << "test '#{file}' = `readlink #{symlink}`"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|