cf_deployer 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +5 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -3
- data/cf_deployer.gemspec +1 -0
- data/lib/cf_deployer.rb +13 -3
- data/lib/cf_deployer/application.rb +9 -0
- data/lib/cf_deployer/cli.rb +16 -1
- data/lib/cf_deployer/component.rb +20 -0
- data/lib/cf_deployer/config_loader.rb +16 -3
- data/lib/cf_deployer/deployment_strategy/base.rb +21 -7
- data/lib/cf_deployer/deployment_strategy/create_or_update.rb +2 -1
- data/lib/cf_deployer/driver/cloud_formation_driver.rb +4 -0
- data/lib/cf_deployer/hook.rb +13 -1
- data/lib/cf_deployer/stack.rb +3 -0
- data/lib/cf_deployer/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/component_spec.rb +8 -0
- data/spec/unit/config_loader_spec.rb +40 -6
- data/spec/unit/deployment_strategy/base_spec.rb +93 -0
- data/spec/unit/deployment_strategy/create_or_update_spec.rb +18 -2
- data/spec/unit/deployment_strategy/deployment_strategy_spec.rb +1 -0
- data/spec/unit/hook_spec.rb +25 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b05cdb7b669ec244099ae727402aadc968485b7
|
4
|
+
data.tar.gz: 650362af16520e93554712c35284add91c75b448
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db066d60450bfc59a695b233c79b0e9b51e6e69798d2c78406ff257bb63ed0a80859669817fe6332cd1fa72d7ff61b30e1bac4561d5a8b22956dcd1e93a25647
|
7
|
+
data.tar.gz: 04f5af6970d722d5e9418ca04eb0fba3b6dd1fafd20e4fe45dcd88dfc685238350ef5d35175236adad1876a9e5fa77b423546672f8f111467f268594b9b9a1bd
|
data/ChangeLog.md
CHANGED
@@ -23,3 +23,8 @@ version 1.2.10
|
|
23
23
|
|
24
24
|
version 1.2.11
|
25
25
|
- Remove record set in R53 when stacks are deployed
|
26
|
+
|
27
|
+
version 1.3.1
|
28
|
+
- Adding way to run hooks manually (outside of deploy)
|
29
|
+
- Adding new command 'diff' to allow diffing between the deployed JSON
|
30
|
+
- Split after-create and after-update hooks for create-or-update strategy
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cf_deployer (1.3.
|
4
|
+
cf_deployer (1.3.1)
|
5
5
|
aws-sdk
|
6
|
+
diffy
|
6
7
|
log4r
|
7
8
|
rainbow
|
8
9
|
thor
|
@@ -10,16 +11,17 @@ PATH
|
|
10
11
|
GEM
|
11
12
|
remote: https://rubygems.org/
|
12
13
|
specs:
|
13
|
-
aws-sdk (1.
|
14
|
+
aws-sdk (1.51.0)
|
14
15
|
json (~> 1.4)
|
15
16
|
nokogiri (>= 1.4.4)
|
16
17
|
coderay (1.1.0)
|
17
18
|
diff-lcs (1.2.5)
|
19
|
+
diffy (3.0.6)
|
18
20
|
json (1.8.1)
|
19
21
|
log4r (1.1.10)
|
20
22
|
method_source (0.8.2)
|
21
23
|
mini_portile (0.6.0)
|
22
|
-
nokogiri (1.6.
|
24
|
+
nokogiri (1.6.3.1)
|
23
25
|
mini_portile (= 0.6.0)
|
24
26
|
pry (0.9.12.6)
|
25
27
|
coderay (~> 1.0)
|
@@ -44,6 +46,7 @@ PLATFORMS
|
|
44
46
|
|
45
47
|
DEPENDENCIES
|
46
48
|
cf_deployer!
|
49
|
+
diffy
|
47
50
|
pry
|
48
51
|
rainbow
|
49
52
|
rake
|
data/cf_deployer.gemspec
CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.add_runtime_dependency 'log4r'
|
14
14
|
gem.add_runtime_dependency 'thor'
|
15
15
|
gem.add_runtime_dependency 'rainbow'
|
16
|
+
gem.add_runtime_dependency 'diffy'
|
16
17
|
|
17
18
|
gem.files = `git ls-files`.split($\).reject {|f| f =~ /^samples\// }
|
18
19
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/lib/cf_deployer.rb
CHANGED
@@ -54,6 +54,12 @@ module CfDeployer
|
|
54
54
|
Application.new(config).deploy
|
55
55
|
end
|
56
56
|
|
57
|
+
def self.runhook opts
|
58
|
+
config = self.parseconfig opts
|
59
|
+
# AWS.config(:logger => Logger.new($stdout))
|
60
|
+
Application.new(config).run_hook opts[:component].first, opts[:hook_name]
|
61
|
+
end
|
62
|
+
|
57
63
|
def self.destroy opts
|
58
64
|
config = self.parseconfig opts, false
|
59
65
|
# AWS.config(:logger => Logger.new($stdout))
|
@@ -65,6 +71,11 @@ module CfDeployer
|
|
65
71
|
Application.new(config).json
|
66
72
|
end
|
67
73
|
|
74
|
+
def self.diff opts
|
75
|
+
config = self.parseconfig opts, false
|
76
|
+
Application.new(config).diff
|
77
|
+
end
|
78
|
+
|
68
79
|
def self.status opts
|
69
80
|
config = self.parseconfig opts, false
|
70
81
|
status_info = Application.new(config).status opts[:component].first, opts[:verbosity]
|
@@ -87,9 +98,8 @@ module CfDeployer
|
|
87
98
|
|
88
99
|
def self.parseconfig options, validate_inputs = true
|
89
100
|
AWS.config(:region => options[:region]) if options[:region]
|
90
|
-
|
91
|
-
|
92
|
-
config = ConfigLoader.new.load split_settings
|
101
|
+
options[:cli_overrides] = {:settings => options.delete(:settings), :inputs => options.delete(:inputs)}
|
102
|
+
config = ConfigLoader.new.load options
|
93
103
|
ConfigValidation.new.validate config, validate_inputs
|
94
104
|
config
|
95
105
|
end
|
@@ -42,6 +42,11 @@ module CfDeployer
|
|
42
42
|
components.each &:json
|
43
43
|
end
|
44
44
|
|
45
|
+
def diff
|
46
|
+
components = get_targets().sort
|
47
|
+
components.each &:diff
|
48
|
+
end
|
49
|
+
|
45
50
|
def status component_name, verbosity
|
46
51
|
statuses = {}
|
47
52
|
@components.select { |component| component_name.nil? || component_name == component.name }.each do |component|
|
@@ -50,6 +55,10 @@ module CfDeployer
|
|
50
55
|
statuses
|
51
56
|
end
|
52
57
|
|
58
|
+
def run_hook component_name, hook_name
|
59
|
+
@components.detect{ |component| component_name == component.name }.run_hook hook_name
|
60
|
+
end
|
61
|
+
|
53
62
|
def destroy
|
54
63
|
components = get_targets.sort { |a, b| b <=> a }
|
55
64
|
components.each &:destroy
|
data/lib/cf_deployer/cli.rb
CHANGED
@@ -19,6 +19,13 @@ module CfDeployer
|
|
19
19
|
CfDeployer.deploy merged_options
|
20
20
|
end
|
21
21
|
|
22
|
+
desc "runhook [ENVIRONMENT] [COMPONENT] [HOOK_NAME]\t", 'Run the specified hook'
|
23
|
+
def runhook environment, component, hook_name
|
24
|
+
@hook_name = hook_name.to_sym
|
25
|
+
prep_for_action :runhook, environment, component
|
26
|
+
CfDeployer.runhook merged_options
|
27
|
+
end
|
28
|
+
|
22
29
|
desc "destroy [ENVIRONMENT] [COMPONENT]\t", 'Destroy the specified environment/component'
|
23
30
|
def destroy environment, component = nil
|
24
31
|
prep_for_action :destroy, environment, component
|
@@ -31,6 +38,12 @@ module CfDeployer
|
|
31
38
|
CfDeployer.config merged_options
|
32
39
|
end
|
33
40
|
|
41
|
+
desc "diff [ENVIRONMENT] [COMPONENT]", 'Show a diff between the template of the active stack and the parsed CloudFormation JSON for the target component'
|
42
|
+
def diff environment, component = nil
|
43
|
+
prep_for_action :diff, environment, component
|
44
|
+
CfDeployer.diff merged_options
|
45
|
+
end
|
46
|
+
|
34
47
|
desc "json [ENVIRONMENT] [COMPONENT]", 'Show parsed CloudFormation JSON for the target component'
|
35
48
|
def json environment, component = nil
|
36
49
|
prep_for_action :json, environment, component
|
@@ -73,7 +86,9 @@ module CfDeployer
|
|
73
86
|
end
|
74
87
|
|
75
88
|
def merged_options
|
76
|
-
|
89
|
+
the_merge_options = {:environment => @environment, :component => @component}
|
90
|
+
the_merge_options[:hook_name] = @hook_name if @hook_name
|
91
|
+
symbolize_all_keys options.merge(the_merge_options)
|
77
92
|
end
|
78
93
|
|
79
94
|
def set_log_level
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'diffy'
|
2
|
+
|
1
3
|
module CfDeployer
|
2
4
|
class Component
|
3
5
|
attr_reader :name, :dependencies, :children
|
@@ -36,6 +38,19 @@ module CfDeployer
|
|
36
38
|
puts ConfigLoader.component_json(name, @context)
|
37
39
|
end
|
38
40
|
|
41
|
+
def diff
|
42
|
+
resolve_settings
|
43
|
+
current_json = strategy.active_template
|
44
|
+
if current_json
|
45
|
+
puts "#{name} json template diff:"
|
46
|
+
new_json = ConfigLoader.component_json(name, @context)
|
47
|
+
Diffy::Diff.default_format = :color
|
48
|
+
puts Diffy::Diff.new( current_json, new_json )
|
49
|
+
else
|
50
|
+
puts "No current json for component #{name}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
39
54
|
def destroy
|
40
55
|
raise ApplicationError.new("Unable to destroy #{name}, it is depended on by other components") if any_children_exist?
|
41
56
|
strategy.destroy
|
@@ -76,6 +91,11 @@ module CfDeployer
|
|
76
91
|
strategy.status get_resource_statuses
|
77
92
|
end
|
78
93
|
|
94
|
+
def run_hook hook_name
|
95
|
+
resolve_settings
|
96
|
+
strategy.run_hook hook_name
|
97
|
+
end
|
98
|
+
|
79
99
|
private
|
80
100
|
|
81
101
|
def resolve_settings
|
@@ -4,7 +4,11 @@ module CfDeployer
|
|
4
4
|
def self.component_json component, config
|
5
5
|
json_file = File.join(config[:config_dir], "#{component}.json")
|
6
6
|
raise ApplicationError.new("#{json_file} is missing") unless File.exists?(json_file)
|
7
|
+
CfDeployer::Log.info "ERBing JSON for #{component}"
|
7
8
|
ERB.new(File.read(json_file)).result(binding)
|
9
|
+
rescue RuntimeError,TypeError,NoMethodError => e
|
10
|
+
self.new.send :error_document, File.read(json_file)
|
11
|
+
raise e
|
8
12
|
end
|
9
13
|
|
10
14
|
def load(options)
|
@@ -35,7 +39,11 @@ module CfDeployer
|
|
35
39
|
|
36
40
|
def load_yaml(text)
|
37
41
|
YAML.load text
|
42
|
+
rescue Psych::SyntaxError => e
|
43
|
+
error_document text
|
44
|
+
raise e
|
38
45
|
rescue
|
46
|
+
error_document text
|
39
47
|
raise ApplicationError.new("The config file is not a valid yaml file")
|
40
48
|
end
|
41
49
|
|
@@ -51,6 +59,12 @@ module CfDeployer
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
62
|
+
def error_document text
|
63
|
+
puts "-" * 80
|
64
|
+
puts text
|
65
|
+
puts "-" * 80
|
66
|
+
end
|
67
|
+
|
54
68
|
def to_array(value)
|
55
69
|
return value if value.is_a?(Array)
|
56
70
|
return [] unless value
|
@@ -118,13 +132,12 @@ module CfDeployer
|
|
118
132
|
end
|
119
133
|
|
120
134
|
json_content = self.class.component_json component.to_s, config
|
135
|
+
CfDeployer::Log.info "Parsing JSON for #{component}"
|
121
136
|
begin
|
122
137
|
JSON.load json_content
|
123
138
|
rescue JSON::ParserError => e
|
124
139
|
puts json_content
|
125
|
-
|
126
|
-
puts e.message[0..300]
|
127
|
-
puts '=' * 80
|
140
|
+
error_document e.message[0..300]
|
128
141
|
raise "Couldn't parse JSON for component #{component}"
|
129
142
|
end
|
130
143
|
end
|
@@ -28,6 +28,26 @@ module CfDeployer
|
|
28
28
|
BLUE_GREEN_STRATEGY
|
29
29
|
end
|
30
30
|
|
31
|
+
def run_hook(hook_name)
|
32
|
+
CfDeployer::Driver::DryRun.guard "Skipping hook #{hook_name}" do
|
33
|
+
unless @params_and_outputs_resolved
|
34
|
+
target_stack = ( active_stack || stack )
|
35
|
+
unless target_stack.exists?
|
36
|
+
CfDeployer::Log.info "Skipping hook call for #{hook_name} since stack #{target_stack.name} doesn't exist."
|
37
|
+
return
|
38
|
+
end
|
39
|
+
get_parameters_outputs target_stack
|
40
|
+
end
|
41
|
+
hook = Hook.new hook_name, context[hook_name]
|
42
|
+
hook.run context
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def active_template
|
47
|
+
target_stack = ( active_stack || stack )
|
48
|
+
(target_stack && target_stack.exists?) ? target_stack.template : nil
|
49
|
+
end
|
50
|
+
|
31
51
|
protected
|
32
52
|
|
33
53
|
def stack_prefix
|
@@ -45,15 +65,9 @@ module CfDeployer
|
|
45
65
|
stack.delete
|
46
66
|
end
|
47
67
|
|
48
|
-
def run_hook(hook_name)
|
49
|
-
CfDeployer::Driver::DryRun.guard "Skipping hook #{hook_name}" do
|
50
|
-
hook = Hook.new hook_name, context[hook_name]
|
51
|
-
hook.run context
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
68
|
def get_parameters_outputs(stack)
|
56
69
|
CfDeployer::Driver::DryRun.guard "Skipping get_parameters_outputs" do
|
70
|
+
@params_and_outputs_resolved = true
|
57
71
|
context[:parameters] = stack.parameters
|
58
72
|
context[:outputs] = stack.outputs
|
59
73
|
end
|
@@ -17,10 +17,11 @@ module CfDeployer
|
|
17
17
|
|
18
18
|
|
19
19
|
def deploy
|
20
|
+
hook_to_run = stack.exists? ? :'after-update' : :'after-create'
|
20
21
|
stack.deploy
|
21
22
|
warm_up_inactive_stack
|
22
23
|
get_parameters_outputs(inactive_stack)
|
23
|
-
run_hook(
|
24
|
+
run_hook(hook_to_run)
|
24
25
|
end
|
25
26
|
|
26
27
|
def output_value(key)
|
data/lib/cf_deployer/hook.rb
CHANGED
@@ -25,8 +25,20 @@ module CfDeployer
|
|
25
25
|
context = context.dup
|
26
26
|
timeout = timeout || Defaults::Timeout
|
27
27
|
Timeout.timeout(timeout.to_f) do
|
28
|
-
|
28
|
+
begin
|
29
|
+
eval(hook, binding)
|
30
|
+
rescue SyntaxError, NoMethodError, ApplicationError => e
|
31
|
+
error_document context, hook
|
32
|
+
raise e
|
33
|
+
end
|
29
34
|
end
|
30
35
|
end
|
36
|
+
|
37
|
+
def error_document context, hook
|
38
|
+
pp context
|
39
|
+
puts '=' * 80
|
40
|
+
puts hook
|
41
|
+
puts '=' * 80
|
42
|
+
end
|
31
43
|
end
|
32
44
|
end
|
data/lib/cf_deployer/stack.rb
CHANGED
data/lib/cf_deployer/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/unit/component_spec.rb
CHANGED
@@ -139,4 +139,12 @@ describe "component" do
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end
|
142
|
+
|
143
|
+
describe '#run_hook' do
|
144
|
+
it 'should resolve_settings before running the hook' do
|
145
|
+
expect(@web).to receive(:resolve_settings)
|
146
|
+
expect(@strategy).to receive(:run_hook).with(:'after-work')
|
147
|
+
@web.run_hook :'after-work'
|
148
|
+
end
|
149
|
+
end
|
142
150
|
end
|
@@ -9,6 +9,8 @@ describe "load config settings" do
|
|
9
9
|
@front_end_json = File.expand_path("../../../tmp/front-end.json", __FILE__)
|
10
10
|
@very_simple_json = File.expand_path("../../../tmp/very-simple.json", __FILE__)
|
11
11
|
@json_with_erb = File.expand_path("../../../tmp/json-with-erb.json", __FILE__)
|
12
|
+
@broken_json = File.expand_path("../../../tmp/broken_json.json", __FILE__)
|
13
|
+
@broken_erb = File.expand_path("../../../tmp/broken_erb.json", __FILE__)
|
12
14
|
|
13
15
|
base_json = <<-eos
|
14
16
|
{
|
@@ -58,11 +60,17 @@ describe "load config settings" do
|
|
58
60
|
}
|
59
61
|
eos
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
broken_json = '{ "Some_broken_json": "foo" '
|
64
|
+
broken_erb = '{ "Some_broken_erb": "<%= [1, 2, 3].first("two") %>" }'
|
65
|
+
@broken_yaml = "something: [ :foo "
|
66
|
+
|
67
|
+
File.open(@api_json, 'w') {|f| f.write(api_json) }
|
68
|
+
File.open(@base_json, 'w') {|f| f.write(base_json) }
|
69
|
+
File.open(@front_end_json, 'w') {|f| f.write(front_end_json) }
|
64
70
|
File.open(@very_simple_json, 'w') {|f| f.write(very_simple_json) }
|
65
|
-
File.open(@json_with_erb,
|
71
|
+
File.open(@json_with_erb, 'w') {|f| f.write(json_with_erb) }
|
72
|
+
File.open(@broken_json, 'w') {|f| f.write(broken_json) }
|
73
|
+
File.open(@broken_erb, 'w') {|f| f.write(broken_erb) }
|
66
74
|
|
67
75
|
|
68
76
|
yaml_string = <<-eos
|
@@ -97,6 +105,8 @@ components:
|
|
97
105
|
keep-previous-stack: false
|
98
106
|
very-simple:
|
99
107
|
json-with-erb:
|
108
|
+
#broken-json:
|
109
|
+
#broken_erb:
|
100
110
|
|
101
111
|
inputs:
|
102
112
|
require-basic-auth: false
|
@@ -104,7 +114,7 @@ inputs:
|
|
104
114
|
mail-server: http://abc.com
|
105
115
|
cname: myserver.com
|
106
116
|
|
107
|
-
notify:
|
117
|
+
notify:
|
108
118
|
- arn:root
|
109
119
|
|
110
120
|
tags:
|
@@ -186,7 +196,7 @@ environments:
|
|
186
196
|
config[:components][:api][:notify].should eq(['arn:root', 'arn:api', 'arn:dev'])
|
187
197
|
config[:components][:'front-end'][:notify].should eq(['arn:root', 'arn:base', 'arn:dev'])
|
188
198
|
end
|
189
|
-
|
199
|
+
|
190
200
|
it "notify option should be merged to environment context" do
|
191
201
|
config = CfDeployer::ConfigLoader.new.load({:'config-file' => @config_file, :environment => 'uat'})
|
192
202
|
config[:components][:base][:notify].should eq(['arn:root', 'arn:base'])
|
@@ -322,6 +332,30 @@ environments:
|
|
322
332
|
CfDeployer::ConfigLoader.component_json('json-with-erb', config[:components][:'json-with-erb']).should include('DrWho')
|
323
333
|
end
|
324
334
|
|
335
|
+
it 'should use error_document to show the broken document when parsing broken ERB' do
|
336
|
+
config = { :config_dir => File.dirname(@config_file) }
|
337
|
+
CfDeployer::ConfigLoader.any_instance.should_receive(:error_document)
|
338
|
+
expect { CfDeployer::ConfigLoader.component_json('broken_erb', config) }.to raise_error
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'should use error_document to show the broken document when parsing broken json' do
|
342
|
+
loader = CfDeployer::ConfigLoader.new
|
343
|
+
config = loader.load(:'config-file' => @config_file, :environment => 'DrWho')
|
344
|
+
config[:components]['broken_json'] = {
|
345
|
+
:config_dir => config[:components][:base][:config_dir],
|
346
|
+
:inputs => {},
|
347
|
+
:defined_parameters => {},
|
348
|
+
:defined_outputs => {}
|
349
|
+
}
|
350
|
+
CfDeployer::ConfigLoader.any_instance.should_receive(:error_document)
|
351
|
+
expect { loader.send(:cf_template, 'broken_json') }.to raise_error
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'should use error_document to show the broken document when parsing broken yaml' do
|
355
|
+
CfDeployer::ConfigLoader.any_instance.should_receive(:error_document)
|
356
|
+
expect { CfDeployer::ConfigLoader.new.send(:load_yaml, @broken_yaml) }.to raise_error
|
357
|
+
end
|
358
|
+
|
325
359
|
it 'should set default keep-previous-stack to true' do
|
326
360
|
config = CfDeployer::ConfigLoader.new.load(:'config-file' => @config_file)
|
327
361
|
config[:components][:api][:settings][:'keep-previous-stack'].should eq(CfDeployer::Defaults::KeepPreviousStack)
|
@@ -41,4 +41,97 @@ describe 'Base Deployment Strategy' do
|
|
41
41
|
}.to raise_error(CfDeployer::ApplicationError)
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
describe '#active_template' do
|
46
|
+
before :each do
|
47
|
+
@context = {
|
48
|
+
application: 'myApp',
|
49
|
+
environment: 'uat',
|
50
|
+
components:
|
51
|
+
{ base: {:'deployment-strategy' => 'create-or-update'},
|
52
|
+
db: {:'deployment-strategy' => 'auto-scaling-group-swap'},
|
53
|
+
web: { :'deployment-strategy' => 'cname-swap' }
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return nil if there is no active stack" do
|
59
|
+
the_stack = double()
|
60
|
+
the_stack.should_receive(:exists?).and_return(false)
|
61
|
+
the_stack.should_not_receive(:template)
|
62
|
+
|
63
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
64
|
+
strategy.should_receive(:active_stack).and_return(the_stack)
|
65
|
+
# strategy.should_not_receive(:get_parameters_outputs)
|
66
|
+
|
67
|
+
expect( strategy.active_template ).to eq(nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return the JSON template of the active stack, if there is one" do
|
71
|
+
the_template = double
|
72
|
+
|
73
|
+
the_stack = double
|
74
|
+
the_stack.should_receive(:exists?).and_return(true)
|
75
|
+
the_stack.should_receive(:template).and_return(the_template)
|
76
|
+
|
77
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
78
|
+
strategy.should_receive(:active_stack).and_return(the_stack)
|
79
|
+
|
80
|
+
expect( strategy.active_template ).to eq(the_template)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#run_hook' do
|
85
|
+
before :each do
|
86
|
+
@context = {
|
87
|
+
application: 'myApp',
|
88
|
+
environment: 'uat',
|
89
|
+
components:
|
90
|
+
{ base: {:'deployment-strategy' => 'create-or-update'},
|
91
|
+
db: {:'deployment-strategy' => 'auto-scaling-group-swap'},
|
92
|
+
web: { :'deployment-strategy' => 'cname-swap' }
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should run the specified hook" do
|
98
|
+
hook = double()
|
99
|
+
CfDeployer::Hook.should_receive(:new).and_return(hook)
|
100
|
+
hook.should_receive(:run)
|
101
|
+
|
102
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
103
|
+
strategy.instance_variable_set('@params_and_outputs_resolved', true)
|
104
|
+
strategy.run_hook(:some_hook)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should not try to resolve parameters and outputs they're already initialized" do
|
108
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
109
|
+
strategy.instance_variable_set('@params_and_outputs_resolved', true)
|
110
|
+
strategy.should_not_receive(:get_parameters_outputs)
|
111
|
+
strategy.run_hook(:some_hook)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should not try to resolve parameters and outputs if there's no running stack" do
|
115
|
+
the_stack = double()
|
116
|
+
the_stack.should_receive(:exists?).and_return(false)
|
117
|
+
the_stack.should_receive(:name).and_return("thestack")
|
118
|
+
|
119
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
120
|
+
strategy.should_receive(:active_stack).and_return(the_stack)
|
121
|
+
strategy.should_not_receive(:get_parameters_outputs)
|
122
|
+
strategy.run_hook(:some_hook)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not try to run a hook if there's no running stack" do
|
126
|
+
CfDeployer::Hook.should_not_receive(:new)
|
127
|
+
|
128
|
+
the_stack = double()
|
129
|
+
the_stack.should_receive(:exists?).and_return(false)
|
130
|
+
the_stack.should_receive(:name).and_return("thestack")
|
131
|
+
|
132
|
+
strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
133
|
+
strategy.should_receive(:active_stack).and_return(the_stack)
|
134
|
+
strategy.run_hook(:some_hook)
|
135
|
+
end
|
136
|
+
end
|
44
137
|
end
|
@@ -3,8 +3,10 @@ require 'spec_helper'
|
|
3
3
|
describe 'CreateOrUpdate Strategy' do
|
4
4
|
before :each do
|
5
5
|
@after_create_hook = double('after_create_hook')
|
6
|
+
@after_update_hook = double('after_update_hook')
|
6
7
|
@before_destroy_hook = double('before_destroy_hook')
|
7
8
|
allow(CfDeployer::Hook).to receive(:new).with(:'after-create', 'after-create'){ @after_create_hook }
|
9
|
+
allow(CfDeployer::Hook).to receive(:new).with(:'after-update', 'after-update'){ @after_update_hook }
|
8
10
|
allow(CfDeployer::Hook).to receive(:new).with(:'before-destroy', 'before-destroy') { @before_destroy_hook }
|
9
11
|
@context = {
|
10
12
|
:application => 'myApp',
|
@@ -14,6 +16,7 @@ describe 'CreateOrUpdate Strategy' do
|
|
14
16
|
:settings => {},
|
15
17
|
:'deployment-strategy' => 'create-or-update',
|
16
18
|
:'after-create' => 'after-create',
|
19
|
+
:'after-update' => 'after-update',
|
17
20
|
:'before-destroy' => 'before-destroy'
|
18
21
|
}
|
19
22
|
}
|
@@ -25,16 +28,28 @@ describe 'CreateOrUpdate Strategy' do
|
|
25
28
|
allow(@stack).to receive(:outputs){ {'ELBName' => 'myelb'}}
|
26
29
|
end
|
27
30
|
|
28
|
-
it 'should deploy stack' do
|
31
|
+
it 'should deploy stack and run the after-create hook if no stack exists' do
|
29
32
|
hook_context = nil
|
30
33
|
expect(@after_create_hook).to receive(:run) do |given_context|
|
31
34
|
hook_context = given_context
|
32
35
|
end
|
36
|
+
@stack.should_receive(:exists?).and_return(false)
|
37
|
+
expect(@stack).to receive(:deploy)
|
38
|
+
@create_or_update.deploy
|
39
|
+
expect(hook_context[:parameters]).to eq( {'vpc' => 'myvpc'} )
|
40
|
+
expect(hook_context[:outputs]).to eq( {'ELBName' => 'myelb'} )
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should deploy stack and run the after-update hook if a stack already exists' do
|
44
|
+
hook_context = nil
|
45
|
+
expect(@after_update_hook).to receive(:run) do |given_context|
|
46
|
+
hook_context = given_context
|
47
|
+
end
|
48
|
+
@stack.should_receive(:exists?).and_return(true)
|
33
49
|
expect(@stack).to receive(:deploy)
|
34
50
|
@create_or_update.deploy
|
35
51
|
expect(hook_context[:parameters]).to eq( {'vpc' => 'myvpc'} )
|
36
52
|
expect(hook_context[:outputs]).to eq( {'ELBName' => 'myelb'} )
|
37
|
-
|
38
53
|
end
|
39
54
|
|
40
55
|
context 'warm up auto scaling group' do
|
@@ -45,6 +60,7 @@ describe 'CreateOrUpdate Strategy' do
|
|
45
60
|
context = @context[:components][:base]
|
46
61
|
context[:settings] = {}
|
47
62
|
context[:settings][:'auto-scaling-group-name-output'] = ['AutoScalingGroupID']
|
63
|
+
@stack.should_receive(:exists?).and_return(false)
|
48
64
|
allow(@stack).to receive(:output).with('AutoScalingGroupID') { 'asg_name' }
|
49
65
|
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('asg_name') { asg_driver }
|
50
66
|
allow(asg_driver).to receive(:describe) { {desired:2, min:1, max:3} }
|
@@ -22,6 +22,7 @@ describe 'Deployment Strategy' do
|
|
22
22
|
expect(CfDeployer::DeploymentStrategy::AutoScalingGroupSwap).to receive(:new)
|
23
23
|
CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'db', @context[:components][:db])
|
24
24
|
end
|
25
|
+
|
25
26
|
it "should create Cname-Swap strategy" do
|
26
27
|
expect(CfDeployer::DeploymentStrategy::CnameSwap).to receive(:new)
|
27
28
|
CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
|
data/spec/unit/hook_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'stringio'
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe CfDeployer::Hook do
|
@@ -62,4 +63,28 @@ describe CfDeployer::Hook do
|
|
62
63
|
context = { app: 'myApp', config_dir: config_dir}
|
63
64
|
expect{CfDeployer::Hook.new('MyHook', {file: '../../tmp/test_code.rb', timeout: 20/1000.0}).run(context)}.to raise_error(Timeout::Error)
|
64
65
|
end
|
66
|
+
|
67
|
+
it 'should catch SyntaxError during eval and show nicer output' do
|
68
|
+
context = { app: 'myApp' }
|
69
|
+
the_hook = CfDeployer::Hook.new('MyHook', "puts 'hello")
|
70
|
+
the_hook.should_receive :error_document
|
71
|
+
expect { the_hook.run(context) }.to raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should catch NoMethodError during eval and show nicer output' do
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should catch ApplicationError during eval and show nicer output' do
|
78
|
+
end
|
79
|
+
|
80
|
+
def capture_stdout(&block)
|
81
|
+
original_stdout = $stdout
|
82
|
+
$stdout = fake = StringIO.new
|
83
|
+
begin
|
84
|
+
yield
|
85
|
+
ensure
|
86
|
+
$stdout = original_stdout
|
87
|
+
end
|
88
|
+
fake.string
|
89
|
+
end
|
65
90
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cf_deployer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jame Brechtel
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-08-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: aws-sdk
|
@@ -69,6 +69,20 @@ dependencies:
|
|
69
69
|
- - '>='
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: diffy
|
74
|
+
requirement: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
type: :runtime
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
72
86
|
description: For automatic blue green deployment flow on CloudFormation.
|
73
87
|
email:
|
74
88
|
- jbrechtel@gmail.com
|