cf_deployer 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|