auser-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 +67 -0
- data/README.rdoc +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.rb +28 -0
- data/lib/sprinkle/actors/capistrano.rb +89 -0
- data/lib/sprinkle/actors/vlad.rb +39 -0
- data/lib/sprinkle/configurable.rb +24 -0
- data/lib/sprinkle/deployment.rb +33 -0
- data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
- data/lib/sprinkle/extensions/array.rb +7 -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 +25 -0
- data/lib/sprinkle/installers/gem.rb +33 -0
- data/lib/sprinkle/installers/installer.rb +74 -0
- data/lib/sprinkle/installers/rake.rb +17 -0
- data/lib/sprinkle/installers/rpm.rb +20 -0
- data/lib/sprinkle/installers/source.rb +121 -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/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 +158 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Apt < Installer
|
4
|
+
attr_accessor :packages
|
5
|
+
|
6
|
+
def initialize(parent, *packages, &block)
|
7
|
+
super parent, &block
|
8
|
+
packages.flatten!
|
9
|
+
|
10
|
+
options = { :dependencies_only => false }
|
11
|
+
options.update(packages.pop) if packages.last.is_a?(Hash)
|
12
|
+
|
13
|
+
@command = options[:dependencies_only] ? 'build-dep' : 'install'
|
14
|
+
@packages = packages
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def install_commands
|
20
|
+
"DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get -qyu #{@command} #{@packages.join(' ')}"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Gem < Installer
|
4
|
+
attr_accessor :gem
|
5
|
+
|
6
|
+
def initialize(parent, gem, options = {}, &block)
|
7
|
+
super parent, options, &block
|
8
|
+
@gem = gem
|
9
|
+
end
|
10
|
+
|
11
|
+
def source(location = nil)
|
12
|
+
# package defines an installer called source so here we specify a method directly
|
13
|
+
# rather than rely on the automatic options processing since packages' method missing
|
14
|
+
# won't be run
|
15
|
+
return @options[:source] unless location
|
16
|
+
@options[:source] = location
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# rubygems 0.9.5+ installs dependencies by default, and does platform selection
|
22
|
+
|
23
|
+
def install_commands
|
24
|
+
cmd = "gem install #{gem}"
|
25
|
+
cmd << " --version '#{version}'" if version
|
26
|
+
cmd << " --source #{source}" if source
|
27
|
+
cmd << " --install-dir #{repository}" if repository
|
28
|
+
cmd
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Installer
|
4
|
+
include Sprinkle::Configurable
|
5
|
+
attr_accessor :delivery, :package, :options, :pre, :post
|
6
|
+
|
7
|
+
def initialize(package, options = {}, &block)
|
8
|
+
@package = package
|
9
|
+
@options = options
|
10
|
+
@pre = {}; @post = {}
|
11
|
+
self.instance_eval(&block) if block
|
12
|
+
end
|
13
|
+
|
14
|
+
def pre(stage, *commands)
|
15
|
+
@pre[stage] ||= []
|
16
|
+
@pre[stage] += commands
|
17
|
+
@pre[stage] += [yield] if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(stage, *commands)
|
21
|
+
@post[stage] ||= []
|
22
|
+
@post[stage] += commands
|
23
|
+
@post[stage] += [yield] if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def process(roles)
|
27
|
+
assert_delivery
|
28
|
+
|
29
|
+
if logger.debug?
|
30
|
+
sequence = install_sequence; sequence = sequence.join('; ') if sequence.is_a? Array
|
31
|
+
logger.debug "#{@package.name} install sequence: #{sequence} for roles: #{roles}\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
unless Sprinkle::OPTIONS[:testing]
|
35
|
+
logger.info "--> Installing #{package.name} for roles: #{roles}"
|
36
|
+
@delivery.process(@package.name, install_sequence, roles)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Installation is separated into two styles that concrete derivative installer classes
|
43
|
+
# can implement.
|
44
|
+
#
|
45
|
+
# Simple installers that issue a single or set of commands can overwride
|
46
|
+
# install_commands (eg. apt, gem, rpm). Pre/post install commands are included in this
|
47
|
+
# style for free.
|
48
|
+
#
|
49
|
+
# More complicated installers that have different stages, and require pre/post commands
|
50
|
+
# within stages can override install_sequence and take complete control of the install
|
51
|
+
# command sequence construction (eg. source based installer).
|
52
|
+
|
53
|
+
def install_sequence
|
54
|
+
commands = pre_commands(:install) + [ install_commands ] + post_commands(:install)
|
55
|
+
commands.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
def install_commands
|
59
|
+
raise 'Concrete installers implement this to specify commands to run to install their respective packages'
|
60
|
+
end
|
61
|
+
|
62
|
+
def pre_commands(stage)
|
63
|
+
dress @pre[stage] || [], :pre
|
64
|
+
end
|
65
|
+
|
66
|
+
def post_commands(stage)
|
67
|
+
dress @post[stage] || [], :post
|
68
|
+
end
|
69
|
+
|
70
|
+
def dress(commands, stage); commands; end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Rake < Installer
|
4
|
+
def initialize(parent, commands = [], &block)
|
5
|
+
super parent, &block
|
6
|
+
@commands = commands
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def install_commands
|
12
|
+
"rake #{@commands.join(' ')}"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Rpm < Installer
|
4
|
+
attr_accessor :packages
|
5
|
+
|
6
|
+
def initialize(parent, packages, &block)
|
7
|
+
super parent, &block
|
8
|
+
packages = [packages] unless packages.is_a? Array
|
9
|
+
@packages = packages
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def install_commands
|
15
|
+
"rpm -Uvh #{@packages.join(' ')}"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Sprinkle
|
2
|
+
module Installers
|
3
|
+
class Source < Installer
|
4
|
+
attr_accessor :source
|
5
|
+
|
6
|
+
def initialize(parent, source, options = {}, &block)
|
7
|
+
@source = source
|
8
|
+
super parent, options, &block
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def install_sequence
|
14
|
+
prepare + download + extract + configure + build + install
|
15
|
+
end
|
16
|
+
|
17
|
+
%w( prepare download extract configure build install ).each do |stage|
|
18
|
+
define_method stage do
|
19
|
+
pre_commands(stage.to_sym) + self.send("#{stage}_commands") + post_commands(stage.to_sym)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_commands
|
24
|
+
raise 'No installation area defined' unless @options[:prefix]
|
25
|
+
raise 'No build area defined' unless @options[:builds]
|
26
|
+
raise 'No source download area defined' unless @options[:archives]
|
27
|
+
|
28
|
+
[ "mkdir -p #{@options[:prefix]}",
|
29
|
+
"mkdir -p #{@options[:builds]}",
|
30
|
+
"mkdir -p #{@options[:archives]}" ]
|
31
|
+
end
|
32
|
+
|
33
|
+
def download_commands
|
34
|
+
[ "wget -cq --directory-prefix='#{@options[:archives]}' #{@source}" ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_commands
|
38
|
+
[ "bash -c 'cd #{@options[:builds]} && #{extract_command} #{@options[:archives]}/#{archive_name}'" ]
|
39
|
+
end
|
40
|
+
|
41
|
+
def configure_commands
|
42
|
+
return [] if custom_install?
|
43
|
+
|
44
|
+
command = "bash -c 'cd #{build_dir} && ./configure --prefix=#{@options[:prefix]} "
|
45
|
+
|
46
|
+
extras = {
|
47
|
+
:enable => '--enable', :disable => '--disable',
|
48
|
+
:with => '--with', :without => '--without'
|
49
|
+
}
|
50
|
+
|
51
|
+
extras.inject(command) { |m, (k, v)| m << create_options(k, v) if options[k]; m }
|
52
|
+
|
53
|
+
[ command << " > #{@package.name}-configure.log 2>&1'" ]
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_commands
|
57
|
+
return [] if custom_install?
|
58
|
+
[ "bash -c 'cd #{build_dir} && make > #{@package.name}-build.log 2>&1'" ]
|
59
|
+
end
|
60
|
+
|
61
|
+
def install_commands
|
62
|
+
return custom_install_commands if custom_install?
|
63
|
+
[ "bash -c 'cd #{build_dir} && make install > #{@package.name}-install.log 2>&1'" ]
|
64
|
+
end
|
65
|
+
|
66
|
+
def custom_install?
|
67
|
+
!! @options[:custom_install]
|
68
|
+
end
|
69
|
+
|
70
|
+
# REVISIT: must be better processing of custom install commands somehow? use splat operator?
|
71
|
+
def custom_install_commands
|
72
|
+
dress @options[:custom_install], :install
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def dress(commands, stage)
|
78
|
+
commands.collect { |command| "bash -c 'cd #{build_dir} && #{command} >> #{@package.name}-#{stage}.log 2>&1'" }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def create_options(key, prefix)
|
84
|
+
@options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_command
|
88
|
+
case @source
|
89
|
+
when /(tar.gz)|(tgz)$/
|
90
|
+
'tar xzf'
|
91
|
+
when /(tar.bz2)|(tb2)$/
|
92
|
+
'tar xjf'
|
93
|
+
when /tar$/
|
94
|
+
'tar xf'
|
95
|
+
when /zip$/
|
96
|
+
'unzip'
|
97
|
+
else
|
98
|
+
raise "Unknown source archive format: #{archive_name}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def archive_name
|
103
|
+
name = @source.split('/').last
|
104
|
+
raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
|
105
|
+
name
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_dir
|
109
|
+
"#{@options[:builds]}/#{options[:custom_dir] || base_dir}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def base_dir
|
113
|
+
if @source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
|
114
|
+
return $1
|
115
|
+
end
|
116
|
+
raise "Unknown base path for source archive: #{@source}, please update code knowledge"
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
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
|