bosh_common 0.5.4 → 1.5.0.pre.1100
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/common/common.rb +77 -0
- data/lib/common/deep_copy.rb +7 -0
- data/lib/common/errors.rb +7 -0
- data/lib/common/properties/property_helper.rb +2 -2
- data/lib/common/properties/template_evaluation_context.rb +29 -8
- data/lib/common/retryable.rb +74 -0
- data/lib/common/runs_commands.rb +12 -0
- data/lib/common/ssl.rb +107 -0
- data/lib/common/version.rb +1 -1
- data/lib/common/version_number.rb +53 -0
- metadata +21 -30
- data/Rakefile +0 -50
- data/spec/assets/foo1 +0 -0
- data/spec/assets/foo2 +0 -0
- data/spec/spec_helper.rb +0 -13
- data/spec/unit/common_spec.rb +0 -59
- data/spec/unit/exec_spec.rb +0 -114
- data/spec/unit/properties/property_helper_spec.rb +0 -39
- data/spec/unit/properties/template_evaluation_context_spec.rb +0 -112
- data/spec/unit/thread_pool_spec.rb +0 -69
data/lib/common/common.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# Copyright (c) 2012 VMware, Inc.
|
2
|
+
require 'common/errors'
|
3
|
+
require 'common/retryable'
|
2
4
|
|
3
5
|
module Bosh
|
4
6
|
|
@@ -43,5 +45,80 @@ module Bosh
|
|
43
45
|
end
|
44
46
|
|
45
47
|
module_function :which
|
48
|
+
|
49
|
+
# Retries execution of given block until block returns true
|
50
|
+
#
|
51
|
+
# The block is called with two parameters: the number of tries and the most recent
|
52
|
+
# exception. If the block returns true retrying is stopped.
|
53
|
+
# Examples:
|
54
|
+
# Bosh::Common.retryable do |retries, exception|
|
55
|
+
# puts "try #{retries} failed with exception: #{exception}" if retries > 0
|
56
|
+
# pick_up_soap
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# # Wait for EC2 instance to be terminated
|
60
|
+
# Bosh::Common.retryable(on: AWS::EC2::Errors::RequestLimitExceeded) do |retries, exception|
|
61
|
+
# @ec2.instance['i-a3x5g5'].status == :terminated
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
#
|
65
|
+
# @param [Hash] options
|
66
|
+
# @options opts [Proc] :ensure
|
67
|
+
# Default: `Proc.new {}`
|
68
|
+
# Ensure that a block of code is executed, regardless of whether an exception
|
69
|
+
# was raised. It doesn't matter if the block exits normally, if it retries
|
70
|
+
# to execute block of code, or if it is terminated by an uncaught exception
|
71
|
+
# -- the ensure block will get run.
|
72
|
+
# Example:
|
73
|
+
# f = File.open("testfile")
|
74
|
+
#
|
75
|
+
# ensure_cb = Proc.new do |retries|
|
76
|
+
# puts "total retry attempts: #{retries}"
|
77
|
+
#
|
78
|
+
# f.close
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# Bosh::Common.retryable(ensure: ensure_cb) do
|
82
|
+
# # process file
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# @options opts [Regexp] :matching
|
86
|
+
# Default: `/.*/`
|
87
|
+
# Retry based on the exception message
|
88
|
+
# Example:
|
89
|
+
# Bosh::Common.retryable(matching: /IO timeout/) do |retries, exception|
|
90
|
+
# raise "yo, IO timeout!" if retries == 0
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# @options opts [Array<ExceptionClass>] :on
|
94
|
+
# Default: `[]`
|
95
|
+
# The array of exception classes to retry on.
|
96
|
+
# Example:
|
97
|
+
# Bosh::Common.retryable(on: [StandardError, ArgumentError]) do
|
98
|
+
# # do something and retry if StandardError or ArgumentError is raised
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @options opts [Proc, Fixnum] :sleep
|
102
|
+
# Defaults: `lambda { |tries, _| [2**(tries-1), 10].min }`, 1, 2, 4, 8, 10, 10..10 seconds
|
103
|
+
# If a Fixnum is given, sleep that many seconds between retries.
|
104
|
+
# If a Proc is given, call it with the expectation that a Fixnum is returned
|
105
|
+
# and sleep that many seconds. The Proc will be called with the number of tries
|
106
|
+
# and the raised exception (or nil)
|
107
|
+
# Example:
|
108
|
+
# Bosh::Common.retryable(sleep: lambda { |n,e| logger.info(e.message) if e; 4**n }) { }
|
109
|
+
#
|
110
|
+
# @options opts [Fixnum] :tries
|
111
|
+
# Default: 2
|
112
|
+
# Number of times to try
|
113
|
+
# Example:
|
114
|
+
# Bosh::Common.retryable(tries: 3, on: OpenURI::HTTPError) do
|
115
|
+
# xml = open("http://example.com/test.xml").read
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
def retryable(options = {}, &block)
|
119
|
+
Bosh::Retryable.new(options).retryer(&block)
|
120
|
+
end
|
121
|
+
|
122
|
+
module_function :retryable
|
46
123
|
end
|
47
124
|
end
|
@@ -23,7 +23,7 @@ module Bosh::Common
|
|
23
23
|
end
|
24
24
|
|
25
25
|
dst_ref[keys[-1]] ||= {}
|
26
|
-
dst_ref[keys[-1]] = src_ref
|
26
|
+
dst_ref[keys[-1]] = src_ref.nil? ? default : src_ref
|
27
27
|
end
|
28
28
|
|
29
29
|
# @param [Hash] collection Property collection
|
@@ -40,4 +40,4 @@ module Bosh::Common
|
|
40
40
|
ref
|
41
41
|
end
|
42
42
|
end
|
43
|
-
end
|
43
|
+
end
|
@@ -73,18 +73,14 @@ module Bosh::Common
|
|
73
73
|
# @return [Object] Property value
|
74
74
|
# @raise [Bosh::Common::UnknownProperty]
|
75
75
|
def p(*args)
|
76
|
-
names = args[0]
|
77
|
-
default_given = args.size > 1
|
78
|
-
default = args[1]
|
79
|
-
|
80
|
-
names = Array(names) unless names.kind_of?(Array)
|
76
|
+
names = Array(args[0])
|
81
77
|
|
82
78
|
names.each do |name|
|
83
79
|
result = lookup_property(@raw_properties, name)
|
84
80
|
return result unless result.nil?
|
85
81
|
end
|
86
82
|
|
87
|
-
return
|
83
|
+
return args[1] if args.length == 2
|
88
84
|
raise UnknownProperty.new(names)
|
89
85
|
end
|
90
86
|
|
@@ -94,11 +90,12 @@ module Bosh::Common
|
|
94
90
|
def if_p(*names)
|
95
91
|
values = names.map do |name|
|
96
92
|
value = lookup_property(@raw_properties, name)
|
97
|
-
return if value.nil?
|
93
|
+
return ActiveElseBlock.new(self) if value.nil?
|
98
94
|
value
|
99
95
|
end
|
100
96
|
|
101
97
|
yield *values
|
98
|
+
InactiveElseBlock.new
|
102
99
|
end
|
103
100
|
|
104
101
|
# @return [Object] Object representation where all hashes are unrolled
|
@@ -115,5 +112,29 @@ module Bosh::Common
|
|
115
112
|
object
|
116
113
|
end
|
117
114
|
end
|
115
|
+
|
116
|
+
private
|
117
|
+
class ActiveElseBlock
|
118
|
+
def initialize(template_context)
|
119
|
+
@context = template_context
|
120
|
+
end
|
121
|
+
|
122
|
+
def else
|
123
|
+
yield
|
124
|
+
end
|
125
|
+
|
126
|
+
def else_if_p(*names, &block)
|
127
|
+
@context.if_p(*names, &block)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class InactiveElseBlock
|
132
|
+
def else
|
133
|
+
end
|
134
|
+
|
135
|
+
def else_if_p(*names)
|
136
|
+
InactiveElseBlock.new
|
137
|
+
end
|
138
|
+
end
|
118
139
|
end
|
119
|
-
end
|
140
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
3
|
+
module Bosh
|
4
|
+
class Retryable
|
5
|
+
def initialize(options = {})
|
6
|
+
opts = validate_options(options)
|
7
|
+
|
8
|
+
@ensure_callback = opts[:ensure]
|
9
|
+
@matching = opts[:matching]
|
10
|
+
@on_exception = [opts[:on]].flatten
|
11
|
+
@try_count = 0
|
12
|
+
@retry_exception = nil
|
13
|
+
@retry_limit = opts[:tries]
|
14
|
+
@sleeper = opts[:sleep]
|
15
|
+
end
|
16
|
+
|
17
|
+
# this method will loop until the block returns a true value
|
18
|
+
def retryer(&block)
|
19
|
+
loop do
|
20
|
+
@try_count += 1
|
21
|
+
y = yield @try_count, @retry_exception
|
22
|
+
@retry_exception = nil # no exception was raised in the block
|
23
|
+
return y if y
|
24
|
+
raise Bosh::Common::RetryCountExceeded if @try_count >= @retry_limit
|
25
|
+
wait
|
26
|
+
end
|
27
|
+
rescue *@on_exception => exception
|
28
|
+
raise unless exception.message =~ @matching
|
29
|
+
raise if @try_count >= @retry_limit
|
30
|
+
|
31
|
+
@retry_exception = exception
|
32
|
+
wait
|
33
|
+
retry
|
34
|
+
ensure
|
35
|
+
@ensure_callback.call(@try_count)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_options(options)
|
41
|
+
merged_options = default_options.merge(options)
|
42
|
+
invalid_options = merged_options.keys - default_options.keys
|
43
|
+
raise ArgumentError.new("Invalid options: #{invalid_options.join(", ")}") unless invalid_options.empty?
|
44
|
+
|
45
|
+
merged_options
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_options
|
49
|
+
{
|
50
|
+
tries: 2,
|
51
|
+
sleep: exponential_sleeper,
|
52
|
+
on: [],
|
53
|
+
matching: /.*/,
|
54
|
+
ensure: Proc.new {}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def wait
|
59
|
+
sleep(@sleeper.respond_to?(:call) ? @sleeper.call(@try_count, @retry_exception) : @sleeper)
|
60
|
+
rescue *@on_exception
|
61
|
+
# SignalException could be raised while sleeping, so if you want to catch it,
|
62
|
+
# it need to be passed in the list of exceptions to ignore
|
63
|
+
end
|
64
|
+
|
65
|
+
def exponential_sleeper
|
66
|
+
lambda { |tries, _| [2**(tries-1), 10].min } # 1, 2, 4, 8, 10, 10..10 seconds
|
67
|
+
end
|
68
|
+
|
69
|
+
def sleep(*args, &blk)
|
70
|
+
Kernel.sleep(*args, &blk)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'common/exec'
|
2
|
+
|
3
|
+
# Mixin to make it easy to inject an alternate command runner into a class that runs commands.
|
4
|
+
module Bosh
|
5
|
+
module RunsCommands
|
6
|
+
def sh(command)
|
7
|
+
(@command_runner || Bosh::Exec).sh(command)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :command_runner
|
11
|
+
end
|
12
|
+
end
|
data/lib/common/ssl.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Bosh
|
4
|
+
module Ssl
|
5
|
+
class Certificate
|
6
|
+
class SubjectsDoNotMatchException < RuntimeError;
|
7
|
+
end
|
8
|
+
class MatchingFileNotFound < RuntimeError;
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :key_path, :certificate_path
|
12
|
+
|
13
|
+
def initialize(key_path, certificate_path, common_name, chain_path = nil)
|
14
|
+
@key_path = key_path
|
15
|
+
@certificate_path = certificate_path
|
16
|
+
@chain_path = chain_path
|
17
|
+
@subject_string = subject_string(common_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def key
|
21
|
+
@key.to_pem
|
22
|
+
end
|
23
|
+
|
24
|
+
def certificate
|
25
|
+
@csr_cert.to_pem
|
26
|
+
end
|
27
|
+
|
28
|
+
def chain
|
29
|
+
@chain.to_pem if @chain
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_or_create
|
33
|
+
@key, @csr_cert = load_or_create_key_and_csr_cert
|
34
|
+
@chain = OpenSSL::X509::Certificate.new(File.read(@chain_path)) if @chain_path
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def load_or_create_key_and_csr_cert
|
42
|
+
if File.exists?(@key_path) && !File.exists?(@certificate_path)
|
43
|
+
raise MatchingFileNotFound, 'The key that matches the given certificate could not be found.'
|
44
|
+
end
|
45
|
+
|
46
|
+
if File.exists?(@certificate_path) && !File.exists?(@key_path)
|
47
|
+
raise MatchingFileNotFound, 'The certificate that matches the given key could not be found.'
|
48
|
+
end
|
49
|
+
|
50
|
+
if File.exists?(@key_path) && File.exists?(@certificate_path)
|
51
|
+
load_key_and_csr_cert
|
52
|
+
else
|
53
|
+
create_key_and_csr_cert
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_key_and_csr_cert
|
58
|
+
key = OpenSSL::PKey::RSA.new(File.read(@key_path))
|
59
|
+
csr_cert = OpenSSL::X509::Certificate.new(File.read(@certificate_path))
|
60
|
+
|
61
|
+
[key, csr_cert]
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_key_and_csr_cert
|
65
|
+
subject = OpenSSL::X509::Name.parse(@subject_string)
|
66
|
+
key = OpenSSL::PKey::RSA.new(2048)
|
67
|
+
csr = new_csr(key, subject)
|
68
|
+
csr_cert = new_csr_certificate(key, csr)
|
69
|
+
|
70
|
+
File.write(@key_path, key.to_pem)
|
71
|
+
File.write(@certificate_path, csr_cert.to_pem)
|
72
|
+
|
73
|
+
[key, csr_cert]
|
74
|
+
end
|
75
|
+
|
76
|
+
def new_csr(key, subject)
|
77
|
+
csr = OpenSSL::X509::Request.new
|
78
|
+
csr.version = 0
|
79
|
+
csr.subject = subject
|
80
|
+
csr.public_key = key.public_key
|
81
|
+
csr.sign key, OpenSSL::Digest::SHA1.new
|
82
|
+
|
83
|
+
csr
|
84
|
+
end
|
85
|
+
|
86
|
+
def new_csr_certificate(key, csr)
|
87
|
+
csr_cert = OpenSSL::X509::Certificate.new
|
88
|
+
csr_cert.serial = 0
|
89
|
+
csr_cert.version = 2
|
90
|
+
csr_cert.not_before = Time.now - 60 * 60 * 24
|
91
|
+
csr_cert.not_after = Time.now + 94608000
|
92
|
+
|
93
|
+
csr_cert.subject = csr.subject
|
94
|
+
csr_cert.public_key = csr.public_key
|
95
|
+
csr_cert.issuer = csr.subject
|
96
|
+
|
97
|
+
csr_cert.sign key, OpenSSL::Digest::SHA1.new
|
98
|
+
|
99
|
+
csr_cert
|
100
|
+
end
|
101
|
+
|
102
|
+
def subject_string(common_name)
|
103
|
+
"/C=US/O=Pivotal/CN=#{common_name}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/common/version.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Bosh::Common
|
2
|
+
class VersionNumber
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def initialize(version_value)
|
6
|
+
@version = version_value.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def <=>(other)
|
10
|
+
v1 = @version
|
11
|
+
v2 = other.to_s
|
12
|
+
return v1 <=> v2 if [v1, v2].all? { |v| v.to_s.match(/^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$/) }
|
13
|
+
|
14
|
+
vp1 = components
|
15
|
+
vp2 = other.components
|
16
|
+
|
17
|
+
[vp1.size, vp2.size].max.times do |i|
|
18
|
+
result = vp1[i].to_i <=> vp2[i].to_i
|
19
|
+
return result unless result == 0
|
20
|
+
end
|
21
|
+
|
22
|
+
0
|
23
|
+
end
|
24
|
+
|
25
|
+
def major
|
26
|
+
components[0].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def minor
|
30
|
+
components[1].to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
def components
|
34
|
+
@version.split('.')
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
@version
|
39
|
+
end
|
40
|
+
|
41
|
+
def final?
|
42
|
+
!@version.end_with?('-dev')
|
43
|
+
end
|
44
|
+
|
45
|
+
def next_minor
|
46
|
+
self.class.new("#{major}.#{minor + 1}")
|
47
|
+
end
|
48
|
+
|
49
|
+
def dev
|
50
|
+
final? ? self.class.new("#{@version}-dev") : self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,23 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bosh_common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 1.5.0.pre.1100
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- VMware
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-12 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description: BOSH common
|
15
|
-
|
14
|
+
description: ! 'BOSH common
|
15
|
+
|
16
|
+
a5984f'
|
17
|
+
email: support@cloudfoundry.com
|
16
18
|
executables: []
|
17
19
|
extensions: []
|
18
20
|
extra_rdoc_files: []
|
19
21
|
files:
|
20
22
|
- lib/common/common.rb
|
23
|
+
- lib/common/deep_copy.rb
|
24
|
+
- lib/common/errors.rb
|
21
25
|
- lib/common/exec.rb
|
22
26
|
- lib/common/exec/error.rb
|
23
27
|
- lib/common/exec/result.rb
|
@@ -25,21 +29,17 @@ files:
|
|
25
29
|
- lib/common/properties/errors.rb
|
26
30
|
- lib/common/properties/property_helper.rb
|
27
31
|
- lib/common/properties/template_evaluation_context.rb
|
32
|
+
- lib/common/retryable.rb
|
33
|
+
- lib/common/runs_commands.rb
|
34
|
+
- lib/common/ssl.rb
|
28
35
|
- lib/common/thread_formatter.rb
|
29
36
|
- lib/common/thread_pool.rb
|
30
37
|
- lib/common/version.rb
|
38
|
+
- lib/common/version_number.rb
|
31
39
|
- README
|
32
|
-
|
33
|
-
|
34
|
-
-
|
35
|
-
- spec/spec_helper.rb
|
36
|
-
- spec/unit/common_spec.rb
|
37
|
-
- spec/unit/exec_spec.rb
|
38
|
-
- spec/unit/properties/property_helper_spec.rb
|
39
|
-
- spec/unit/properties/template_evaluation_context_spec.rb
|
40
|
-
- spec/unit/thread_pool_spec.rb
|
41
|
-
homepage: http://www.vmware.com
|
42
|
-
licenses: []
|
40
|
+
homepage: https://github.com/cloudfoundry/bosh
|
41
|
+
licenses:
|
42
|
+
- Apache 2.0
|
43
43
|
post_install_message:
|
44
44
|
rdoc_options: []
|
45
45
|
require_paths:
|
@@ -49,26 +49,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
49
|
requirements:
|
50
50
|
- - ! '>='
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
52
|
+
version: 1.9.3
|
53
53
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
54
|
none: false
|
55
55
|
requirements:
|
56
|
-
- - ! '
|
56
|
+
- - ! '>'
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version:
|
58
|
+
version: 1.3.1
|
59
59
|
requirements: []
|
60
60
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.8.
|
61
|
+
rubygems_version: 1.8.23
|
62
62
|
signing_key:
|
63
63
|
specification_version: 3
|
64
64
|
summary: BOSH common
|
65
|
-
test_files:
|
66
|
-
- spec/assets/foo1
|
67
|
-
- spec/assets/foo2
|
68
|
-
- spec/spec_helper.rb
|
69
|
-
- spec/unit/common_spec.rb
|
70
|
-
- spec/unit/exec_spec.rb
|
71
|
-
- spec/unit/properties/property_helper_spec.rb
|
72
|
-
- spec/unit/properties/template_evaluation_context_spec.rb
|
73
|
-
- spec/unit/thread_pool_spec.rb
|
74
|
-
has_rdoc:
|
65
|
+
test_files: []
|
data/Rakefile
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
-
|
3
|
-
$:.unshift(File.expand_path("../../rake", __FILE__))
|
4
|
-
|
5
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
|
6
|
-
|
7
|
-
require "rubygems"
|
8
|
-
require "bundler"
|
9
|
-
Bundler.setup(:default, :test)
|
10
|
-
|
11
|
-
require "rake"
|
12
|
-
begin
|
13
|
-
require "rspec/core/rake_task"
|
14
|
-
rescue LoadError
|
15
|
-
end
|
16
|
-
|
17
|
-
require "bundler_task"
|
18
|
-
require "ci_task"
|
19
|
-
|
20
|
-
gem_helper = Bundler::GemHelper.new(Dir.pwd)
|
21
|
-
|
22
|
-
desc "Build BOSH Common gem into the pkg directory"
|
23
|
-
task "build" do
|
24
|
-
gem_helper.build_gem
|
25
|
-
end
|
26
|
-
|
27
|
-
desc "Build and install BOSH Common into system gems"
|
28
|
-
task "install" do
|
29
|
-
Rake::Task["bundler:install"].invoke
|
30
|
-
gem_helper.install_gem
|
31
|
-
end
|
32
|
-
|
33
|
-
BundlerTask.new
|
34
|
-
|
35
|
-
if defined?(RSpec)
|
36
|
-
namespace :spec do
|
37
|
-
desc "Run Unit Tests"
|
38
|
-
rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
|
39
|
-
t.pattern = "spec/unit/**/*_spec.rb"
|
40
|
-
t.rspec_opts = %w(--format progress --colour)
|
41
|
-
end
|
42
|
-
|
43
|
-
CiTask.new do |task|
|
44
|
-
task.rspec_task = rspec_task
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
desc "Run tests"
|
49
|
-
task :spec => %w(spec:unit)
|
50
|
-
end
|
data/spec/assets/foo1
DELETED
File without changes
|
data/spec/assets/foo2
DELETED
File without changes
|
data/spec/spec_helper.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
-
|
3
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
4
|
-
|
5
|
-
require "rubygems"
|
6
|
-
require "bundler"
|
7
|
-
Bundler.setup(:default, :test)
|
8
|
-
|
9
|
-
require "rspec"
|
10
|
-
|
11
|
-
def asset(file)
|
12
|
-
File.expand_path(File.join(File.dirname(__FILE__), "assets", file))
|
13
|
-
end
|
data/spec/unit/common_spec.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
# Copyright (c) 2012 VMware, Inc.
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
require "common/common"
|
6
|
-
|
7
|
-
describe Bosh::Common do
|
8
|
-
|
9
|
-
describe "#symbolize_keys" do
|
10
|
-
ORIGINAL = {
|
11
|
-
"foo1" => "bar",
|
12
|
-
:foo2 => "bar",
|
13
|
-
"foo3" => {
|
14
|
-
"foo4" => "bar"
|
15
|
-
}
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
EXPECTED = {
|
19
|
-
:foo1 => "bar",
|
20
|
-
:foo2 => "bar",
|
21
|
-
:foo3 => {
|
22
|
-
:foo4 => "bar"
|
23
|
-
}
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
it "should not modify the original hash" do
|
27
|
-
duplicate = ORIGINAL.dup
|
28
|
-
Bosh::Common.symbolize_keys(ORIGINAL)
|
29
|
-
ORIGINAL.should == duplicate
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should return a new hash with all keys as symbols" do
|
33
|
-
Bosh::Common.symbolize_keys(ORIGINAL).should == EXPECTED
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "#which" do
|
38
|
-
let(:path) {
|
39
|
-
path = ENV["PATH"]
|
40
|
-
path += ":#{File.expand_path('../../assets', __FILE__)}"
|
41
|
-
}
|
42
|
-
|
43
|
-
it "should return the path when it finds the executable" do
|
44
|
-
Bosh::Common.which("foo1", path).should_not be_nil
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should return the path when it finds an executable" do
|
48
|
-
Bosh::Common.which(%w[foo2 foo1], path).should match(%r{/foo1$})
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should return nil when it isn't executable" do
|
52
|
-
Bosh::Common.which("foo2", path).should be_nil
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should return nil when it doesn't find an executable" do
|
56
|
-
Bosh::Common.which("foo1").should be_nil
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/spec/unit/exec_spec.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
# Copyright (c) 2012 VMware, Inc.
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
require "common/exec"
|
5
|
-
|
6
|
-
describe Bosh::Exec do
|
7
|
-
let(:opts) { {} }
|
8
|
-
|
9
|
-
context "existing command" do
|
10
|
-
|
11
|
-
context "executes successfully" do
|
12
|
-
it "should not fail" do
|
13
|
-
Bosh::Exec.sh("ls /", opts).failed?.should be_false
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should execute block" do
|
17
|
-
block = false
|
18
|
-
Bosh::Exec.sh("ls /", opts) do
|
19
|
-
block = true
|
20
|
-
end
|
21
|
-
block.should be_true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context "fails to execute" do
|
26
|
-
it "should raise error by default" do
|
27
|
-
expect {
|
28
|
-
Bosh::Exec.sh("ls /asdasd 2>&1", opts)
|
29
|
-
}.to raise_error { |error|
|
30
|
-
error.should be_a Bosh::Exec::Error
|
31
|
-
error.output.should match /No such file or directory/
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should yield block on false" do
|
36
|
-
opts[:yield] = :on_false
|
37
|
-
block = false
|
38
|
-
Bosh::Exec.sh("ls /asdasd 2>&1", opts) do
|
39
|
-
block = true
|
40
|
-
end
|
41
|
-
block.should be_true
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should return result" do
|
45
|
-
opts[:on_error] = :return
|
46
|
-
Bosh::Exec.sh("ls /asdasd 2>&1", opts).failed?.should be_true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
context "missing command" do
|
53
|
-
it "should raise error by default" do
|
54
|
-
expect {
|
55
|
-
Bosh::Exec.sh("/asdasd 2>&1", opts)
|
56
|
-
}.to raise_error Bosh::Exec::Error
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should not raise error when requested" do
|
60
|
-
opts[:on_error] = :return
|
61
|
-
expect {
|
62
|
-
Bosh::Exec.sh("/asdasd 2>&1", opts)
|
63
|
-
}.to_not raise_error Bosh::Exec::Error
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should execute block when requested" do
|
67
|
-
opts[:yield] = :on_false
|
68
|
-
expect {
|
69
|
-
Bosh::Exec.sh("/asdasd 2>&1", opts) do
|
70
|
-
raise "foo"
|
71
|
-
end
|
72
|
-
}.to raise_error "foo"
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
context "mock" do
|
78
|
-
it "should be possible fake result" do
|
79
|
-
cmd = "ls /"
|
80
|
-
result = Bosh::Exec::Result.new(cmd, "output", 0)
|
81
|
-
Bosh::Exec.should_receive(:sh).with(cmd).and_return(result)
|
82
|
-
result = Bosh::Exec.sh(cmd)
|
83
|
-
result.success?.should be_true
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
context "module" do
|
88
|
-
it "should be possible to invoke as a module" do
|
89
|
-
Bosh::Exec.sh("ls /").success?.should be_true
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
context "include" do
|
94
|
-
class IncludeTest
|
95
|
-
include Bosh::Exec
|
96
|
-
def run
|
97
|
-
sh("ls /")
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.run
|
101
|
-
sh("ls /")
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
it "should add instance method" do
|
106
|
-
inc = IncludeTest.new
|
107
|
-
inc.run.success?.should be_true
|
108
|
-
end
|
109
|
-
|
110
|
-
it "should add class method" do
|
111
|
-
IncludeTest.run.success?.should be_true
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# Copyright (c) 2012 VMware, Inc.
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
require "common/properties"
|
5
|
-
|
6
|
-
describe Bosh::Common::PropertyHelper do
|
7
|
-
|
8
|
-
before(:each) do
|
9
|
-
@helper = Object.new
|
10
|
-
@helper.extend(Bosh::Common::PropertyHelper)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "can copy named property from one collection to another" do
|
14
|
-
dst = {}
|
15
|
-
src = {"foo" => {"bar" => "baz", "secret" => "zazzle"}}
|
16
|
-
|
17
|
-
@helper.copy_property(dst, src, "foo.bar")
|
18
|
-
dst.should == {"foo" => {"bar" => "baz"}}
|
19
|
-
|
20
|
-
@helper.copy_property(dst, src, "no.such.prop", "default")
|
21
|
-
dst.should == {
|
22
|
-
"foo" => {"bar" => "baz"},
|
23
|
-
"no" => {
|
24
|
-
"such" => {"prop" => "default"}
|
25
|
-
}
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
it "can lookup the property in a Hash using dot-syntax" do
|
30
|
-
properties = {
|
31
|
-
"foo" => {"bar" => "baz"},
|
32
|
-
"router" => {"token" => "foo"}
|
33
|
-
}
|
34
|
-
|
35
|
-
@helper.lookup_property(properties, "foo.bar").should == "baz"
|
36
|
-
@helper.lookup_property(properties, "router").should == {"token" => "foo"}
|
37
|
-
@helper.lookup_property(properties, "no.prop").should be_nil
|
38
|
-
end
|
39
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
# Copyright (c) 2012 VMware, Inc.
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
require "common/properties"
|
5
|
-
|
6
|
-
describe Bosh::Common::TemplateEvaluationContext do
|
7
|
-
|
8
|
-
def eval_template(erb, context)
|
9
|
-
ERB.new(erb).result(context.get_binding)
|
10
|
-
end
|
11
|
-
|
12
|
-
def make(spec)
|
13
|
-
Bosh::Common::TemplateEvaluationContext.new(spec)
|
14
|
-
end
|
15
|
-
|
16
|
-
before(:each) do
|
17
|
-
@spec = {
|
18
|
-
"job" => {
|
19
|
-
"name" => "foobar"
|
20
|
-
},
|
21
|
-
"properties" => {
|
22
|
-
"foo" => "bar",
|
23
|
-
"router" => {"token" => "zbb"},
|
24
|
-
"vtrue" => true,
|
25
|
-
"vfalse" => false
|
26
|
-
},
|
27
|
-
"index" => 0,
|
28
|
-
}
|
29
|
-
|
30
|
-
@context = make(@spec)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "unrolls properties into OpenStruct" do
|
34
|
-
eval_template("<%= properties.foo %>", @context).should == "bar"
|
35
|
-
end
|
36
|
-
|
37
|
-
it "retains raw_properties" do
|
38
|
-
eval_template("<%= raw_properties['router']['token'] %>", @context).
|
39
|
-
should == "zbb"
|
40
|
-
end
|
41
|
-
|
42
|
-
it "supports looking up template index" do
|
43
|
-
eval_template("<%= spec.index %>", @context).should == "0"
|
44
|
-
end
|
45
|
-
|
46
|
-
it "supports 'p' helper" do
|
47
|
-
eval_template("<%= p('router.token') %>", @context).should == "zbb"
|
48
|
-
|
49
|
-
eval_template("<%= p('vtrue') %>", @context).should == "true"
|
50
|
-
eval_template("<%= p('vfalse') %>", @context).should == "false"
|
51
|
-
|
52
|
-
expect {
|
53
|
-
eval_template("<%= p('bar.baz') %>", @context)
|
54
|
-
}.to raise_error(Bosh::Common::UnknownProperty,
|
55
|
-
"Can't find property `[\"bar.baz\"]'")
|
56
|
-
eval_template("<%= p('bar.baz', 22) %>", @context).should == "22"
|
57
|
-
end
|
58
|
-
|
59
|
-
it "supports chaining property lookup via 'p' helper" do
|
60
|
-
eval_template(<<-TMPL, @context).strip.should == "zbb"
|
61
|
-
<%= p(%w(a b router.token c)) %>
|
62
|
-
TMPL
|
63
|
-
|
64
|
-
expect {
|
65
|
-
eval_template(<<-TMPL, @context)
|
66
|
-
<%= p(%w(a b c)) %>
|
67
|
-
TMPL
|
68
|
-
}.to raise_error(Bosh::Common::UnknownProperty,
|
69
|
-
"Can't find property `[\"a\", \"b\", \"c\"]'")
|
70
|
-
|
71
|
-
eval_template(<<-TMPL, @context).strip.should == "22"
|
72
|
-
<%= p(%w(a b c), 22) %>
|
73
|
-
TMPL
|
74
|
-
end
|
75
|
-
|
76
|
-
it "allows 'false' and 'nil' defaults for 'p' helper" do
|
77
|
-
eval_template(<<-TMPL, @context).strip.should == "false"
|
78
|
-
<%= p(%w(a b c), false) %>
|
79
|
-
TMPL
|
80
|
-
|
81
|
-
eval_template(<<-TMPL, @context).strip.should == ""
|
82
|
-
<%= p(%w(a b c), nil) %>
|
83
|
-
TMPL
|
84
|
-
end
|
85
|
-
|
86
|
-
it "supports 'if_p' helper" do
|
87
|
-
template = <<-TMPL
|
88
|
-
<% if_p("router.token") do |token| %>
|
89
|
-
<%= token %>
|
90
|
-
<% end %>
|
91
|
-
TMPL
|
92
|
-
|
93
|
-
eval_template(template, @context).strip.should == "zbb"
|
94
|
-
|
95
|
-
template = <<-TMPL
|
96
|
-
<% if_p("router.token", "foo") do |token, foo| %>
|
97
|
-
<%= token %>, <%= foo %>
|
98
|
-
<% end %>
|
99
|
-
TMPL
|
100
|
-
|
101
|
-
eval_template(template, @context).strip.should == "zbb, bar"
|
102
|
-
|
103
|
-
template = <<-TMPL
|
104
|
-
<% if_p("router.token", "no.such.prop") do |token, none| %>
|
105
|
-
test output
|
106
|
-
<% end %>
|
107
|
-
TMPL
|
108
|
-
|
109
|
-
eval_template(template, @context).strip.should == ""
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
-
|
3
|
-
require File.expand_path("../../spec_helper", __FILE__)
|
4
|
-
|
5
|
-
require "common/thread_pool"
|
6
|
-
|
7
|
-
describe Bosh::ThreadPool do
|
8
|
-
|
9
|
-
before(:all) do
|
10
|
-
@logger = Logger.new(STDOUT)
|
11
|
-
@logger.level = Logger::INFO
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should respect max threads" do
|
15
|
-
max = 0
|
16
|
-
current = 0
|
17
|
-
lock = Mutex.new
|
18
|
-
|
19
|
-
Bosh::ThreadPool.new(:max_threads => 2, :logger => @logger).wrap do |pool|
|
20
|
-
4.times do
|
21
|
-
pool.process do
|
22
|
-
lock.synchronize do
|
23
|
-
current += 1
|
24
|
-
max = current if current > max
|
25
|
-
end
|
26
|
-
sleep(0.050)
|
27
|
-
lock.synchronize do
|
28
|
-
max = current if current > max
|
29
|
-
current -= 1
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
max.should be <= 2
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should raise exceptions" do
|
38
|
-
lambda {
|
39
|
-
Bosh::ThreadPool.new(:max_threads => 2, :logger => @logger).wrap do |pool|
|
40
|
-
5.times do |index|
|
41
|
-
pool.process do
|
42
|
-
sleep(0.050)
|
43
|
-
raise "bad" if index == 4
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
}.should raise_exception("bad")
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should stop processing new work when there was an exception" do
|
51
|
-
max = 0
|
52
|
-
lock = Mutex.new
|
53
|
-
|
54
|
-
lambda {
|
55
|
-
Bosh::ThreadPool.new(:max_threads => 1, :logger => @logger).wrap do |pool|
|
56
|
-
10.times do |index|
|
57
|
-
pool.process do
|
58
|
-
lock.synchronize { max = index if index > max }
|
59
|
-
sleep(0.050)
|
60
|
-
raise "bad" if index == 4
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
}.should raise_exception("bad")
|
65
|
-
|
66
|
-
max.should be == 4
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|