jamie 0.1.0.beta3 → 0.1.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -1
- data/Rakefile +12 -4
- data/jamie.gemspec +1 -0
- data/lib/jamie.rb +24 -22
- data/lib/jamie/cli.rb +15 -15
- data/lib/jamie/driver/dummy.rb +11 -3
- data/lib/jamie/thor_tasks.rb +1 -1
- data/lib/jamie/version.rb +1 -1
- data/spec/jamie/config/collection_spec.rb +77 -0
- data/spec/jamie/config_spec.rb +226 -0
- data/spec/jamie/instance_spec.rb +159 -0
- data/spec/jamie/platform_spec.rb +45 -0
- data/spec/jamie/suite_spec.rb +57 -0
- data/spec/spec_helper.rb +45 -0
- metadata +30 -4
- data/spec/jamie_spec.rb +0 -512
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -7,7 +7,7 @@ Rake::TestTask.new do |t|
|
|
7
7
|
t.verbose = true
|
8
8
|
end
|
9
9
|
|
10
|
-
task :default => [
|
10
|
+
task :default => [:test]
|
11
11
|
|
12
12
|
unless RUBY_ENGINE == 'jruby'
|
13
13
|
require 'cane/rake_task'
|
@@ -29,9 +29,17 @@ unless RUBY_ENGINE == 'jruby'
|
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
|
-
Tailor::RakeTask.new
|
32
|
+
Tailor::RakeTask.new do |task|
|
33
|
+
task.file_set('bin/*', 'binaries')
|
34
|
+
task.file_set('lib/**/*.rb', 'code') do |style|
|
35
|
+
# TODO: Tailor is confused thinking `module Jamie` is a class. Until
|
36
|
+
# the classes are split in seperate files, let's punt on this
|
37
|
+
style.max_code_lines_in_class 1550, level: :warn
|
38
|
+
end
|
39
|
+
task.file_set('spec/**/*.rb', 'tests')
|
40
|
+
end
|
33
41
|
|
34
|
-
Rake::Task[:default].enhance [
|
42
|
+
Rake::Task[:default].enhance [:cane, :tailor]
|
35
43
|
end
|
36
44
|
|
37
45
|
desc "Display LOC stats"
|
@@ -42,4 +50,4 @@ task :stats do
|
|
42
50
|
sh "countloc -r spec"
|
43
51
|
end
|
44
52
|
|
45
|
-
Rake::Task[:default].enhance [
|
53
|
+
Rake::Task[:default].enhance [:stats]
|
data/jamie.gemspec
CHANGED
data/lib/jamie.rb
CHANGED
@@ -164,7 +164,7 @@ module Jamie
|
|
164
164
|
def log_level
|
165
165
|
@log_level ||= begin
|
166
166
|
ENV['JAMIE_LOG'] && ENV['JAMIE_LOG'].downcase.to_sym ||
|
167
|
-
|
167
|
+
Jamie::DEFAULT_LOG_LEVEL
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -286,7 +286,7 @@ module Jamie
|
|
286
286
|
def platform_driver_hash(platform_name)
|
287
287
|
h = yaml[:platforms].find { |p| p[:name] == platform_name } || Hash.new
|
288
288
|
|
289
|
-
h.select { |key, value| [
|
289
|
+
h.select { |key, value| [:driver_plugin, :driver_config].include?(key) }
|
290
290
|
end
|
291
291
|
|
292
292
|
def new_instance_logger(index)
|
@@ -355,7 +355,7 @@ module Jamie
|
|
355
355
|
|
356
356
|
def common_driver_hash
|
357
357
|
yaml.select do |key, value|
|
358
|
-
[
|
358
|
+
[:driver_plugin, :driver_config].include?(key)
|
359
359
|
end
|
360
360
|
end
|
361
361
|
end
|
@@ -374,7 +374,7 @@ module Jamie
|
|
374
374
|
|
375
375
|
COLORS = %w(
|
376
376
|
cyan yellow green magenta red blue bright_cyan bright_yellow
|
377
|
-
bright_green bright_magenta bright_red
|
377
|
+
bright_green bright_magenta bright_red bright_blue
|
378
378
|
).freeze
|
379
379
|
|
380
380
|
def self.escape(name)
|
@@ -563,7 +563,7 @@ module Jamie
|
|
563
563
|
private
|
564
564
|
|
565
565
|
def validate_options(opts)
|
566
|
-
[
|
566
|
+
[:name, :run_list].each do |k|
|
567
567
|
raise ClientError, "Suite#new requires option :#{k}" if opts[k].nil?
|
568
568
|
end
|
569
569
|
end
|
@@ -604,7 +604,7 @@ module Jamie
|
|
604
604
|
private
|
605
605
|
|
606
606
|
def validate_options(opts)
|
607
|
-
[
|
607
|
+
[:name].each do |k|
|
608
608
|
raise ClientError, "Platform#new requires option :#{k}" if opts[k].nil?
|
609
609
|
end
|
610
610
|
end
|
@@ -792,7 +792,7 @@ module Jamie
|
|
792
792
|
private
|
793
793
|
|
794
794
|
def validate_options(opts)
|
795
|
-
[
|
795
|
+
[:suite, :platform, :driver, :jr, :logger].each do |k|
|
796
796
|
raise ClientError, "Instance#new requires option :#{k}" if opts[k].nil?
|
797
797
|
end
|
798
798
|
end
|
@@ -838,7 +838,7 @@ module Jamie
|
|
838
838
|
banner "#{output_verb} #{to_str}"
|
839
839
|
elapsed = action(verb) { |state| driver.public_send(verb, state) }
|
840
840
|
info("Finished #{output_verb.downcase} #{to_str}" +
|
841
|
-
|
841
|
+
" #{Util.duration(elapsed.real)}.")
|
842
842
|
yield if block_given?
|
843
843
|
Actor.current
|
844
844
|
end
|
@@ -927,7 +927,7 @@ module Jamie
|
|
927
927
|
|
928
928
|
private
|
929
929
|
|
930
|
-
TRANSITIONS = [
|
930
|
+
TRANSITIONS = [:destroy, :create, :converge, :setup, :verify]
|
931
931
|
|
932
932
|
def self.index(transition)
|
933
933
|
if transition.nil?
|
@@ -953,7 +953,7 @@ module Jamie
|
|
953
953
|
# @param [Hash] opts optional configuration
|
954
954
|
# @option opts [TrueClass, FalseClass] :use_sudo whether or not to invoke
|
955
955
|
# sudo before commands requiring root access (default: `true`)
|
956
|
-
def initialize(suite_name, opts = {:use_sudo => true})
|
956
|
+
def initialize(suite_name, opts = { :use_sudo => true })
|
957
957
|
validate_options(suite_name)
|
958
958
|
|
959
959
|
@suite_name = suite_name
|
@@ -972,7 +972,7 @@ module Jamie
|
|
972
972
|
@setup_cmd ||= if local_suite_files.empty?
|
973
973
|
nil
|
974
974
|
else
|
975
|
-
<<-INSTALL_CMD.gsub(
|
975
|
+
<<-INSTALL_CMD.gsub(/^ {10}/, '')
|
976
976
|
#{sudo}#{ruby_bin} -e "$(cat <<"EOF"
|
977
977
|
#{install_script}
|
978
978
|
EOF
|
@@ -994,7 +994,7 @@ module Jamie
|
|
994
994
|
@sync_cmd ||= if local_suite_files.empty?
|
995
995
|
nil
|
996
996
|
else
|
997
|
-
<<-INSTALL_CMD.gsub(
|
997
|
+
<<-INSTALL_CMD.gsub(/^ {10}/, '')
|
998
998
|
#{sudo}#{jr_bin} cleanup-suites
|
999
999
|
#{local_suite_files.map { |f| stream_file(f, remote_file(f)) }.join}
|
1000
1000
|
INSTALL_CMD
|
@@ -1053,7 +1053,7 @@ module Jamie
|
|
1053
1053
|
def stream_file(local_path, remote_path)
|
1054
1054
|
local_file = IO.read(local_path)
|
1055
1055
|
md5 = Digest::MD5.hexdigest(local_file)
|
1056
|
-
perms = sprintf("%o", File.stat(local_path).mode)[3,3]
|
1056
|
+
perms = sprintf("%o", File.stat(local_path).mode)[3, 3]
|
1057
1057
|
jr_stream_file = "#{jr_bin} stream-file #{remote_path} #{md5} #{perms}"
|
1058
1058
|
|
1059
1059
|
<<-STREAMFILE.gsub(/^ {8}/, '')
|
@@ -1115,7 +1115,7 @@ module Jamie
|
|
1115
1115
|
|
1116
1116
|
def self.symbolized_hash(obj)
|
1117
1117
|
if obj.is_a?(Hash)
|
1118
|
-
obj.inject({}) { |h, (k,v)| h[k.to_sym] = symbolized_hash(v) ; h }
|
1118
|
+
obj.inject({}) { |h, (k, v)| h[k.to_sym] = symbolized_hash(v) ; h }
|
1119
1119
|
elsif obj.is_a?(Array)
|
1120
1120
|
obj.inject([]) { |a, v| a << symbolized_hash(v) ; a }
|
1121
1121
|
else
|
@@ -1126,7 +1126,7 @@ module Jamie
|
|
1126
1126
|
def self.duration(total)
|
1127
1127
|
minutes = (total / 60).to_i
|
1128
1128
|
seconds = (total - (minutes * 60))
|
1129
|
-
"(%dm%.2fs)" % [
|
1129
|
+
"(%dm%.2fs)" % [minutes, seconds]
|
1130
1130
|
end
|
1131
1131
|
end
|
1132
1132
|
|
@@ -1316,7 +1316,7 @@ module Jamie
|
|
1316
1316
|
end
|
1317
1317
|
end
|
1318
1318
|
end
|
1319
|
-
@validations << [
|
1319
|
+
@validations << [attr, block]
|
1320
1320
|
end
|
1321
1321
|
|
1322
1322
|
def self.no_parallel_for(*methods)
|
@@ -1391,7 +1391,7 @@ module Jamie
|
|
1391
1391
|
opts[:password] = config[:password] if config[:password]
|
1392
1392
|
opts[:keys] = Array(config[:ssh_key]) if config[:ssh_key]
|
1393
1393
|
|
1394
|
-
[
|
1394
|
+
[state[:hostname], config[:username], opts]
|
1395
1395
|
end
|
1396
1396
|
|
1397
1397
|
def chef_home
|
@@ -1475,7 +1475,7 @@ module Jamie
|
|
1475
1475
|
socket = TCPSocket.new(hostname, config[:port])
|
1476
1476
|
IO.select([socket], nil, nil, 5)
|
1477
1477
|
rescue SocketError, Errno::ECONNREFUSED,
|
1478
|
-
|
1478
|
+
Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
|
1479
1479
|
sleep 2
|
1480
1480
|
false
|
1481
1481
|
rescue Errno::EPERM, Errno::ETIMEDOUT
|
@@ -1546,8 +1546,9 @@ module Jamie
|
|
1546
1546
|
end
|
1547
1547
|
|
1548
1548
|
def upload_path(scp, path, dir = File.basename(path))
|
1549
|
-
|
1550
|
-
|
1549
|
+
dest = "#{chef_home}/#{dir}"
|
1550
|
+
|
1551
|
+
scp.upload!(path, dest, :recursive => true) do |ch, name, sent, total|
|
1551
1552
|
if sent == total
|
1552
1553
|
info("Uploaded #{name.sub(%r{^#{path}/}, '')} (#{total} bytes)")
|
1553
1554
|
end
|
@@ -1593,7 +1594,8 @@ module Jamie
|
|
1593
1594
|
fatal("#{name} must be installed, add it to your Gemfile.")
|
1594
1595
|
raise UserError, "#{bin} command not found"
|
1595
1596
|
end
|
1596
|
-
|
1597
|
+
|
1598
|
+
Jamie.mutex.synchronize { run_command "#{bin} install --path #{tmpdir}" }
|
1597
1599
|
end
|
1598
1600
|
|
1599
1601
|
def cp_cookbooks(tmpdir)
|
@@ -1628,7 +1630,7 @@ module Jamie
|
|
1628
1630
|
# attributes or nil values if they could not be determined
|
1629
1631
|
def self.extract(metadata_file)
|
1630
1632
|
mc = new(File.expand_path(metadata_file))
|
1631
|
-
[
|
1633
|
+
[mc[:name], mc[:version]]
|
1632
1634
|
end
|
1633
1635
|
|
1634
1636
|
# Creates a new instances and loads in the contents of the metdata.rb
|
data/lib/jamie/cli.rb
CHANGED
@@ -47,10 +47,10 @@ module Jamie
|
|
47
47
|
def list(*args)
|
48
48
|
result = parse_subcommand(args.first)
|
49
49
|
if options[:bare]
|
50
|
-
say Array(result).map{ |i| i.name }.join("\n")
|
50
|
+
say Array(result).map { |i| i.name }.join("\n")
|
51
51
|
else
|
52
52
|
table = [
|
53
|
-
[
|
53
|
+
[set_color("Instance", :green), set_color("Last Action", :green)]
|
54
54
|
]
|
55
55
|
table += Array(result).map { |i| display_instance(i) }
|
56
56
|
print_table(table)
|
@@ -107,7 +107,7 @@ module Jamie
|
|
107
107
|
results = get_filtered_instances(regexp)
|
108
108
|
if results.size > 1
|
109
109
|
die task, "Argument `#{regexp}' returned multiple results:\n" +
|
110
|
-
results.map{ |i| " * #{i.name}" }.join("\n")
|
110
|
+
results.map { |i| " * #{i.name}" }.join("\n")
|
111
111
|
end
|
112
112
|
instance = results.pop
|
113
113
|
|
@@ -210,7 +210,7 @@ module Jamie
|
|
210
210
|
when nil then set_color("<Not Created>", :red)
|
211
211
|
else set_color("<Unknown>", :white)
|
212
212
|
end
|
213
|
-
[
|
213
|
+
[set_color(instance.name, :white), action]
|
214
214
|
end
|
215
215
|
|
216
216
|
def die(task, msg)
|
@@ -220,18 +220,19 @@ module Jamie
|
|
220
220
|
end
|
221
221
|
|
222
222
|
def pry_prompts
|
223
|
-
[
|
224
|
-
|
223
|
+
[
|
224
|
+
proc { |target_self, nest_level, pry|
|
225
|
+
["[#{pry.input_array.size}] ",
|
225
226
|
"jc(#{Pry.view_clip(target_self.class)})",
|
226
227
|
"#{":#{nest_level}" unless nest_level.zero?}> "
|
227
228
|
].join
|
228
229
|
},
|
229
230
|
proc { |target_self, nest_level, pry|
|
230
|
-
[
|
231
|
+
["[#{pry.input_array.size}] ",
|
231
232
|
"jc(#{Pry.view_clip(target_self.class)})",
|
232
233
|
"#{":#{nest_level}" unless nest_level.zero?}* "
|
233
234
|
].join
|
234
|
-
}
|
235
|
+
},
|
235
236
|
]
|
236
237
|
end
|
237
238
|
end
|
@@ -265,7 +266,7 @@ module Jamie
|
|
265
266
|
puts ">>>>> Jamie gem not loaded, omitting tasks" unless ENV['CI']
|
266
267
|
end
|
267
268
|
THOR
|
268
|
-
empty_directory "test/integration/
|
269
|
+
empty_directory "test/integration/default" if init_test_dir?
|
269
270
|
append_to_gitignore(".jamie/")
|
270
271
|
append_to_gitignore(".jamie.local.yml")
|
271
272
|
add_plugins
|
@@ -277,7 +278,7 @@ module Jamie
|
|
277
278
|
url_base = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes"
|
278
279
|
platforms = [
|
279
280
|
{ :n => 'ubuntu', :vers => %w(12.04 10.04), :rl => "recipe[apt]" },
|
280
|
-
{ :n => 'centos', :vers => %w(6.3 5.8), :rl => "recipe[yum::epel]" }
|
281
|
+
{ :n => 'centos', :vers => %w(6.3 5.8), :rl => "recipe[yum::epel]" },
|
281
282
|
]
|
282
283
|
platforms = platforms.map do |p|
|
283
284
|
p[:vers].map do |v|
|
@@ -296,15 +297,14 @@ module Jamie
|
|
296
297
|
nil
|
297
298
|
end
|
298
299
|
run_list = cookbook_name ? "recipe[#{cookbook_name}]" : nil
|
299
|
-
attributes = cookbook_name ? { cookbook_name => nil } : nil
|
300
300
|
|
301
301
|
{ 'driver_plugin' => 'vagrant',
|
302
302
|
'platforms' => platforms,
|
303
303
|
'suites' => [
|
304
|
-
{ 'name' => '
|
304
|
+
{ 'name' => 'default',
|
305
305
|
'run_list' => Array(run_list),
|
306
|
-
'attributes' =>
|
307
|
-
}
|
306
|
+
'attributes' => Hash.new
|
307
|
+
},
|
308
308
|
]
|
309
309
|
}.to_yaml
|
310
310
|
end
|
@@ -345,7 +345,7 @@ module Jamie
|
|
345
345
|
|
346
346
|
def list_plugins
|
347
347
|
specs = fetch_gem_specs.map { |t| t.first }.map { |t| t[0, 2] }.
|
348
|
-
sort { |x,y| x[0] <=> y[0] }
|
348
|
+
sort { |x, y| x[0] <=> y[0] }
|
349
349
|
specs = specs[0, 49].push(["...", "..."]) if specs.size > 49
|
350
350
|
specs = specs.unshift(["Gem Name", "Latest Stable Release"])
|
351
351
|
print_table(specs, :indent => 4)
|
data/lib/jamie/driver/dummy.rb
CHANGED
@@ -55,14 +55,22 @@ module Jamie
|
|
55
55
|
private
|
56
56
|
|
57
57
|
def report(action, state)
|
58
|
-
|
59
|
-
|
58
|
+
what = action.capitalize
|
59
|
+
info("[Dummy] #{what} on instance=#{instance} with state=#{state}")
|
60
|
+
sleep_if_set
|
61
|
+
random_failure_if_set
|
62
|
+
debug("[Dummy] #{what} completed (#{config[:sleep]}s).")
|
63
|
+
end
|
64
|
+
|
65
|
+
def sleep_if_set
|
60
66
|
sleep(config[:sleep].to_f) if config[:sleep].to_f > 0.0
|
67
|
+
end
|
68
|
+
|
69
|
+
def random_failure_if_set
|
61
70
|
if config[:random_failure] && [true, false].sample
|
62
71
|
debug("[Dummy] Random failure for action ##{action}.")
|
63
72
|
raise ActionFailed, "Action ##{action} failed for #{instance.to_str}."
|
64
73
|
end
|
65
|
-
debug("[Dummy] Action ##{action} completed (#{config[:sleep]}s).")
|
66
74
|
end
|
67
75
|
end
|
68
76
|
end
|
data/lib/jamie/thor_tasks.rb
CHANGED
data/lib/jamie/version.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require_relative '../../spec_helper'
|
20
|
+
|
21
|
+
describe Jamie::Config::Collection do
|
22
|
+
|
23
|
+
let(:collection) do
|
24
|
+
Jamie::Config::Collection.new([
|
25
|
+
obj('one'), obj('two', 'a'), obj('two', 'b'), obj('three')
|
26
|
+
])
|
27
|
+
end
|
28
|
+
|
29
|
+
it "transparently wraps an Array" do
|
30
|
+
collection.must_be_instance_of Array
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#get" do
|
34
|
+
|
35
|
+
it "returns a single object by its name" do
|
36
|
+
collection.get('three').must_equal obj('three')
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns the first occurance of an object by its name" do
|
40
|
+
collection.get('two').must_equal obj('two', 'a')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns nil if an object cannot be found by its name" do
|
44
|
+
collection.get('nope').must_be_nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#get_all" do
|
49
|
+
|
50
|
+
it "returns a Collection of objects whose name matches the regex" do
|
51
|
+
result = collection.get_all(/(one|three)/)
|
52
|
+
result.size.must_equal 2
|
53
|
+
result[0].must_equal obj('one')
|
54
|
+
result[1].must_equal obj('three')
|
55
|
+
result.get_all(/one/).size.must_equal 1
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns an empty Collection if on matches are found" do
|
59
|
+
result = collection.get_all(/noppa/)
|
60
|
+
result.must_equal []
|
61
|
+
result.get("nahuh").must_be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#as_name" do
|
66
|
+
|
67
|
+
it "returns an Array of names as strings" do
|
68
|
+
collection.as_names.must_equal %w{one two two three}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def obj(name, extra = nil)
|
75
|
+
OpenStruct.new(:name => name, :extra => extra)
|
76
|
+
end
|
77
|
+
end
|