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 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