caterer 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,12 +5,15 @@ module Caterer
5
5
  class Image < Base
6
6
 
7
7
  def call(env)
8
- # check to ensure the image exists
9
- image = env[:config].images[env[:image]]
10
8
 
11
- if not image
12
- env[:ui].error "image ':#{env[:image]}' is not defined"
13
- return
9
+ if env[:image]
10
+ # check to ensure the image exists
11
+ image = env[:config].images[env[:image]]
12
+
13
+ if not image
14
+ env[:ui].error "image ':#{env[:image]}' is not defined"
15
+ return
16
+ end
14
17
  end
15
18
 
16
19
  @app.call(env)
@@ -5,19 +5,22 @@ module Caterer
5
5
  class Provisioner < Base
6
6
 
7
7
  def call(env)
8
- provisioner = env[:config].images[env[:image]].provisioner
9
8
 
10
- if not provisioner
11
- env[:ui].error "provisioner for image ':#{env[:image]}' is not defined"
12
- return
13
- end
14
-
15
- if errors = provisioner.errors
16
- errors.each do |key, val|
17
- env[:ui].error "image :#{env[:image]} provisioner error -> #{key} #{val}"
9
+ if env[:image]
10
+ provisioner = env[:config].images[env[:image]].provisioner
11
+
12
+ if not provisioner
13
+ env[:ui].error "provisioner for image ':#{env[:image]}' is not defined"
14
+ return
18
15
  end
19
- return
20
- end
16
+
17
+ if errors = provisioner.errors
18
+ errors.each do |key, val|
19
+ env[:ui].error "image :#{env[:image]} provisioner error -> #{key} #{val}"
20
+ end
21
+ return
22
+ end
23
+ end
21
24
 
22
25
  @app.call(env)
23
26
  end
@@ -1,13 +1,19 @@
1
- require 'active_support/inflector'
2
-
3
1
  module Caterer
4
2
  module Action
5
3
  module Provisioner
6
4
  class Base < Action::Base
7
5
 
8
6
  def provisioner(env)
9
- config = env[:config].images[env[:image]].provisioner
10
- "Caterer::Provisioner::#{config.name.to_s.classify}".constantize.new(env[:server], config)
7
+
8
+ if image = env[:image]
9
+ env[:config].images[image].provisioner
10
+ else
11
+ Caterer.provisioners.get(env[:engine] || default_engine).new
12
+ end
13
+ end
14
+
15
+ def default_engine
16
+ Caterer.config.default_provisioner
11
17
  end
12
18
 
13
19
  end
@@ -4,7 +4,7 @@ module Caterer
4
4
  class Bootstrap < Base
5
5
 
6
6
  def call(env)
7
- provisioner(env).bootstrap
7
+ provisioner(env).bootstrap(env[:server])
8
8
  @app.call(env)
9
9
  end
10
10
 
@@ -4,7 +4,7 @@ module Caterer
4
4
  class Cleanup < Base
5
5
 
6
6
  def call(env)
7
- provisioner(env).cleanup
7
+ provisioner(env).cleanup(env[:server])
8
8
  @app.call(env)
9
9
  end
10
10
 
@@ -4,7 +4,7 @@ module Caterer
4
4
  class Install < Base
5
5
 
6
6
  def call(env)
7
- provisioner(env).install
7
+ provisioner(env).install(env[:server])
8
8
  @app.call(env)
9
9
  end
10
10
 
@@ -4,7 +4,7 @@ module Caterer
4
4
  class Prepare < Base
5
5
 
6
6
  def call(env)
7
- provisioner(env).prepare
7
+ provisioner(env).prepare(env[:server])
8
8
  @app.call(env)
9
9
  end
10
10
 
@@ -4,7 +4,7 @@ module Caterer
4
4
  class Provision < Base
5
5
 
6
6
  def call(env)
7
- provisioner(env).provision
7
+ provisioner(env).provision(env[:server])
8
8
  @app.call(env)
9
9
  end
10
10
 
@@ -6,7 +6,7 @@ module Caterer
6
6
 
7
7
  def call(env)
8
8
 
9
- if not provisioner(env).bootstrapped?
9
+ if not provisioner(env).bootstrapped?(env[:server])
10
10
  env[:ui].error "Server not bootstrapped, cannot continue"
11
11
  return
12
12
  end
@@ -0,0 +1,24 @@
1
+ module Caterer
2
+ module Action
3
+ module Provisioner
4
+ module Validate
5
+ class Engine < Base
6
+
7
+ def call(env)
8
+
9
+ if env[:engine]
10
+ if not Caterer.provisioners.get(env[:engine])
11
+ env[:ui].error "Unsupported provision engine #{env[:engine]}"
12
+ return
13
+ end
14
+ end
15
+
16
+ @app.call(env)
17
+
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,6 +3,7 @@ module Caterer
3
3
  module Provisioner
4
4
  module Validate
5
5
  autoload :Bootstrapped, 'caterer/action/provisioner/validate/bootstrapped'
6
+ autoload :Engine, 'caterer/action/provisioner/validate/engine'
6
7
  end
7
8
  end
8
9
  end
@@ -3,6 +3,7 @@ Caterer.actions.register(:validate) do
3
3
  Vli::Action::Builder.new do
4
4
  use Caterer::Action::Config::Validate::Image
5
5
  use Caterer::Action::Config::Validate::Provisioner
6
+ use Caterer::Action::Provisioner::Validate::Engine
6
7
  use Caterer::Action::Server::Validate::SSH
7
8
  use Caterer::Action::Server::Validate::Unlocked
8
9
  end
@@ -60,7 +60,7 @@ module Caterer
60
60
  argv.first.split(",").each do |host|
61
61
 
62
62
  if group = @env.config.groups[host.to_sym]
63
- group.members.each do |member|
63
+ group.members.each do |key, member|
64
64
  servers << init_server(group, member, options)
65
65
  end
66
66
  else
@@ -99,6 +99,10 @@ module Caterer
99
99
  opts[:images] = image_list(options) || member.images || group.images
100
100
  opts[:key] = options[:key] || member.key || group.key
101
101
 
102
+ if engine = options[:engine]
103
+ opts[:engine] = engine.to_sym
104
+ end
105
+
102
106
  opts[:data] = begin
103
107
  data = nil
104
108
  if json = options[:data]
@@ -3,10 +3,12 @@ module Caterer
3
3
  class Base
4
4
 
5
5
  attr_reader :images, :groups
6
+ attr_accessor :default_provisioner
6
7
 
7
8
  def initialize
8
9
  @images = {}
9
10
  @groups = {}
11
+ @default_provisioner = :chef_solo
10
12
  end
11
13
 
12
14
  def image(name)
@@ -1,5 +1,3 @@
1
- require 'active_support/inflector'
2
-
3
1
  module Caterer
4
2
  module Config
5
3
  class Image
@@ -11,7 +9,9 @@ module Caterer
11
9
  end
12
10
 
13
11
  def provision(type)
14
- @provisioner = "Caterer::Config::Provision::#{type.to_s.classify}".constantize.new(type)
12
+ provisioner_klass = Caterer.provisioners.get(type)
13
+ raise ":#{type} is not a valida provisioner" if not provisioner_klass
14
+ @provisioner = provisioner_klass.new
15
15
  yield @provisioner if block_given?
16
16
  end
17
17
 
@@ -2,19 +2,16 @@ module Caterer
2
2
  module Provisioner
3
3
  class Base
4
4
 
5
- attr_reader :server, :config
5
+ # config dsl
6
+ def errors; end
6
7
 
7
- def initialize(server, config=nil)
8
- @server = server
9
- @config = config
10
- end
11
-
12
- def bootstrap; end
13
- def bootstrapped?; true; end
14
- def cleanup; end
15
- def install; end
16
- def prepare; end
17
- def provision; end
8
+ # provision dsl
9
+ def bootstrap(server); end
10
+ def bootstrapped?(server); true; end
11
+ def cleanup(server); end
12
+ def install(server); end
13
+ def prepare(server); end
14
+ def provision(server); end
18
15
 
19
16
  end
20
17
  end
@@ -8,7 +8,76 @@ module Caterer
8
8
  module Provisioner
9
9
  class ChefSolo < Base
10
10
 
11
- def bootstrap
11
+ attr_reader :run_list
12
+ attr_accessor :json, :cookbooks_path, :roles_path
13
+ attr_accessor :data_bags_path, :bootstrap_scripts
14
+
15
+ def initialize
16
+ @run_list = []
17
+ @json = {}
18
+ @cookbooks_path = ['cookbooks']
19
+ @roles_path = ['roles']
20
+ @data_bags_path = ['data_bags']
21
+ @bootstrap_scripts = []
22
+ end
23
+
24
+ # config DSL
25
+
26
+ def add_recipe(recipe)
27
+ @run_list << "recipe[#{recipe}]"
28
+ end
29
+
30
+ def add_role(role)
31
+ @run_list << "role[#{role}]"
32
+ end
33
+
34
+ # I don't like this at all, but it seems to make the best Caterfile workflow
35
+ def add_image(image)
36
+ image = Caterer.config.images[image]
37
+ raise "Unknown image :#{image}" if not image
38
+
39
+ provisioner = image.provisioner
40
+ raise "No provisioner for :#{image}" if not provisioner
41
+
42
+ if not provisioner.class == self.class
43
+ raise "add_image incompatibility: #{provisioner.class} != #{self.class}"
44
+ end
45
+
46
+ @run_list += provisioner.run_list
47
+ @bootstrap_scripts += provisioner.bootstrap_scripts
48
+ end
49
+
50
+ def add_bootstrap(script)
51
+ @bootstrap_scripts << script
52
+ end
53
+
54
+ def errors
55
+ errors = {}
56
+
57
+ if not @run_list.length > 0
58
+ errors[:run_list] = "is empty"
59
+ end
60
+
61
+ if not @cookbooks_path.is_a? Array
62
+ errors[:cookbooks_path] = "must be an array"
63
+ end
64
+
65
+ if not @roles_path.is_a? Array
66
+ errors[:roles_path] = "must be an array"
67
+ end
68
+
69
+ if not @data_bags_path.is_a? Array
70
+ errors[:data_bags_path] = "must be an array"
71
+ end
72
+
73
+ if errors.length > 0
74
+ errors
75
+ end
76
+ end
77
+
78
+ # provision engine
79
+
80
+ def bootstrap(server)
12
81
 
13
82
  # validate
14
83
  with_bootstrap_scripts do |script, count|
@@ -24,10 +93,10 @@ module Caterer
24
93
  with_bootstrap_scripts do |script, count|
25
94
 
26
95
  server.ui.info "Uploading #{script}..."
27
- server.ssh.upload script, "#{bootstrap_path}-#{count}"
96
+ server.ssh.upload script, "#{target_bootstrap_path}-#{count}"
28
97
 
29
- server.ssh.sudo "chown #{server.username} #{bootstrap_path}-#{count}", :stream => true
30
- server.ssh.sudo "chmod +x #{bootstrap_path}-#{count}", :stream => true
98
+ server.ssh.sudo "chown #{server.username} #{target_bootstrap_path}-#{count}", :stream => true
99
+ server.ssh.sudo "chmod +x #{target_bootstrap_path}-#{count}", :stream => true
31
100
 
32
101
  end
33
102
 
@@ -35,189 +104,162 @@ module Caterer
35
104
  with_bootstrap_scripts do |script, count|
36
105
 
37
106
  server.ui.info "Running #{script}..."
38
- server.ssh.sudo "#{bootstrap_path}-#{count}", :stream => true
107
+ server.ssh.sudo "#{target_bootstrap_path}-#{count}", :stream => true
39
108
 
40
109
  end
41
110
 
42
111
  end
43
112
 
44
- def bootstrapped?
113
+ def bootstrapped?(server)
45
114
  res = server.ssh.sudo "command -v chef-solo &>/dev/null"
46
115
  res == 0 ? true : false
47
116
  end
48
117
 
49
- def prepare
118
+ def prepare(server)
50
119
  # create base dir
51
- server.ssh.sudo "mkdir -p #{base_path}", :stream => true
52
- server.ssh.sudo "chown -R #{server.username} #{base_path}", :stream => true
120
+ server.ssh.sudo "mkdir -p #{target_base_path}", :stream => true
121
+ server.ssh.sudo "chown -R #{server.username} #{target_base_path}", :stream => true
53
122
  end
54
123
 
55
- def install
124
+ def install(server)
56
125
  server.ui.info "Preparing installation..."
57
126
 
58
127
  # upload
59
- server.ssh.upload install_script, "#{install_path}"
128
+ server.ssh.upload install_script, "#{target_install_path}"
60
129
 
61
130
  # set permissions
62
- server.ssh.sudo "chown #{server.username} #{install_path}", :stream => true
63
- server.ssh.sudo "chmod +x #{install_path}", :stream => true
131
+ server.ssh.sudo "chown #{server.username} #{target_install_path}", :stream => true
132
+ server.ssh.sudo "chmod +x #{target_install_path}", :stream => true
64
133
 
65
134
  # run
66
135
  server.ui.info "Installing chef-solo..."
67
- server.ssh.sudo "#{install_path}", :stream => true
136
+ server.ssh.sudo "#{target_install_path}", :stream => true
68
137
  end
69
138
 
70
- def provision
139
+ def provision(server)
71
140
 
72
141
  # create cookbooks directory
73
- server.ssh.sudo "mkdir -p #{cookbooks_path}", :stream => true
74
- server.ssh.sudo "chown -R #{server.username} #{cookbooks_path}", :stream => true
142
+ server.ssh.sudo "mkdir -p #{target_cookbooks_path}", :stream => true
143
+ server.ssh.sudo "chown -R #{server.username} #{target_cookbooks_path}", :stream => true
75
144
 
76
145
  # sync cookbooks
77
146
  server.ui.info "Syncing cookbooks..."
78
- config.cookbooks_path.each do |path|
79
- upload_directory path, "#{cookbooks_path}/#{Digest::MD5.hexdigest(path)}"
147
+ cookbooks_path.each do |path|
148
+ server.upload_directory path, "#{target_cookbooks_path}/#{Digest::MD5.hexdigest(path)}"
80
149
  end
81
150
 
82
151
  # create roles directory
83
- server.ssh.sudo "mkdir -p #{roles_path}", :stream => true
84
- server.ssh.sudo "chown -R #{server.username} #{roles_path}", :stream => true
152
+ server.ssh.sudo "mkdir -p #{target_roles_path}", :stream => true
153
+ server.ssh.sudo "chown -R #{server.username} #{target_roles_path}", :stream => true
85
154
 
86
155
  # sync roles
87
156
  server.ui.info "Syncing roles..."
88
- config.roles_path.each do |path|
89
- upload_directory path, roles_path
157
+ roles_path.each do |path|
158
+ server.upload_directory path, target_roles_path
90
159
  end
91
160
 
92
161
  # create data_bags directory
93
- server.ssh.sudo "mkdir -p #{data_bags_path}", :stream => true
94
- server.ssh.sudo "chown -R #{server.username} #{data_bags_path}", :stream => true
162
+ server.ssh.sudo "mkdir -p #{target_data_bags_path}", :stream => true
163
+ server.ssh.sudo "chown -R #{server.username} #{target_data_bags_path}", :stream => true
95
164
 
96
165
  # sync databags
97
166
  server.ui.info "Syncing data bags..."
98
- config.data_bags_path.each do |path|
99
- upload_directory path, data_bags_path
167
+ data_bags_path.each do |path|
168
+ server.upload_directory path, target_data_bags_path
100
169
  end
101
170
 
102
171
  # create solo.rb
103
172
  server.ui.info "Generating solo.rb..."
104
- server.ssh.upload(StringIO.new(solo_content), solo_path)
173
+ server.ssh.upload(StringIO.new(solo_content(server)), target_solo_path)
105
174
 
106
175
  # create json
107
176
  server.ui.info "Generating json config..."
108
- server.ssh.upload(StringIO.new(json_config), json_config_path)
177
+ server.ssh.upload(StringIO.new(json_config(config_data.merge(server.data))), target_json_config_path)
109
178
 
110
179
  # set permissions on everything
111
- server.ssh.sudo "chown -R #{server.username} #{base_path}", :stream => true
180
+ server.ssh.sudo "chown -R #{server.username} #{target_base_path}", :stream => true
112
181
 
113
182
  # run
114
183
  server.ui.info "Running chef-solo..."
115
184
  server.ssh.sudo command_string, :stream => true
116
185
  end
117
186
 
118
- def cleanup
187
+ def cleanup(server)
119
188
  server.ui.info "Cleaning up..."
120
189
 
121
190
  # installer
122
- server.ssh.sudo "rm -f #{install_path}", :stream => true
191
+ server.ssh.sudo "rm -f #{target_install_path}", :stream => true
123
192
 
124
193
  # bootstrap scripts
125
- server.ssh.sudo "rm -f #{bootstrap_path}*", :stream => true
194
+ server.ssh.sudo "rm -f #{target_bootstrap_path}*", :stream => true
126
195
 
127
196
  # solo.rb
128
- server.ssh.sudo "rm -f #{solo_path}", :stream => true
197
+ server.ssh.sudo "rm -f #{target_solo_path}", :stream => true
129
198
 
130
199
  # json
131
- server.ssh.sudo "rm -f #{json_config_path}", :stream => true
200
+ server.ssh.sudo "rm -f #{target_json_config_path}", :stream => true
132
201
 
133
202
  # for now, leave cookbooks, roles, and data bags for faster provisioning
134
203
  end
135
204
 
136
205
  protected
137
206
 
138
- def upload_directory(from, to)
139
- if File.exists? from
140
- if @server.can_rsync?
141
- from += "/" if not from.match /\/$/
142
- res = @server.rsync.sync(from, to)
143
- server.ui.error "rsync was unsuccessful" if res != 0
144
- else
145
- server.ui.warn "Rsync unavailable, falling back to scp (slower)..."
146
- unique = Digest::MD5.hexdigest(from)
147
- server.ssh.sudo "rm -rf #{to}/*", :stream => true
148
- server.ssh.sudo "mkdir -p #{to}/#{unique}", :stream => true
149
- server.ssh.sudo "chown -R #{server.username} #{to}/#{unique}", :stream => true
150
- server.ssh.upload from, "#{to}/#{unique}"
151
- server.ssh.sudo "mv #{to}/#{unique}/**/* #{to}/", :stream => true
152
- server.ssh.sudo "rm -rf #{to}/#{unique}", :stream => true
153
- end
154
- end
155
- end
156
-
157
207
  def with_bootstrap_scripts
158
- config.bootstrap_scripts.each_with_index do |script, index|
208
+ bootstrap_scripts.each_with_index do |script, index|
159
209
  yield script, index if block_given?
160
210
  end
161
211
  end
162
212
 
163
- def bootstrap_scripts
164
- config.bootstrap_scripts
165
- end
166
-
167
- def base_path
213
+ def target_base_path
168
214
  "/tmp/cater-chef-solo"
169
215
  end
170
216
 
171
- def install_path
172
- "#{base_path}/install"
173
- end
174
-
175
- def bootstrap_path
176
- "#{base_path}/bootstrap"
217
+ def target_install_path
218
+ "#{target_base_path}/install"
177
219
  end
178
220
 
179
- def cookbooks_path
180
- "#{base_path}/cookbooks"
221
+ def target_bootstrap_path
222
+ "#{target_base_path}/bootstrap"
181
223
  end
182
224
 
183
- def roles_path
184
- "#{base_path}/roles"
225
+ def target_cookbooks_path
226
+ "#{target_base_path}/cookbooks"
185
227
  end
186
228
 
187
- def data_bags_path
188
- "#{base_path}/data_bags"
229
+ def target_roles_path
230
+ "#{target_base_path}/roles"
189
231
  end
190
232
 
191
- def solo_path
192
- "#{base_path}/solo.rb"
233
+ def target_data_bags_path
234
+ "#{target_base_path}/data_bags"
193
235
  end
194
236
 
195
- def json_config_path
196
- "#{base_path}/config.json"
237
+ def target_solo_path
238
+ "#{target_base_path}/solo.rb"
197
239
  end
198
240
 
199
- def config_bootstrap
200
- config.bootstrap_script
241
+ def target_json_config_path
242
+ "#{target_base_path}/config.json"
201
243
  end
202
244
 
203
245
  def install_script
204
246
  File.expand_path("../../../templates/provisioner/chef_solo/bootstrap.sh", __FILE__)
205
247
  end
206
248
 
207
- def solo_content
208
- Tilt.new(File.expand_path('../../../templates/provisioner/chef_solo/solo.erb', __FILE__)).render(self)
249
+ def solo_content(server)
250
+ Tilt.new(File.expand_path('../../../templates/provisioner/chef_solo/solo.erb', __FILE__)).render(self, {:server => server})
209
251
  end
210
252
 
211
- def json_config
212
- MultiJson.dump(config_data)
253
+ def json_config(data)
254
+ MultiJson.dump(data)
213
255
  end
214
256
 
215
257
  def config_data
216
- {:run_list => config.run_list}.merge(config.json).merge(server.data)
258
+ {:run_list => run_list}.merge(json)
217
259
  end
218
260
 
219
261
  def command_string
220
- "chef-solo -c #{solo_path} -j #{json_config_path}"
262
+ "chef-solo -c #{target_solo_path} -j #{target_json_config_path}"
221
263
  end
222
264
 
223
265
  end
@@ -0,0 +1 @@
1
+ Caterer.provisioners.register(:chef_solo) { Caterer::Provisioner::ChefSolo }
@@ -15,6 +15,7 @@ module Caterer
15
15
  @key = opts[:key]
16
16
  @images = opts[:images] || []
17
17
  @data = opts[:data] || {}
18
+ @engine = opts[:engine]
18
19
 
19
20
  @logger = Log4r::Logger.new("caterer::server")
20
21
 
@@ -22,22 +23,38 @@ module Caterer
22
23
  end
23
24
 
24
25
  def bootstrap(opts={})
25
- if @images.length == 0
26
- ui.error "image list is empty, nothing to do"
27
- end
28
- @images.each do |i|
29
- ui.info "*** Bootstrapping image: #{i} ***"
30
- run_action(:bootstrap, opts.merge({:image => i}))
26
+ if @images.length > 0
27
+ @images.each do |i|
28
+ ui.info "*** Bootstrapping image: #{i} ***"
29
+ run_action(:bootstrap, opts.merge({:image => i}))
30
+ end
31
+ else
32
+ ui.info "*** Bootstrapping ***"
33
+ run_action(:bootstrap, opts.merge({:engine => @engine}))
31
34
  end
32
35
  end
33
36
 
34
37
  def provision(opts={})
35
- if @images.length == 0
36
- ui.error "image list is empty, nothing to do"
38
+ if @images.length > 0
39
+ @images.each do |i|
40
+ ui.info "*** Provisioning image: #{i} ***"
41
+ run_action(:provision, opts.merge({:image => i}))
42
+ end
43
+ else
44
+ ui.info "*** Provisioning ***"
45
+ run_action(:provision, opts.merge({:engine => @engine}))
37
46
  end
38
- @images.each do |i|
39
- ui.info "*** Provisioning image: #{i} ***"
40
- run_action(:provision, opts.merge({:image => i}))
47
+ end
48
+
49
+ def up(opts={})
50
+ if @images.length > 0
51
+ @images.each do |i|
52
+ ui.info "*** Up'ing image: #{i} ***"
53
+ run_action(:up, opts.merge({:image => i}))
54
+ end
55
+ else
56
+ ui.info "*** Up'ing ***"
57
+ run_action(:up, opts.merge({:engine => @engine}))
41
58
  end
42
59
  end
43
60
 
@@ -45,34 +62,14 @@ module Caterer
45
62
  run_action(:reboot, opts)
46
63
  end
47
64
 
48
- def up(opts={})
49
- if @images.length == 0
50
- ui.error "image list is empty, nothing to do"
51
- end
52
- @images.each do |i|
53
- ui.info "*** Up'ing image: #{i} ***"
54
- run_action(:up, opts.merge({:image => i}))
55
- end
56
- end
57
-
58
65
  def lock(opts={})
59
- if @images.length == 0
60
- ui.error "image list is empty, nothing to do"
61
- end
62
- @images.each do |i|
63
- ui.info "*** Locking image: #{i} ***"
64
- run_action(:lock, opts.merge({:image => i}))
65
- end
66
+ ui.info "*** Locking ***"
67
+ run_action(:lock, opts)
66
68
  end
67
69
 
68
70
  def unlock(opts={})
69
- if @images.length == 0
70
- ui.error "image list is empty, nothing to do"
71
- end
72
- @images.each do |i|
73
- ui.info "*** Unlocking image: #{i} ***"
74
- run_action(:unlock, opts.merge({:image => i}))
75
- end
71
+ ui.info "*** Unlocking ***"
72
+ run_action(:unlock, opts)
76
73
  end
77
74
 
78
75
  def reboot!
@@ -112,7 +109,7 @@ module Caterer
112
109
  opts[:paranoid] = false
113
110
  opts[:port] = port
114
111
  opts[:password] = password if password
115
- opts[:keys] = ["#{@key}"] if @key
112
+ opts[:keys] = [].tap {|keys| keys << @key if @key }
116
113
  end
117
114
  end
118
115
 
@@ -160,6 +157,26 @@ module Caterer
160
157
  env.action_runner.run(name, options)
161
158
  end
162
159
 
160
+ def upload_directory(from, to)
161
+ if File.exists? from
162
+ if can_rsync?
163
+ from += "/" if not from.match /\/$/
164
+ res = rsync.sync(from, to)
165
+ ui.error "rsync was unsuccessful" if res != 0
166
+ else
167
+ # yuck. Heaven help the poor soul who hath not rsync...
168
+ ui.warn "Rsync unavailable, falling back to scp (slower)..."
169
+ unique = Digest::MD5.hexdigest(from)
170
+ ssh.sudo "rm -rf #{to}/*", :stream => true
171
+ ssh.sudo "mkdir -p #{to}/#{unique}", :stream => true
172
+ ssh.sudo "chown -R #{username} #{to}/#{unique}", :stream => true
173
+ ssh.upload from, "#{to}/#{unique}"
174
+ ssh.sudo "mv #{to}/#{unique}/**/* #{to}/", :stream => true
175
+ ssh.sudo "rm -rf #{to}/#{unique}", :stream => true
176
+ end
177
+ end
178
+ end
179
+
163
180
  protected
164
181
 
165
182
  def lock_path
@@ -1,3 +1,3 @@
1
1
  module Caterer
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/caterer.rb CHANGED
@@ -23,6 +23,10 @@ module Caterer
23
23
  @commands ||= Vli::Registry.new
24
24
  end
25
25
 
26
+ def provisioners
27
+ @provisioners ||= Vli::Registry.new
28
+ end
29
+
26
30
  def config
27
31
  @config ||= Config::Base.new
28
32
  end
@@ -34,4 +38,5 @@ module Caterer
34
38
  end
35
39
 
36
40
  require 'caterer/commands'
37
- require 'caterer/actions'
41
+ require 'caterer/actions'
42
+ require 'caterer/provisioners'
@@ -1,5 +1,5 @@
1
- file_cache_path "<%= base_path %>"
1
+ file_cache_path "<%= target_base_path %>"
2
2
  node_name "<%= server.host %>"
3
- cookbook_path <%= config.cookbooks_path.map { |path| "#{cookbooks_path}/#{Digest::MD5.hexdigest(path)}" }.inspect %>
4
- data_bag_path "<%= data_bags_path %>"
5
- role_path "<%= roles_path %>"
3
+ cookbook_path <%= cookbooks_path.map { |path| "#{target_cookbooks_path}/#{Digest::MD5.hexdigest(path)}" }.inspect %>
4
+ data_bag_path "<%= target_data_bags_path %>"
5
+ role_path "<%= target_roles_path %>"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caterer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-23 00:00:00.000000000 Z
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: log4r
@@ -195,6 +195,7 @@ files:
195
195
  - lib/caterer/action/provisioner/provision.rb
196
196
  - lib/caterer/action/provisioner/validate.rb
197
197
  - lib/caterer/action/provisioner/validate/bootstrapped.rb
198
+ - lib/caterer/action/provisioner/validate/engine.rb
198
199
  - lib/caterer/action/server.rb
199
200
  - lib/caterer/action/server/lock.rb
200
201
  - lib/caterer/action/server/reboot.rb
@@ -222,13 +223,12 @@ files:
222
223
  - lib/caterer/config/group.rb
223
224
  - lib/caterer/config/image.rb
224
225
  - lib/caterer/config/member.rb
225
- - lib/caterer/config/provision.rb
226
- - lib/caterer/config/provision/chef_solo.rb
227
226
  - lib/caterer/environment.rb
228
227
  - lib/caterer/logger.rb
229
228
  - lib/caterer/provisioner.rb
230
229
  - lib/caterer/provisioner/base.rb
231
230
  - lib/caterer/provisioner/chef_solo.rb
231
+ - lib/caterer/provisioners.rb
232
232
  - lib/caterer/server.rb
233
233
  - lib/caterer/util.rb
234
234
  - lib/caterer/util/ansi_escape_code_remover.rb
@@ -1,74 +0,0 @@
1
- module Caterer
2
- module Config
3
- module Provision
4
-
5
- class ChefSolo
6
-
7
- attr_reader :name, :run_list
8
- attr_accessor :json, :cookbooks_path, :roles_path, :data_bags_path, :bootstrap_scripts
9
-
10
- def initialize(name)
11
- @name = name
12
- @run_list = []
13
- @json = {}
14
- @cookbooks_path = ['cookbooks']
15
- @roles_path = ['roles']
16
- @data_bags_path = ['data_bags']
17
- @bootstrap_scripts = []
18
- end
19
-
20
- def add_recipe(recipe)
21
- @run_list << "recipe[#{recipe}]"
22
- end
23
-
24
- def add_role(role)
25
- @run_list << "role[#{role}]"
26
- end
27
-
28
- def add_image(image)
29
- image = Caterer.config.images[image]
30
- raise "Unknown image :#{image}" if not image
31
-
32
- provisioner = image.provisioner
33
- raise "No provisioner for :#{image}" if not provisioner
34
-
35
- if not provisioner.name == :chef_solo
36
- raise "Incompatible provisioner. Must be :#{provisioner.name}"
37
- end
38
-
39
- @run_list += provisioner.run_list
40
- @bootstrap_scripts += provisioner.bootstrap_scripts
41
- end
42
-
43
- def add_bootstrap(script)
44
- @bootstrap_scripts << script
45
- end
46
-
47
- def errors
48
- errors = {}
49
-
50
- if not @run_list.length > 0
51
- errors[:run_list] = "is empty"
52
- end
53
-
54
- if not @cookbooks_path.is_a? Array
55
- errors[:cookbooks_path] = "must be an array"
56
- end
57
-
58
- if not @roles_path.is_a? Array
59
- errors[:roles_path] = "must be an array"
60
- end
61
-
62
- if not @data_bags_path.is_a? Array
63
- errors[:data_bags_path] = "must be an array"
64
- end
65
-
66
- if errors.length > 0
67
- errors
68
- end
69
- end
70
-
71
- end
72
- end
73
- end
74
- end
@@ -1,7 +0,0 @@
1
- module Caterer
2
- module Config
3
- module Provision
4
- autoload :ChefSolo, 'caterer/config/provision/chef_solo'
5
- end
6
- end
7
- end