cf_deployer 1.3.9 → 1.4.0

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: 6d5da90ce16ae1b8de132cba6d1359e3a643d4c9
4
- data.tar.gz: 8d34ac5950b1ce737da92f384c86dc72a0b8128c
3
+ metadata.gz: 6ff8335edd7be5ca468d0a45f6cf614f401cbdce
4
+ data.tar.gz: ceecfb7632c801761d0b655f87a147ce181dfd1f
5
5
  SHA512:
6
- metadata.gz: 05f26ca292c10038313d724d7a24e5b875179aa41f986fa0b757bd7ea16038cd65fa585950bf18282bde4db042dede93dbc7e2d5fe1b6c1f3749bdceab967f12
7
- data.tar.gz: 843e008b514878929b1a1d2802ae2b42b5fe638acc9898f7ca6bdb0d11af0c52d8db541fad2c55f331988dac1ac2b57cd25ca866ccd322607ea3a284ed885635
6
+ metadata.gz: 330695b0672d8af04459a26eb04973cf11e3f95415d453743fb9fed6d269530633bb876d37527e366b990d3980628c3778db8715cfbf9ac40e00eae35b6fecfc
7
+ data.tar.gz: 7416a2d5697ef2d66fcd89b556c1adb902e299f4acfa84c36746c51694d775b8898b39d751485cd50e73666f79e08e79d09f6a37331922809468ef1e09630fb7
data/.travis.yml CHANGED
@@ -8,6 +8,8 @@ rvm:
8
8
  - jruby-19mode
9
9
  - jruby-9.0.1.0
10
10
  - rbx-2
11
+ before_install:
12
+ - gem install bundler
11
13
  matrix:
12
14
  allow_failures:
13
15
  - rvm: ruby-head
data/ChangeLog.md CHANGED
@@ -22,10 +22,10 @@ version 1.2.10
22
22
  - Update DETAILS.md
23
23
 
24
24
  version 1.2.11
25
- - Remove record set in R53 when stacks are deployed
25
+ - Remove record set in R53 when stacks are deployed
26
26
 
27
27
  version 1.3.1
28
- - Adding way to run hooks manually (outside of deploy)
28
+ - Adding way to run hooks manually (outside of deploy)
29
29
  - Adding new command 'diff' to allow diffing between the deployed JSON
30
30
  - Split after-create and after-update hooks for create-or-update strategy
31
31
 
@@ -50,3 +50,7 @@ version 1.3.8
50
50
  version 1.3.9
51
51
  - Allow new ASGs to be added to template (See: https://github.com/manheim/cf_deployer/issues/31)
52
52
 
53
+ version 1.4.0
54
+ - Merge settings from parent component when given (https://github.com/manheim/cf_deployer/pull/37)
55
+ - Added support for stack policies (https://github.com/manheim/cf_deployer/pull/40)
56
+ - Fix broken Travis builds with newer version of bundler (https://github.com/manheim/cf_deployer/pull/42)
data/DETAILS.md CHANGED
@@ -82,6 +82,18 @@ Used by the gem for blue/green deployments and naming conventions
82
82
  * keep-previous-stack (True/False: for Cname-Swap and Auto Scaling Group Swap, previous stack will be kept after new stack is created by default. Set it to false to delete the previous stack)
83
83
  * raise-error-for-unused-inputs (True/False: it is false by default. If it is set to true, errors will be thrown if there are any inputs which are not used as parameters of CloudFormation json templates. If it is set to false or the setting does not exist, warnings will be printed in the console if there are un-used inputs.)
84
84
  * auto-scaling-group-name-output
85
+ * create-stack-policy: The name of the stack policy to be used during the
86
+ creation of the component stack. The location of the policy json file is
87
+ assumed to be "config/{create-stack-policy}.json". The intended use of
88
+ this setting is to make it persistent by placing it in your
89
+ `cf_deployer.yml` file.
90
+ * override-stack-policy: The name of the override stack policy to be used
91
+ during an update of the component stack. The location of the policy json
92
+ file is assumed to be "config/{override-stack-policy}.json". Since the
93
+ override policy is only used occasionally to override the
94
+ `create-stack-policy`, the intended use of this setting is for it to be
95
+ called via the `--settings` cli flag, v.s. being persistently set in the
96
+ config file.
85
97
  * **For Components Using the Cname-Swap Deployment Strategy**
86
98
  * dns-fqdn (DNS record set name, for example, myApp.api.abc.com)
87
99
  * dns-zone (DNS hosted zone, for example, api.abc.com)
@@ -35,7 +35,7 @@ module CfDeployer
35
35
  def json
36
36
  resolve_settings
37
37
  puts "#{name} json template:"
38
- puts ConfigLoader.component_json(name, @context)
38
+ puts ConfigLoader.erb_to_json(name, @context)
39
39
  end
40
40
 
41
41
  def diff
@@ -43,7 +43,7 @@ module CfDeployer
43
43
  current_json = strategy.active_template
44
44
  if current_json
45
45
  puts "#{name} json template diff:"
46
- new_json = ConfigLoader.component_json(name, @context)
46
+ new_json = ConfigLoader.erb_to_json(name, @context)
47
47
  Diffy::Diff.default_format = :color
48
48
  puts Diffy::Diff.new( current_json, new_json )
49
49
  else
@@ -100,8 +100,9 @@ module CfDeployer
100
100
 
101
101
  def resolve_settings
102
102
  inputs.each do |key, value|
103
- if(value.is_a? Hash)
103
+ if(value.is_a?(Hash) && value.key?(:component))
104
104
  dependency = @dependencies.find { |d| d.name == value[:component] }
105
+ raise "No component '#{value[:component]}' found when attempting to derive input '#{key}'" unless dependency
105
106
  output_key = value[:'output-key']
106
107
  inputs[key] = dependency.output_value(output_key)
107
108
  end
@@ -1,10 +1,10 @@
1
1
  module CfDeployer
2
2
  class ConfigLoader
3
3
 
4
- def self.component_json component, config
5
- json_file = File.join(config[:config_dir], "#{component}.json")
4
+ def self.erb_to_json filename, config
5
+ json_file = File.join(config[:config_dir], "#{filename}.json")
6
6
  raise ApplicationError.new("#{json_file} is missing") unless File.exists?(json_file)
7
- CfDeployer::Log.info "ERBing JSON for #{component}"
7
+ CfDeployer::Log.info "ERBing JSON for #{filename}"
8
8
  ERB.new(File.read(json_file)).result(binding)
9
9
  rescue RuntimeError,TypeError,NoMethodError => e
10
10
  self.new.send :error_document, File.read(json_file)
@@ -112,6 +112,8 @@ module CfDeployer
112
112
  if component[:settings][:'keep-previous-stack'] == nil
113
113
  component[:settings][:'keep-previous-stack'] = Defaults::KeepPreviousStack
114
114
  end
115
+ component[:settings][:'create-stack-policy'] ||= Defaults::CreateStackPolicy
116
+ component[:settings][:'override-stack-policy'] ||= Defaults::OverrideStackPolicy
115
117
  end
116
118
  end
117
119
 
@@ -131,7 +133,7 @@ module CfDeployer
131
133
  end
132
134
  end
133
135
 
134
- json_content = self.class.component_json component.to_s, config
136
+ json_content = self.class.erb_to_json component.to_s, config
135
137
  CfDeployer::Log.info "Parsing JSON for #{component}"
136
138
  begin
137
139
  JSON.load json_content
@@ -21,7 +21,6 @@ module CfDeployer
21
21
 
22
22
  private
23
23
 
24
-
25
24
  def check_asg_name_output(component)
26
25
  component[:settings][:'auto-scaling-group-name-output'] ||= []
27
26
  outputs = component[:settings][:'auto-scaling-group-name-output'].map { |name| name.to_sym }
@@ -6,5 +6,7 @@ module CfDeployer
6
6
  DNSDriver = 'CfDeployer::Driver::Route53'
7
7
  RaiseErrorForUnusedInputs = false
8
8
  KeepPreviousStack = true
9
+ CreateStackPolicy = nil
10
+ OverrideStackPolicy = nil
9
11
  end
10
12
  end
@@ -17,13 +17,25 @@ module CfDeployer
17
17
 
18
18
  def deploy
19
19
  config_dir = @context[:config_dir]
20
- template = CfDeployer::ConfigLoader.component_json(@component, @context)
20
+ template = CfDeployer::ConfigLoader.erb_to_json(@component, @context)
21
21
  capabilities = @context[:capabilities] || []
22
22
  notify = @context[:notify] || []
23
23
  tags = @context[:tags] || {}
24
24
  params = to_str(@context[:inputs].select{|key, value| @context[:defined_parameters].keys.include?(key)})
25
25
  CfDeployer::Driver::DryRun.guard "Skipping deploy" do
26
- exists? ? update_stack(template, params, capabilities, tags) : create_stack(template, params, capabilities, tags, notify)
26
+ if exists?
27
+ override_policy_json = nil
28
+ unless @context[:settings][:'override-stack-policy'].nil?
29
+ override_policy_json = CfDeployer::ConfigLoader.erb_to_json(@context[:settings][:'override-stack-policy'], @context)
30
+ end
31
+ update_stack(template, params, capabilities, tags, override_policy_json)
32
+ else
33
+ create_policy_json = nil
34
+ unless @context[:settings][:'create-stack-policy'].nil?
35
+ create_policy_json = CfDeployer::ConfigLoader.erb_to_json(@context[:settings][:'create-stack-policy'], @context)
36
+ end
37
+ create_stack(template, params, capabilities, tags, notify, create_policy_json)
38
+ end
27
39
  end
28
40
  end
29
41
 
@@ -106,22 +118,32 @@ module CfDeployer
106
118
  hash.each { |k,v| hash[k] = v.to_s }
107
119
  end
108
120
 
109
- def update_stack(template, params, capabilities, tags)
121
+ def update_stack(template, params, capabilities, tags, override_policy_json)
110
122
  Log.info "Updating stack #{@stack_name}..."
111
- @cf_driver.update_stack template,
112
- :capabilities => capabilities,
113
- :parameters => params
123
+ args = {
124
+ :capabilities => capabilities,
125
+ :parameters => params
126
+ }
127
+ unless override_policy_json.nil?
128
+ args[:stack_policy_during_update_body] = override_policy_json
129
+ end
130
+ @cf_driver.update_stack(template, args)
114
131
  wait_for_stack_op_terminate
115
132
  end
116
133
 
117
- def create_stack(template, params, capabilities, tags, notify)
134
+ def create_stack(template, params, capabilities, tags, notify, create_policy_json)
118
135
  Log.info "Creating stack #{@stack_name}..."
119
- @cf_driver.create_stack template,
120
- :disable_rollback => true,
121
- :capabilities => capabilities,
122
- :notify => notify,
123
- :tags => reformat_tags(tags),
124
- :parameters => params
136
+ args = {
137
+ :disable_rollback => true,
138
+ :capabilities => capabilities,
139
+ :notify => notify,
140
+ :tags => reformat_tags(tags),
141
+ :parameters => params
142
+ }
143
+ unless create_policy_json.nil?
144
+ args[:stack_policy_body] = create_policy_json
145
+ end
146
+ @cf_driver.create_stack(template, args)
125
147
  wait_for_stack_op_terminate
126
148
  end
127
149
 
@@ -1,3 +1,3 @@
1
1
  module CfDeployer
2
- VERSION = "1.3.9"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -12,13 +12,17 @@ describe "component" do
12
12
  :'vpc-subnets' => {
13
13
  :component => 'base',
14
14
  :'output-key' => 'subnets'
15
+ },
16
+ :'nested-hash' => {
17
+ :foo => 'foo value',
18
+ :bar => 'bar value'
15
19
  }
16
20
  }
17
21
  }
18
22
  @base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
19
23
  @db = CfDeployer::Component.new('myApp', 'uat', 'db', {})
20
24
 
21
- @web = CfDeployer::Component.new('myApp', 'uat', 'web', @context)
25
+ @web = CfDeployer::Component.new('myApp', 'uat', 'component', @context)
22
26
  @web.dependencies << @base
23
27
  @web.dependencies << @db
24
28
  @base.children << @web
@@ -30,13 +34,39 @@ describe "component" do
30
34
  it 'should revolve settings from parent components if parent components has been deployed' do
31
35
  allow(@base).to receive(:exists?){ true }
32
36
  allow(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
33
- expect(CfDeployer::ConfigLoader).to receive(:component_json).with('web', @context)
37
+ expect(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('component', @context)
34
38
  @web.json
35
39
 
36
40
  expect(@context[:inputs][:'vpc-subnets']).to eq('abcd1234, edfas1234')
37
41
  end
38
- end
39
42
 
43
+ it 'should not attempt to resolve settings from referenced component unless component is specified' do
44
+ expected = {
45
+ :foo => 'foo value',
46
+ :bar => 'bar value'
47
+ }
48
+ allow(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
49
+ expect(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('component', @context)
50
+ @web.json
51
+
52
+ expect(@context[:inputs][:'nested-hash']).to eq(expected)
53
+ end
54
+
55
+ it 'should provide a descriptive error message when referenced component does not exist' do
56
+ context = {
57
+ :inputs => {
58
+ :some_key => {
59
+ :component => 'blah',
60
+ :'output-key' => 'referenced_key'
61
+ }
62
+ }
63
+ }
64
+ expected_message = "No component 'blah' found when attempting to derive input 'some_key'"
65
+
66
+ component = CfDeployer::Component.new('myApp', 'uat', 'component', context)
67
+ expect {component.json}.to raise_error(expected_message)
68
+ end
69
+ end
40
70
 
41
71
  it "should destroy component" do
42
72
  expect(@strategy).to receive(:destroy)
@@ -72,7 +102,7 @@ describe "component" do
72
102
  end
73
103
 
74
104
  it "should find direct dependencies" do
75
- web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
105
+ web = CfDeployer::Component.new('myApp', 'uat', 'component', {})
76
106
  base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
77
107
  web.dependencies << base
78
108
 
@@ -80,7 +110,7 @@ describe "component" do
80
110
  end
81
111
 
82
112
  it "should find transitive dependencies" do
83
- web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
113
+ web = CfDeployer::Component.new('myApp', 'uat', 'component', {})
84
114
  haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
85
115
  base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
86
116
 
@@ -91,7 +121,7 @@ describe "component" do
91
121
  end
92
122
 
93
123
  it "should find cyclic dependency" do
94
- web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
124
+ web = CfDeployer::Component.new('myApp', 'uat', 'component', {})
95
125
  haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
96
126
  base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
97
127
  foo = CfDeployer::Component.new('myApp', 'uat', 'foo', {})
@@ -127,7 +157,7 @@ describe "component" do
127
157
  it 'should raise an error that there is no stack for the component' do
128
158
  allow(@strategy).to receive(:exists?) { false }
129
159
  expect(@strategy).not_to receive(:switch)
130
- expect { @web.switch }.to raise_error 'No stack exists for component: web'
160
+ expect { @web.switch }.to raise_error 'No stack exists for component: component'
131
161
  end
132
162
  end
133
163
 
@@ -329,13 +329,13 @@ environments:
329
329
 
330
330
  it "should ERB the component JSON and make the parsed template available" do
331
331
  config = CfDeployer::ConfigLoader.new.load(:'config-file' => @config_file, :environment => 'DrWho')
332
- CfDeployer::ConfigLoader.component_json('json-with-erb', config[:components][:'json-with-erb']).should include('DrWho')
332
+ CfDeployer::ConfigLoader.erb_to_json('json-with-erb', config[:components][:'json-with-erb']).should include('DrWho')
333
333
  end
334
334
 
335
335
  it 'should use error_document to show the broken document when parsing broken ERB' do
336
336
  config = { :config_dir => File.dirname(@config_file) }
337
337
  CfDeployer::ConfigLoader.any_instance.should_receive(:error_document)
338
- expect { CfDeployer::ConfigLoader.component_json('broken_erb', config) }.to raise_error
338
+ expect { CfDeployer::ConfigLoader.erb_to_json('broken_erb', config) }.to raise_error
339
339
  end
340
340
 
341
341
  it 'should use error_document to show the broken document when parsing broken json' do
@@ -1,23 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe CfDeployer::Stack do
4
- before do
5
- @cf_driver = double CfDeployer::Driver::CloudFormation
6
- @stack = CfDeployer::Stack.new('test', 'web', {:cf_driver => @cf_driver})
7
- end
4
+ before do
5
+ @cf_driver = double CfDeployer::Driver::CloudFormation
6
+ @stack = CfDeployer::Stack.new('test', 'web', {:cf_driver => @cf_driver})
7
+ @config = {
8
+ :inputs => { :foo => :bar, :goo => :hoo },
9
+ :tags => { :app => 'app1', :env => 'dev'},
10
+ :defined_parameters => { :foo => 'bar' },
11
+ :notify => ['topic1_arn', 'topic2_arn'],
12
+ :cf_driver => @cf_driver,
13
+ :settings => {
14
+ 'create-stack-policy' => nil,
15
+ 'override-stack-policy' => nil
16
+ }
17
+ }
18
+ end
8
19
 
9
20
  context '#deploy' do
10
- it 'should call CfDeployer::ConfigLoader.component_json to get the JSON for the stack' do
11
- config = { :inputs => { :foo => :bar, :goo => :hoo },
12
- :tags => { :app => 'app1', :env => 'dev'},
13
- :defined_parameters => { :foo => 'bar' },
14
- :notify => ['topic1_arn', 'topic2_arn'],
15
- :cf_driver => @cf_driver }
16
- template = { :resourses => {}}
17
- allow(CfDeployer::ConfigLoader).to receive(:component_json).with('web', config).and_return(template)
21
+ it 'creates a stack, when it doesnt exist' do
22
+ template = { :resources => {}}
23
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('web', @config).and_return(template)
18
24
  allow(@cf_driver).to receive(:stack_exists?) { false }
19
25
  allow(@cf_driver).to receive(:stack_status) { :create_complete }
20
- expected_opt = { :disable_rollback => true,
26
+ expected_opt = {
27
+ :disable_rollback => true,
21
28
  :capabilities => [],
22
29
  :notify => ['topic1_arn', 'topic2_arn'],
23
30
  :tags => [{'Key' => 'app', 'Value' => 'app1'},
@@ -25,9 +32,64 @@ describe CfDeployer::Stack do
25
32
  :parameters => {:foo => 'bar'}
26
33
  }
27
34
  expect(@cf_driver).to receive(:create_stack).with(template, expected_opt)
28
- stack = CfDeployer::Stack.new('test','web', config)
35
+ stack = CfDeployer::Stack.new('test','web', @config)
36
+ stack.deploy
37
+ end
38
+
39
+ it 'creates a stack using a policy when defined' do
40
+ template = { :resources => {}}
41
+ create_policy = { :Statement => [] }
42
+ @config[:settings][:'create-stack-policy'] = 'create-policy'
43
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('web', @config).and_return(template)
44
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('create-policy', @config).and_return(create_policy)
45
+ allow(@cf_driver).to receive(:stack_exists?) { false }
46
+ allow(@cf_driver).to receive(:stack_status) { :create_complete }
47
+ expected_opt = {
48
+ :disable_rollback => true,
49
+ :capabilities => [],
50
+ :notify => ['topic1_arn', 'topic2_arn'],
51
+ :tags => [{'Key' => 'app', 'Value' => 'app1'},
52
+ {'Key' => 'env', 'Value' => 'dev'}],
53
+ :parameters => {:foo => 'bar'},
54
+ :stack_policy_body => create_policy
55
+ }
56
+ expect(@cf_driver).to receive(:create_stack).with(template, expected_opt)
57
+ stack = CfDeployer::Stack.new('test','web', @config)
29
58
  stack.deploy
30
59
  end
60
+
61
+ it 'updates a stack, when it exists' do
62
+ template = { :resources => {}}
63
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('web', @config).and_return(template)
64
+ allow(@cf_driver).to receive(:stack_exists?) { true }
65
+ allow(@cf_driver).to receive(:stack_status) { :create_complete }
66
+ expected_opt = {
67
+ :capabilities => [],
68
+ :parameters => {:foo => 'bar'}
69
+ }
70
+ expect(@cf_driver).to receive(:update_stack).with(template, expected_opt)
71
+ stack = CfDeployer::Stack.new('test','web', @config)
72
+ stack.deploy
73
+ end
74
+
75
+ it 'updates a stack using the override policy, when defined' do
76
+ template = { :resources => {}}
77
+ override_policy = { :Statement => [] }
78
+ @config[:settings][:'override-stack-policy'] = 'override-policy'
79
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('web', @config).and_return(template)
80
+ allow(CfDeployer::ConfigLoader).to receive(:erb_to_json).with('override-policy', @config).and_return(override_policy)
81
+ allow(@cf_driver).to receive(:stack_exists?) { true }
82
+ allow(@cf_driver).to receive(:stack_status) { :create_complete }
83
+ expected_opt = {
84
+ :capabilities => [],
85
+ :parameters => {:foo => 'bar'},
86
+ :stack_policy_during_update_body => override_policy
87
+ }
88
+ expect(@cf_driver).to receive(:update_stack).with(template, expected_opt)
89
+ stack = CfDeployer::Stack.new('test','web', @config)
90
+ stack.deploy
91
+ end
92
+
31
93
  end
32
94
 
33
95
  context '#parameters' do
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.9
4
+ version: 1.4.0
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: 2015-11-09 00:00:00.000000000 Z
14
+ date: 2016-04-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: aws-sdk