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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1db5cec4a3e85b734de2469723174af6076d0334
4
- data.tar.gz: 79eebbe809bb12a92a59c4c8eed7d7064df46fbd
3
+ metadata.gz: 6b05cdb7b669ec244099ae727402aadc968485b7
4
+ data.tar.gz: 650362af16520e93554712c35284add91c75b448
5
5
  SHA512:
6
- metadata.gz: 8568f79cd0a27b30174422ce1edb8cfd05e9c554def87df53423cea0fd3f7547e221c6abf53335dfed02aa735cb73a756b2277dddbe78deaa16ad6a1fa51a010
7
- data.tar.gz: 5d7fc88066821fc2bffd2abed89914354e516d913635c1274b8bcf681e8417505ff3c9e5ba1c13098e475401921d1f353317f604a189225afdb5e93b3e0f06ed
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
@@ -8,3 +8,4 @@ gem 'pry'
8
8
  gem 'rspec'
9
9
  gem 'rake'
10
10
  gem 'rainbow'
11
+ gem 'diffy'
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cf_deployer (1.3.0)
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.44.0)
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.2.1)
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
- split_settings = options.dup
91
- split_settings.merge!({:cli_overrides => {:settings => split_settings.delete(:settings), :inputs => split_settings.delete(:inputs)} })
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
@@ -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
- symbolize_all_keys options.merge({:environment => @environment, :component => @component})
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
- puts '=' * 80
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(:'after-create')
24
+ run_hook(hook_to_run)
24
25
  end
25
26
 
26
27
  def output_value(key)
@@ -69,6 +69,10 @@ module CfDeployer
69
69
  resources
70
70
  end
71
71
 
72
+ def template
73
+ aws_stack.template
74
+ end
75
+
72
76
  private
73
77
 
74
78
  def cloud_formation
@@ -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
- eval(hook, binding)
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
@@ -92,6 +92,9 @@ module CfDeployer
92
92
  @stack_name
93
93
  end
94
94
 
95
+ def template
96
+ @cf_driver.template
97
+ end
95
98
 
96
99
  private
97
100
 
@@ -1,3 +1,3 @@
1
1
  module CfDeployer
2
- VERSION = "1.3.0"
2
+ VERSION = "1.3.1"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -6,3 +6,4 @@ CfDeployer::Log.log.outputters = nil
6
6
  def puts *args
7
7
 
8
8
  end
9
+
@@ -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
- File.open(@api_json, 'w') {|f| f.write(api_json) }
62
- File.open(@base_json, 'w') {|f| f.write(base_json) }
63
- File.open(@front_end_json, 'w') {|f| f.write(front_end_json) }
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, 'w') {|f| f.write(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])
@@ -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.0
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-07-01 00:00:00.000000000 Z
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