auser-sprinkle 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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,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,13 @@
1
+ module Sprinkle
2
+ class Script
3
+ def self.sprinkle(script, filename = '__SCRIPT__')
4
+ powder = new
5
+ powder.instance_eval script, filename
6
+ powder.sprinkle
7
+ end
8
+
9
+ def sprinkle
10
+ @deployment.process if @deployment
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ module Directory
4
+ Sprinkle::Verify.register(Sprinkle::Verifiers::Directory)
5
+
6
+ def has_directory(dir)
7
+ @commands << "test -d #{dir}"
8
+ end
9
+ end
10
+ end
11
+ 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,11 @@
1
+ module Sprinkle
2
+ module Verifiers
3
+ module File
4
+ Sprinkle::Verify.register(Sprinkle::Verifiers::File)
5
+
6
+ def has_file(path)
7
+ @commands << "test -f #{path}"
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,55 @@
1
+ module Sprinkle
2
+ class Verify
3
+ include Sprinkle::Configurable
4
+ attr_accessor :package, :description, :commands
5
+
6
+ class <<self
7
+ # Register a verification module
8
+ def register(new_module)
9
+ class_eval { include new_module }
10
+ end
11
+ end
12
+
13
+ def initialize(package, description = '', &block)
14
+ raise 'Verify requires a block.' unless block
15
+
16
+ @package = package
17
+ @description = description
18
+ @commands = []
19
+ @options ||= {}
20
+ @options[:padding] = 4
21
+
22
+ self.instance_eval(&block)
23
+ end
24
+
25
+ def process(roles, pre = false)
26
+ assert_delivery
27
+
28
+ description = @description.empty? ? @package.name : @description
29
+
30
+ if logger.debug?
31
+ logger.debug "#{@package.name}#{description} verification sequence: #{@commands.join('; ')} for roles: #{roles}\n"
32
+ end
33
+
34
+ unless Sprinkle::OPTIONS[:testing]
35
+ logger.info "#{" " * @options[:padding]}--> Verifying #{description}..."
36
+
37
+ unless @delivery.process(@package.name, @commands, roles, true)
38
+ # Verification failed, halt sprinkling gracefully.
39
+ raise Sprinkle::VerificationFailed.new(@package, description)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class VerificationFailed < Exception
46
+ attr_accessor :package, :description
47
+
48
+ def initialize(package, description)
49
+ super("Verifying #{package.name}#{description} failed.")
50
+
51
+ @package = package
52
+ @description = description
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module Sprinkle #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 5
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'sprinkle'
11
+
12
+ module Kernel
13
+ def logger
14
+ @@__log_file__ ||= StringIO.new
15
+ @@__log__ = ActiveSupport::BufferedLogger.new @@__log_file__
16
+ end
17
+ end
@@ -0,0 +1,170 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Sprinkle::Actors::Capistrano do
4
+
5
+ before do
6
+ @recipes = 'deploy'
7
+ @cap = ::Capistrano::Configuration.new
8
+ ::Capistrano::Configuration.stub!(:new).and_return(@cap)
9
+ @cap.stub!(:load).and_return
10
+ end
11
+
12
+ def create_cap(&block)
13
+ Sprinkle::Actors::Capistrano.new &block
14
+ end
15
+
16
+ describe 'when created' do
17
+
18
+ it 'should create a new capistrano object' do
19
+ ::Capistrano::Configuration.should_receive(:new).and_return(@cap)
20
+ create_cap
21
+ end
22
+
23
+ describe 'when verbose' do
24
+
25
+ before do
26
+ Sprinkle::OPTIONS[:verbose] = true
27
+ end
28
+
29
+ it 'should set verbose logging on the capistrano object' do
30
+ @cap = create_cap
31
+ @cap.config.logger.level.should == ::Capistrano::Logger::INFO
32
+ end
33
+
34
+ end
35
+
36
+ describe 'when not verbose' do
37
+
38
+ before do
39
+ Sprinkle::OPTIONS[:verbose] = false
40
+ end
41
+
42
+ it 'should set quiet logging on the capistrano object' do
43
+ @cap = create_cap
44
+ @cap.config.logger.level.should == ::Capistrano::Logger::IMPORTANT
45
+ end
46
+
47
+ end
48
+
49
+ describe 'with a block' do
50
+
51
+ before do
52
+ @actor = create_cap do
53
+ recipes 'cool gear' # default is deploy
54
+ end
55
+ end
56
+
57
+ it 'should evaluate the block against the actor instance' do
58
+ @actor.loaded_recipes.should include('cool gear')
59
+ end
60
+
61
+ end
62
+
63
+ describe 'without a block' do
64
+
65
+ it 'should automatically load the default capistrano configuration' do
66
+ @cap.should_receive(:load).with('deploy').and_return
67
+ end
68
+
69
+ after do
70
+ @actor = create_cap
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ describe 'recipes' do
78
+
79
+ it 'should add the recipe location to an internal store' do
80
+ @cap = create_cap do
81
+ recipes 'deploy'
82
+ end
83
+ @cap.loaded_recipes.should == [ @recipes ]
84
+ end
85
+
86
+ it 'should load the given recipe' do
87
+ @cap.should_receive(:load).with(@recipes).and_return
88
+ create_cap
89
+ end
90
+
91
+ end
92
+
93
+ describe 'processing commands' do
94
+
95
+ before do
96
+ @commands = %w( op1 op2 )
97
+ @roles = %w( app )
98
+ @name = 'name'
99
+
100
+ @cap = create_cap do; recipes 'deploy'; end
101
+ @cap.stub!(:run).and_return
102
+
103
+ @testing_errors = false
104
+ end
105
+
106
+ it 'should dynamically create a capistrano task containing the commands' do
107
+ @cap.config.should_receive(:task).and_return
108
+ end
109
+
110
+ it 'should invoke capistrano task after creation' do
111
+ @cap.should_receive(:run).with(@name).and_return
112
+ end
113
+
114
+ it 'should raise capistrano errors when suppressing parameter is not set' do
115
+ @testing_errors = true
116
+
117
+ @cap.should_receive(:run).and_raise(::Capistrano::CommandError)
118
+ lambda { @cap.process @name, @commands, @roles }.should raise_error(::Capistrano::CommandError)
119
+ end
120
+
121
+ it 'should not raise errors and instead return false when suppressing parameter is set' do
122
+ @testing_errors = true
123
+
124
+ @cap.should_receive(:run).and_raise(::Capistrano::CommandError)
125
+
126
+ value = nil
127
+ lambda { value = @cap.process(@name, @commands, @roles, true) }.should_not raise_error(::Capistrano::CommandError)
128
+
129
+ value.should_not be
130
+ end
131
+
132
+ after do
133
+ @cap.process @name, @commands, @roles unless @testing_errors
134
+ end
135
+
136
+ end
137
+
138
+ describe 'generated task' do
139
+
140
+ before do
141
+ @commands = %w( op1 op2 )
142
+ @roles = %w( app )
143
+ @name = 'name'
144
+
145
+ @cap = create_cap do; recipes 'deploy'; end
146
+ @cap.config.stub!(:fetch).and_return(:sudo)
147
+ @cap.config.stub!(:invoke_command).and_return
148
+ end
149
+
150
+ it 'should use sudo to invoke commands when so configured' do
151
+ @cap.config.should_receive(:fetch).with(:run_method, :sudo).and_return(:sudo)
152
+ end
153
+
154
+ it 'should run the supplied commands' do
155
+ @cap.config.should_receive(:invoke_command).with('op1', :via => :sudo).ordered.and_return
156
+ @cap.config.should_receive(:invoke_command).with('op2', :via => :sudo).ordered.and_return
157
+ end
158
+
159
+ it 'should be applicable for the supplied roles' do
160
+ @cap.stub!(:run).and_return
161
+ @cap.config.should_receive(:task).with(:install_name, :roles => @roles).and_return
162
+ end
163
+
164
+ after do
165
+ @cap.process @name, @commands, @roles
166
+ end
167
+
168
+ end
169
+
170
+ end