knife-stackbuilder 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,379 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ module StackBuilder::Common
4
+
5
+ module Helpers
6
+
7
+ #
8
+ # Returns whether platform is a nix OS
9
+ #
10
+ def is_nix_os
11
+ return RbConfig::CONFIG["host_os"] =~ /linux|freebsd|darwin|unix/
12
+ end
13
+
14
+ #
15
+ # Runs the given execution list asynchronously if fork is supported
16
+ #
17
+ def exec_forked(exec_list)
18
+
19
+ if is_nix_os
20
+ p = []
21
+ exec_list.each do |data|
22
+ p << fork {
23
+ yield(data)
24
+ }
25
+ end
26
+ p.each { |pid| Process.waitpid(pid) }
27
+ else
28
+ exec_list.each do |data|
29
+ yield(data)
30
+ printf("\n")
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ #
37
+ # Loads a Yaml file and resolves any includes
38
+ #
39
+ def load_yaml(file, env_vars, my = nil)
40
+
41
+ yaml = YAML.load_file(file)
42
+ eval_map_values(yaml, env_vars, file, my)
43
+ end
44
+
45
+ #
46
+ # Evaluates map values against the
47
+ # given map of environment variables
48
+ #
49
+ def eval_map_values(v, env, file = nil, my = nil)
50
+
51
+ my ||= v
52
+
53
+ if v.is_a?(String)
54
+
55
+ if v=~/#\{.*\}/
56
+ begin
57
+ return eval("\"#{v.gsub(/\"/, "\\\"")}\"")
58
+ rescue
59
+ return v
60
+ end
61
+
62
+ elsif v.start_with?('<<')
63
+
64
+ k1 = v[/<<(\w*)[\*\$\+]/,1]
65
+ env_val = k1.nil? || k1.empty? ? nil : ENV[k1]
66
+
67
+ i = k1.length + 3
68
+ k2 = v[i,v.length-i]
69
+
70
+ case v[i-1]
71
+
72
+ when '*'
73
+ if env_val.nil?
74
+ v = ask(k2) { |q| q.echo = "*" }.to_s
75
+ ENV[k1] = v unless k1.nil? || k1.empty?
76
+ else
77
+ v = env_val
78
+ end
79
+ return v
80
+
81
+ when '$'
82
+ if env_val.nil?
83
+ v = ask(k2).to_s
84
+ ENV[k1] = v unless k1.nil? || k1.empty?
85
+ else
86
+ v = env_val
87
+ end
88
+ return v
89
+
90
+ when '+'
91
+ lookup_keys = (env_val || k2).split(/[\[\]]/).reject { |k| k.empty? }
92
+
93
+ key = lookup_keys.shift
94
+ include_file = key.start_with?('/') || key.nil? ? key : File.expand_path('../' + key, file)
95
+
96
+ begin
97
+ yaml = load_yaml(include_file, env, my)
98
+
99
+ return lookup_keys.empty? ? yaml
100
+ : eval('yaml' + lookup_keys.collect { |v| "['#{v}']" }.join)
101
+
102
+ rescue Exception => msg
103
+ puts "ERROR: Unable to include referenced data '#{v}'."
104
+ raise msg
105
+ end
106
+ else
107
+ return v
108
+ end
109
+ end
110
+
111
+ elsif v.is_a?(Hash)
112
+
113
+ new_keys = { }
114
+ v.each_pair do |k,vv|
115
+
116
+ if k=~/#\{.*\}/
117
+
118
+ new_k = eval("\"#{k}\"")
119
+ if k!=new_k
120
+ v.delete(k)
121
+ new_keys[new_k] = eval_map_values(vv, env, file, my)
122
+ next
123
+ end
124
+ end
125
+
126
+ v[k] = eval_map_values(vv, env, file, my)
127
+ end
128
+ v.merge!(new_keys) unless new_keys.empty?
129
+
130
+ elsif v.is_a?(Array)
131
+ v.map! { |vv| eval_map_values(vv, env, file, my) }
132
+ end
133
+
134
+ v
135
+ end
136
+
137
+ #
138
+ # Merges values of keys of to maps
139
+ #
140
+ def merge_maps(map1, map2)
141
+
142
+ map2.each_pair do |k, v2|
143
+
144
+ v1 = map1[k]
145
+ if v1.nil?
146
+ if v2.is_a?(Hash)
147
+ v1 = { }
148
+ merge_maps(v1, v2)
149
+ elsif v2.is_a?(Array)
150
+ v1 = [ ] + v2
151
+ else
152
+ v1 = v2
153
+ end
154
+ map1[k] = v1
155
+ elsif v1.is_a?(Hash) && v2.is_a?(Hash)
156
+ merge_maps(v1, v2)
157
+ elsif v1.is_a?(Array) && v2.is_a?(Array)
158
+
159
+ if v2.size > 0
160
+
161
+ if v1.size > 0 && v1[0].is_a?(Hash) && v2[0].is_a?(Hash)
162
+
163
+ i = 0
164
+ while i < [ v1.size, v2.size ].max do
165
+
166
+ v1[i] = { } if v1[i].nil?
167
+ v2[i] = { } if v2[i].nil?
168
+ merge_maps(v1[i], v2[i])
169
+
170
+ i += 1
171
+ end
172
+
173
+ else
174
+ v1 += v2
175
+ end
176
+ end
177
+ else
178
+ map1[k] = v2
179
+ end
180
+ end
181
+ end
182
+
183
+ #
184
+ # Prints a 2-d array within a table formatted to terminal size
185
+ #
186
+ def print_table(table, format = true, cols = nil)
187
+
188
+ # Apply column filter
189
+ unless cols.nil?
190
+
191
+ headings = cols.split(",").to_set
192
+
193
+ cols_to_delete = [ ]
194
+ i = 0
195
+ table[0].each do |col|
196
+ cols_to_delete << i unless headings.include?(col)
197
+ i = i + 1
198
+ end
199
+
200
+ table.each do |row|
201
+ cols_to_delete.reverse_each { |j| row.delete_at(j) }
202
+ end
203
+ end
204
+
205
+ if format
206
+
207
+ # Calculate widths
208
+ widths = []
209
+ table.each do |line|
210
+ c = 0
211
+ line.each do |col|
212
+
213
+ len = col.nil? ? 0 : col.length
214
+ widths[c] = (widths[c] && widths[c] > len) ? widths[c] : len
215
+ c += 1
216
+ end
217
+ end
218
+
219
+ max_scr_cols = HighLine::SystemExtensions.terminal_size[0].nil? ? 9999
220
+ : HighLine::SystemExtensions.terminal_size[0] - (widths.length * 3) - 2
221
+
222
+ max_tbl_cols = 0
223
+ width_map = {}
224
+
225
+ c = 0
226
+ widths.each do |n|
227
+ max_tbl_cols += n
228
+ width_map[c] = n
229
+ c += 1
230
+ end
231
+ c = nil
232
+
233
+ # Shrink columns that have too much space to try and fit table into visible console
234
+ if max_tbl_cols > max_scr_cols
235
+
236
+ width_map = width_map.sort_by { |col,width| -width }
237
+
238
+ last_col = widths.length - 1
239
+ c = 0
240
+
241
+ while max_tbl_cols > max_scr_cols && c < last_col
242
+
243
+ while width_map[c][1] > width_map[c + 1][1]
244
+
245
+ i = c
246
+ while i >= 0
247
+ width_map[i][1] -= 1
248
+ widths[width_map[i][0]] -= 1
249
+ max_tbl_cols -= 1
250
+ i -= 1
251
+
252
+ break if max_tbl_cols == max_scr_cols
253
+ end
254
+ break if max_tbl_cols == max_scr_cols
255
+ end
256
+ c += 1
257
+ end
258
+ end
259
+
260
+ border1 = ""
261
+ border2 = ""
262
+ format = ""
263
+ widths.each do |n|
264
+
265
+ border1 += "+#{'-' * (n + 2)}"
266
+ border2 += "+#{'=' * (n + 2)}"
267
+ format += "| %#{n}s "
268
+ end
269
+ border1 += "+\n"
270
+ border2 += "+\n"
271
+ format += "|\n"
272
+
273
+ else
274
+ c = nil
275
+ border1 = nil
276
+ border2 = nil
277
+
278
+ format = Array.new(table[0].size, "%s,").join.chop! + "\n"
279
+
280
+ # Delete column headings for unformatted output
281
+ table.delete_at(0)
282
+ end
283
+
284
+ # Print each line.
285
+ write_header_border = !border2.nil?
286
+ printf border1 if border1
287
+ table.each do |line|
288
+
289
+ if c
290
+ # Check if cell needs to be truncated
291
+ i = 0
292
+ while i < c
293
+
294
+ j = width_map[i][0]
295
+ width = width_map[i][1]
296
+
297
+ cell = line[j]
298
+ len = cell.length
299
+ if len > width
300
+ line[j] = cell[0, width - 2] + ".."
301
+ end
302
+ i += 1
303
+ end
304
+ end
305
+
306
+ printf format, *line
307
+ if write_header_border
308
+ printf border2
309
+ write_header_border = false
310
+ end
311
+ end
312
+ printf border1 if border1
313
+
314
+ end
315
+
316
+ #
317
+ # Helper command to rin Chef knife
318
+ #
319
+ def run_knife(knife_cmd, retries = 0, output = StringIO.new, error = StringIO.new)
320
+
321
+ knife_cmd.ui = Chef::Knife::UI.new(output, error, STDIN, knife_cmd.config) \
322
+ unless output.nil? && error.nil?
323
+
324
+ run = true
325
+ while run
326
+
327
+ begin
328
+ knife_cmd.run
329
+ run = false
330
+
331
+ rescue Exception => msg
332
+
333
+ if retries==0
334
+
335
+ if @logger.level>=::Logger::WARN
336
+ puts "Knife execution failed with an error."
337
+ puts "* StdOut from knife run: #{output.string}"
338
+ puts "* StdErr from knife run: #{error.string}"
339
+ end
340
+
341
+ @logger.debug(msg.backtrace.join("\n\t")) if Config.logger.debug?
342
+ raise msg
343
+ end
344
+
345
+ @logger.debug("Knife command #{knife_cmd} failed. Retrying after 2s.")
346
+
347
+ sleep 2
348
+ retries -= 1
349
+ end
350
+ end
351
+
352
+ output.string
353
+ end
354
+
355
+ #
356
+ # Captures standard out and error to a string
357
+ #
358
+ def capture_stdout
359
+ # The output stream must be an IO-like object. In this case we capture it in
360
+ # an in-memory IO object so we can return the string value. You can assign any
361
+ # IO object here.
362
+ stdout = StringIO.new
363
+ previous_stdout, $stdout = $stdout, stdout
364
+ previous_stderr, $stderr = $stderr, stdout
365
+ yield
366
+ stdout.string
367
+
368
+ rescue Exception => msg
369
+ puts("Error: #{stdout.string}")
370
+ raise msg
371
+
372
+ ensure
373
+ # Restore the previous value of stderr (typically equal to STDERR).
374
+ $stdout = previous_stdout
375
+ $stderr = previous_stderr
376
+ end
377
+
378
+ end
379
+ end
@@ -0,0 +1,54 @@
1
+ # From https://gist.github.com/pettyjamesm/3746457
2
+
3
+ require 'monitor'
4
+
5
+ module StackBuilder::Common
6
+
7
+ class Semaphore
8
+
9
+ def initialize(maxval = nil)
10
+
11
+ maxval = maxval.to_i unless maxval.nil?
12
+ raise ArgumentError, "Semaphores must use a positive maximum " +
13
+ "value or have no maximum!" if maxval and maxval <= 0
14
+
15
+ @max = maxval || -1
16
+ @count = 0
17
+ @mon = Monitor.new
18
+ @dwait = @mon.new_cond
19
+ @uwait = @mon.new_cond
20
+ end
21
+
22
+ def count; @mon.synchronize { @count } end
23
+
24
+ def up!(number = 1)
25
+ if (number > 1)
26
+ number.times { up!(1) }
27
+ count
28
+ else
29
+ @mon.synchronize do
30
+ @uwait.wait while @max > 0 and @count == @max
31
+ @dwait.signal if @count == 0
32
+ @count += 1
33
+ end
34
+ end
35
+ end
36
+
37
+ def down!(number = 1)
38
+ if (number > 1)
39
+ number.times { down!(1) }
40
+ count
41
+ else
42
+ @mon.synchronize do
43
+ @dwait.wait while @count == 0
44
+ @uwait.signal if @count == @max
45
+ @count -= 1
46
+ end
47
+ end
48
+ end
49
+
50
+ alias_method :wait, :down!
51
+ alias_method :signal, :up!
52
+ end
53
+
54
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ module StackBuilder::Common
4
+
5
+ #
6
+ # Sends data written to an IO object to multiple outputs.
7
+ #
8
+ class TeeIO < IO
9
+
10
+ def initialize(output = nil)
11
+ @string_io = StringIO.new
12
+ @output = output
13
+ end
14
+
15
+ def tty?
16
+ return false
17
+ end
18
+
19
+ def write(string)
20
+ @string_io.write(string)
21
+ @output.write(string) unless @output.nil?
22
+ end
23
+
24
+ def string
25
+ @string_io.string
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ include StackBuilder::Common::Helpers
4
+
5
+ module StackBuilder::Stack
6
+
7
+ class NodeManager
8
+
9
+ def get_name
10
+ raise NotImplemented, 'NodeManager.get_name'
11
+ end
12
+
13
+ def get_scale
14
+ @scale.nil? ? 0 : @scale
15
+ end
16
+
17
+ def set_scale(scale)
18
+ @scale = scale
19
+ end
20
+
21
+ def node_attributes
22
+ raise NotImplemented, 'NodeManager.node_attributes'
23
+ end
24
+
25
+ def create(index)
26
+ raise NotImplemented, 'NodeManager.create'
27
+ end
28
+
29
+ def process(index, events, attributes, target = nil)
30
+ raise NotImplemented, 'NodeManager.process'
31
+ end
32
+
33
+ def delete(index)
34
+ raise NotImplemented, 'NodeManager.delete'
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ include StackBuilder::Common::Helpers
4
+
5
+ module StackBuilder::Stack
6
+
7
+ class NodeProvider
8
+
9
+ def set_stack(stack, id)
10
+ raise NotImplemented, 'NodeProvider.set_stack_id'
11
+ end
12
+
13
+ def get_env_vars
14
+ return { }
15
+ end
16
+
17
+ def get_node_manager(node_config)
18
+ raise NotImplemented, 'NodeProvider.get_node_manager'
19
+ end
20
+ end
21
+ end