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.
Files changed (69) hide show
  1. data/CREDITS +16 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +67 -0
  5. data/README.rdoc +224 -0
  6. data/Rakefile +4 -0
  7. data/bin/sprinkle +86 -0
  8. data/config/hoe.rb +70 -0
  9. data/config/requirements.rb +17 -0
  10. data/examples/merb/deploy.rb +5 -0
  11. data/examples/rails/README +15 -0
  12. data/examples/rails/deploy.rb +2 -0
  13. data/examples/rails/packages/database.rb +9 -0
  14. data/examples/rails/packages/essential.rb +6 -0
  15. data/examples/rails/packages/rails.rb +28 -0
  16. data/examples/rails/packages/search.rb +11 -0
  17. data/examples/rails/packages/server.rb +28 -0
  18. data/examples/rails/rails.rb +71 -0
  19. data/examples/sprinkle/sprinkle.rb +38 -0
  20. data/lib/sprinkle.rb +28 -0
  21. data/lib/sprinkle/actors/capistrano.rb +89 -0
  22. data/lib/sprinkle/actors/vlad.rb +39 -0
  23. data/lib/sprinkle/configurable.rb +24 -0
  24. data/lib/sprinkle/deployment.rb +33 -0
  25. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  26. data/lib/sprinkle/extensions/array.rb +7 -0
  27. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  28. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  29. data/lib/sprinkle/extensions/string.rb +10 -0
  30. data/lib/sprinkle/extensions/symbol.rb +7 -0
  31. data/lib/sprinkle/installers/apt.rb +25 -0
  32. data/lib/sprinkle/installers/gem.rb +33 -0
  33. data/lib/sprinkle/installers/installer.rb +74 -0
  34. data/lib/sprinkle/installers/rake.rb +17 -0
  35. data/lib/sprinkle/installers/rpm.rb +20 -0
  36. data/lib/sprinkle/installers/source.rb +121 -0
  37. data/lib/sprinkle/package.rb +127 -0
  38. data/lib/sprinkle/policy.rb +85 -0
  39. data/lib/sprinkle/script.rb +13 -0
  40. data/lib/sprinkle/verifiers/directory.rb +11 -0
  41. data/lib/sprinkle/verifiers/executable.rb +17 -0
  42. data/lib/sprinkle/verifiers/file.rb +11 -0
  43. data/lib/sprinkle/verifiers/symlink.rb +15 -0
  44. data/lib/sprinkle/verify.rb +55 -0
  45. data/lib/sprinkle/version.rb +9 -0
  46. data/script/destroy +14 -0
  47. data/script/generate +14 -0
  48. data/spec/spec.opts +1 -0
  49. data/spec/spec_helper.rb +17 -0
  50. data/spec/sprinkle/actors/capistrano_spec.rb +170 -0
  51. data/spec/sprinkle/configurable_spec.rb +46 -0
  52. data/spec/sprinkle/deployment_spec.rb +80 -0
  53. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  54. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  55. data/spec/sprinkle/installers/apt_spec.rb +74 -0
  56. data/spec/sprinkle/installers/gem_spec.rb +75 -0
  57. data/spec/sprinkle/installers/installer_spec.rb +151 -0
  58. data/spec/sprinkle/installers/rpm_spec.rb +50 -0
  59. data/spec/sprinkle/installers/source_spec.rb +331 -0
  60. data/spec/sprinkle/package_spec.rb +422 -0
  61. data/spec/sprinkle/policy_spec.rb +126 -0
  62. data/spec/sprinkle/script_spec.rb +51 -0
  63. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  64. data/spec/sprinkle/verify_spec.rb +137 -0
  65. data/sprinkle.gemspec +43 -0
  66. data/tasks/deployment.rake +34 -0
  67. data/tasks/environment.rake +7 -0
  68. data/tasks/rspec.rake +21 -0
  69. metadata +158 -0
@@ -0,0 +1,10 @@
1
+ module ArbitraryOptions
2
+ def self.included(base)
3
+ base.alias_method_chain :method_missing, :arbitrary_options
4
+ end
5
+
6
+ def method_missing_with_arbitrary_options(sym, *args, &block)
7
+ self.class.dsl_accessor sym
8
+ send(sym, *args, &block)
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+
3
+ def to_task_name
4
+ collect(&:to_task_name).join('_')
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ class BlankSlate
2
+ instance_methods.each do |m|
3
+ undef_method(m) unless %w( __send__ __id__ send class inspect instance_eval instance_variables ).include?(m)
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class Module
2
+ def dsl_accessor(*symbols)
3
+ symbols.each do |sym|
4
+ class_eval %{
5
+ def #{sym}(*val)
6
+ if val.empty?
7
+ @#{sym}
8
+ else
9
+ @#{sym} = val.size == 1 ? val[0] : val
10
+ end
11
+ end
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ class String
2
+
3
+ # REVISIT: what chars shall we allow in task names?
4
+ def to_task_name
5
+ s = downcase
6
+ s.gsub!(/-/, '_') # all - to _ chars
7
+ s
8
+ end
9
+
10
+ end
@@ -0,0 +1,7 @@
1
+ class Symbol
2
+
3
+ def to_task_name
4
+ to_s.to_task_name
5
+ end
6
+
7
+ end
@@ -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