cf_deployer 1.2.8 → 1.2.11

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: 2678998aaac0ea19f9aa0694359f9a54b92331a5
4
- data.tar.gz: 62266fe192f394c3f1abc15c0002742a09add738
3
+ metadata.gz: 4084bea273ad75869cd75c03ba6c6eb98496b2dc
4
+ data.tar.gz: ce1a68d375d5058e6b1c4d7cb8566542c7499bab
5
5
  SHA512:
6
- metadata.gz: 65bc63a0d516b1aa7a77c94d8cb4269a1231f81c062ea0716c5d737e703eed7cd862c6b04a9dd9a51b38fda39552886161a194602a83defe7436ad9e5e7edd02
7
- data.tar.gz: 0150e15aa2782cb98d65d7da9439c63724224810eb8eb8b186dbff765157b717b6bd750bceb6a3d18f933f862d5bcec2c0f13fabfb3f01fbf9a38be3d61a0a42
6
+ metadata.gz: 0227d848398efd43b2a8a007e159ff1cbbce34172f0fa592ca1b7d49e3bc8c0cdc01291946c09439541ab6d50b9efa49b5ca658188df0c71c00ef32f494cf1f7
7
+ data.tar.gz: 78932f8530e973d070eaa01565b0fd457db7718720a21ac2bd0390147ac7266d9e0ecc4589cfd6a4b30a6abf7a63aba788151bd62b8512ae5cb25493b2ab653f
data/ChangeLog.md CHANGED
@@ -14,3 +14,12 @@ version 1.2.7
14
14
 
15
15
  version 1.2.8
16
16
  - Removed configure option validion at root level. Now users can have their own options at the root level in cf_deployer.yml file.
17
+
18
+ version 1.2.9
19
+ - Support notify option in cf_deployer.yml, which can be set to ARNs of AWS topics to get notification of events of cloud-formation stacks.
20
+
21
+ version 1.2.10
22
+ - Update DETAILS.md
23
+
24
+ version 1.2.11
25
+ - Remove record set in R53 when stacks are deployed
data/DETAILS.md CHANGED
@@ -50,7 +50,7 @@ in the examples below
50
50
 
51
51
  For Blue/Green deployments keep-previous-stack is defaulted to true.
52
52
  This means that after a deployment, the previous deployed stack will be kept.
53
- For non-production environment, you may set it to false to delete the previous stack
53
+ For non-production environment, you may set it to false to delete the previous stack
54
54
  after a deployment for saving cost.
55
55
 
56
56
  If the CloudFormation template has the output named 'AutoScalingGroupName'
@@ -78,22 +78,56 @@ There are 3 strategies for deploying with cf_deployer:
78
78
  Used by the gem for blue/green deployments and naming conventions
79
79
 
80
80
  * **For all deploys - These are the settings you can use**
81
- * application
82
- * component
83
- * environment
84
81
  * dns-driver (defaults to CfDeployer::Driver::Route53)
85
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)
86
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.)
87
- * auto-scaling-group-swap-name-output
84
+ * auto-scaling-group-name-output
88
85
  * **For Components Using the Cname-Swap Deployment Strategy**
89
- * dns-fqdn
90
- * dns-zone
86
+ * dns-fqdn (DNS record set name, for example, myApp.api.abc.com)
87
+ * dns-zone (DNS hosted zone, for example, api.abc.com)
91
88
  * elb-name-output
92
89
 
93
90
  ##### Inputs:
94
91
  Used by CloudFormation in the JSON template (Note: Settings can be used as inputs, but inputs are not used as settings). These can be almost anything. They are defined in the **Parameters** block in the CF template.
95
92
 
96
93
 
94
+
95
+ =================
96
+ ### Tagging Your Stacks
97
+
98
+ You can use tags option to tag your application. The root level tags will be applied to all the components.
99
+ The component level tags will be only applied to that component. When a component with tags is deploying, the co-responding cloud-formation stack and auto-scaling groups and ec2 instances within the stack will be tagged with the tags of the component.
100
+
101
+ ```
102
+ tags:
103
+ project: my-project
104
+ environment: <%= environment %>
105
+ ownerEmail: awesome@domain.com
106
+ components:
107
+ web:
108
+ capabilities:
109
+ - CAPABILITY_IAM
110
+ tags:
111
+ component: web
112
+
113
+ ```
114
+
115
+ =================
116
+ ### Get notification of events of cloud-formation stacks
117
+
118
+ You can create ASW SNS topics and set the notify option to the ARNs of the topics to get notification of the events of cloud-formation stacks.
119
+ The notify option can be set to a string or an array of strings.
120
+
121
+ ```
122
+ notify: arn:foo:boo:mytopic
123
+ components:
124
+ web:
125
+ notify: arn:web:topic
126
+ environments:
127
+ prod:
128
+ notify: arn:only-prod:topic
129
+
130
+ ```
97
131
  ==================
98
132
  ### How Termination Works
99
133
 
@@ -115,22 +149,6 @@ Components may be deleted several different ways:
115
149
  instances
116
150
 
117
151
 
118
- =================
119
- ### Tagging Your Stacks
120
-
121
- Tags for cf_deployer:application, cf_deployer:environment, and cf_deployer:component are automatically added to the CF stack by cf_deployer. To add your own tags:
122
-
123
- ```
124
- components:
125
- web:
126
- capabilities:
127
- - CAPABILITY_IAM
128
- tags:
129
- Team: Team Awesome
130
- OwnerEmail: awesome@domain.com
131
-
132
- ```
133
-
134
152
  ==============
135
153
  ### Hooks
136
154
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cf_deployer (1.2.8)
4
+ cf_deployer (1.2.11)
5
5
  aws-sdk
6
6
  log4r
7
7
  rainbow
@@ -10,7 +10,7 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- aws-sdk (1.41.0)
13
+ aws-sdk (1.44.0)
14
14
  json (~> 1.4)
15
15
  nokogiri (>= 1.4.4)
16
16
  coderay (1.1.0)
@@ -16,11 +16,13 @@ module CfDeployer
16
16
  @config[:settings] ||= {}
17
17
  @config[:environments] ||= {}
18
18
  @config[:tags] ||= {}
19
+ @config[:notify] ||= []
19
20
  get_targets
20
21
  copy_config_dir
21
- merge_section(:settings)
22
- merge_section(:inputs)
23
- merge_section(:tags)
22
+ merge_hash(:settings)
23
+ merge_hash(:inputs)
24
+ merge_hash(:tags)
25
+ merge_array(:notify)
24
26
  copy_region_app_env_component
25
27
  get_cf_template_keys('Parameters')
26
28
  get_cf_template_keys('Outputs')
@@ -37,8 +39,25 @@ module CfDeployer
37
39
  raise ApplicationError.new("The config file is not a valid yaml file")
38
40
  end
39
41
 
42
+ def merge_array(section)
43
+ root_value = to_array(@config[section])
44
+ environment_name = @config[:environment] || ''
45
+ environment = @config[:environments][environment_name.to_sym] || {}
46
+ environment_value = to_array(environment[section])
47
+ @config[:components].each do |component_name, component|
48
+ component_value = to_array(component[section])
49
+ component[section] = root_value + component_value + environment_value
50
+ component[section].uniq!
51
+ end
52
+ end
53
+
54
+ def to_array(value)
55
+ return value if value.is_a?(Array)
56
+ return [] unless value
57
+ [value]
58
+ end
40
59
 
41
- def merge_section(section)
60
+ def merge_hash(section)
42
61
  merge_component_options section
43
62
  merge_environment_options(@config[:environment], section)
44
63
  merge_environment_variables section
@@ -6,7 +6,7 @@ module CfDeployer
6
6
 
7
7
  CommonInputs = [:application, :environment, :component, :region]
8
8
  EnvironmentOptions = [:settings, :inputs, :tags, :components]
9
- ComponentOptions = [:settings, :inputs, :tags, :'depends-on', :'deployment-strategy', :'before-destroy', :'after-create', :'after-swap', :'defined_outputs', :'defined_parameters', :config_dir, :capabilities]
9
+ ComponentOptions = [:settings, :inputs, :tags, :'depends-on', :'deployment-strategy', :'before-destroy', :'after-create', :'after-swap', :'defined_outputs', :'defined_parameters', :config_dir, :capabilities, :notify]
10
10
 
11
11
  def validate config, validate_inputs = true
12
12
  @config = config
@@ -71,14 +71,12 @@ module CfDeployer
71
71
 
72
72
  end
73
73
 
74
-
75
74
  def stack_active?(stack)
76
75
  return false unless stack.exists?
77
76
  get_active_asg(stack).any?
78
77
  end
79
78
 
80
79
 
81
-
82
80
  def get_active_asg stack
83
81
  return [] unless stack && stack.exists?
84
82
  group_ids(stack).select do |id|
@@ -9,8 +9,12 @@ module CfDeployer
9
9
  def destroy
10
10
  delete_stack green_stack
11
11
  delete_stack blue_stack
12
+ destroy_post
12
13
  end
13
14
 
15
+ def destroy_post
16
+ #overwrite in sub class
17
+ end
14
18
 
15
19
  def output_value(key)
16
20
  active_stack ? active_stack.output(key) : "The value will be referenced from the output #{key} of undeployed component #{component_name}"
@@ -28,6 +28,10 @@ module CfDeployer
28
28
  Log.info "#{component_name} switched successfully"
29
29
  end
30
30
 
31
+ def destroy_post
32
+ dns_driver.delete_record_set(dns_zone, dns_fqdn)
33
+ end
34
+
31
35
  private
32
36
 
33
37
 
@@ -5,22 +5,20 @@ module CfDeployer
5
5
  @aws_route53 = aws_route53 || AWS::Route53.new
6
6
  end
7
7
 
8
- def find_alias_target(target_zone_name, target_host_name)
9
- target_zone = @aws_route53.hosted_zones.find { |z| z.name == trailing_dot(target_zone_name.downcase) }
10
- raise ApplicationError.new('Target zone not found!') if target_zone.nil?
11
-
12
- target_host = target_zone.resource_record_sets.find { |r| r.name == trailing_dot(target_host_name.downcase) }
13
- return nil if target_host.nil? || target_host.alias_target.nil?
14
-
15
- remove_trailing_dot(target_host.alias_target[:dns_name])
8
+ def find_alias_target(hosted_zone_name, target_host_name)
9
+ hosted_zone = get_hosted_zone(hosted_zone_name)
10
+ raise ApplicationError.new('Target zone not found!') if hosted_zone.nil?
11
+ record_set = get_record_set(hosted_zone, target_host_name)
12
+ return nil if record_set.nil? || record_set.alias_target.nil?
13
+ remove_trailing_dot(record_set.alias_target[:dns_name])
16
14
  end
17
15
 
18
- def set_alias_target(target_zone_name, target_host_name, elb_hosted_zone_id, elb_dnsname)
19
- Log.info "set alias target --Hosted Zone: #{target_zone_name} --Host Name: #{target_host_name} --ELB DNS Name: #{elb_dnsname} --ELB Zone ID: #{elb_hosted_zone_id}"
20
- target_zone_name = trailing_dot(target_zone_name)
16
+ def set_alias_target(hosted_zone_name, target_host_name, elb_hosted_zone_id, elb_dnsname)
17
+ Log.info "set alias target --Hosted Zone: #{hosted_zone_name} --Host Name: #{target_host_name} --ELB DNS Name: #{elb_dnsname} --ELB Zone ID: #{elb_hosted_zone_id}"
18
+ hosted_zone_name = trailing_dot(hosted_zone_name)
21
19
  target_host_name = trailing_dot(target_host_name)
22
- target_zone = @aws_route53.hosted_zones.find { |z| z.name == target_zone_name }
23
- raise ApplicationError.new('Target zone not found!') if target_zone.nil?
20
+ hosted_zone = @aws_route53.hosted_zones.find { |z| z.name == hosted_zone_name }
21
+ raise ApplicationError.new('Target zone not found!') if hosted_zone.nil?
24
22
 
25
23
  change = {
26
24
  action: "UPSERT",
@@ -36,7 +34,7 @@ module CfDeployer
36
34
  }
37
35
 
38
36
  batch = {
39
- hosted_zone_id: target_zone.path,
37
+ hosted_zone_id: hosted_zone.path,
40
38
  change_batch: {
41
39
  changes: [change]
42
40
  }
@@ -47,6 +45,14 @@ module CfDeployer
47
45
  end
48
46
  end
49
47
 
48
+ def delete_record_set(hosted_zone_name, target_host_name)
49
+ hosted_zone = get_hosted_zone(hosted_zone_name)
50
+ return unless hosted_zone
51
+ record_set = get_record_set(hosted_zone, target_host_name)
52
+ CfDeployer::Driver::DryRun.guard "Skipping Route53 DNS delete" do
53
+ record_set.delete if record_set
54
+ end
55
+ end
50
56
  private
51
57
 
52
58
  def change_resource_record_sets_with_retry(batch)
@@ -65,6 +71,14 @@ module CfDeployer
65
71
  raise ApplicationError.new('Failed to update Route53 alias target record!')
66
72
  end
67
73
 
74
+ def get_hosted_zone(zone_name)
75
+ @aws_route53.hosted_zones.find { |z| z.name == trailing_dot(zone_name.downcase) }
76
+ end
77
+
78
+ def get_record_set(hosted_zone, target_host_name)
79
+ hosted_zone.resource_record_sets.find { |r| r.name == trailing_dot(target_host_name.downcase) }
80
+ end
81
+
68
82
  def trailing_dot(text)
69
83
  return text if text[-1] == '.'
70
84
  "#{text}."
@@ -19,10 +19,11 @@ module CfDeployer
19
19
  config_dir = @context[:config_dir]
20
20
  template = CfDeployer::ConfigLoader.component_json(@component, @context)
21
21
  capabilities = @context[:capabilities] || []
22
- tags = @context[:tags] || []
22
+ notify = @context[:notify] || []
23
+ tags = @context[:tags] || {}
23
24
  params = to_str(@context[:inputs].select{|key, value| @context[:defined_parameters].keys.include?(key)})
24
25
  CfDeployer::Driver::DryRun.guard "Skipping deploy" do
25
- exists? ? update_stack(template, params, capabilities, tags) : create_stack(template, params, capabilities, tags)
26
+ exists? ? update_stack(template, params, capabilities, tags) : create_stack(template, params, capabilities, tags, notify)
26
27
  end
27
28
  end
28
29
 
@@ -106,11 +107,12 @@ module CfDeployer
106
107
  wait_for_stack_op_terminate
107
108
  end
108
109
 
109
- def create_stack(template, params, capabilities, tags)
110
+ def create_stack(template, params, capabilities, tags, notify)
110
111
  Log.info "Creating stack #{@stack_name}..."
111
112
  @cf_driver.create_stack template,
112
113
  :disable_rollback => true,
113
114
  :capabilities => capabilities,
115
+ :notify => notify,
114
116
  :tags => reformat_tags(tags),
115
117
  :parameters => params
116
118
  wait_for_stack_op_terminate
@@ -1,3 +1,3 @@
1
1
  module CfDeployer
2
- VERSION = "1.2.8"
2
+ VERSION = "1.2.11"
3
3
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  describe "load config settings" do
3
3
 
4
4
  before :each do
5
+ Dir.mkdir 'tmp' unless Dir.exists?('tmp')
5
6
  @config_file = File.expand_path("../../../tmp/test_config.yml", __FILE__)
6
7
  @base_json = File.expand_path("../../../tmp/base.json", __FILE__)
7
8
  @api_json = File.expand_path("../../../tmp/api.json", __FILE__)
@@ -69,8 +70,12 @@ application: myApp
69
70
  components:
70
71
  base:
71
72
  deployment-strategy: create-or-update
73
+ tags:
74
+ component: base
72
75
  inputs:
73
76
  foobar: <%= environment %>.IsGreat
77
+ notify:
78
+ - arn:base
74
79
  api:
75
80
  deployment-strategy: auto-scaling-group-swap
76
81
  depends-on:
@@ -79,8 +84,10 @@ components:
79
84
  require-basic-auth: true
80
85
  timeout: 90
81
86
  mail-server: http://api.abc.com
87
+ notify: arn:api
82
88
  front-end:
83
89
  deployment-strategy: cname-swap
90
+ notify: arn:base
84
91
  depends-on:
85
92
  - base
86
93
  - api
@@ -97,12 +104,18 @@ inputs:
97
104
  mail-server: http://abc.com
98
105
  cname: myserver.com
99
106
 
107
+ notify:
108
+ - arn:root
109
+
110
+ tags:
111
+ version: v1.1
100
112
 
101
113
  environments:
102
114
  dev:
103
115
  inputs:
104
116
  mail-server: http://dev.abc.com
105
117
  timeout: 60
118
+ notify: arn:dev
106
119
  production:
107
120
  inputs:
108
121
  requires-basic-auth: true
@@ -167,6 +180,28 @@ environments:
167
180
  config[:components][:'front-end'][:config_dir].should eq(config_dir)
168
181
  end
169
182
 
183
+ it "notify option should be merged to component context" do
184
+ config = CfDeployer::ConfigLoader.new.load({:'config-file' => @config_file, :environment => 'dev'})
185
+ config[:components][:base][:notify].should eq(['arn:root', 'arn:base', 'arn:dev'])
186
+ config[:components][:api][:notify].should eq(['arn:root', 'arn:api', 'arn:dev'])
187
+ config[:components][:'front-end'][:notify].should eq(['arn:root', 'arn:base', 'arn:dev'])
188
+ end
189
+
190
+ it "notify option should be merged to environment context" do
191
+ config = CfDeployer::ConfigLoader.new.load({:'config-file' => @config_file, :environment => 'uat'})
192
+ config[:components][:base][:notify].should eq(['arn:root', 'arn:base'])
193
+ config[:components][:api][:notify].should eq(['arn:root', 'arn:api'])
194
+ config[:components][:'front-end'][:notify].should eq(['arn:root', 'arn:base'])
195
+ end
196
+
197
+ it "tags option should be copied to component context" do
198
+ config = CfDeployer::ConfigLoader.new.load({:'config-file' => @config_file, :environment => 'uat'})
199
+ config[:components][:api][:tags].should eq({:version => 'v1.1'})
200
+ config[:components][:base][:tags].should eq({:version => 'v1.1', :component => 'base'})
201
+ config[:components][:'front-end'][:tags].should eq({:version => 'v1.1'})
202
+ end
203
+
204
+
170
205
  it "component's settings should be merged to common settings" do
171
206
  config = CfDeployer::ConfigLoader.new.load({:'config-file' => @config_file, :environment => 'uat'})
172
207
  config[:components][:api][:inputs][:'timeout'].should eq(90)
@@ -13,6 +13,7 @@ describe "Config Validation" do
13
13
  :base => {
14
14
  :inputs => {:time_out => 30, :mail_server =>'abc'},
15
15
  :defined_outputs => {:VPCID => {}},
16
+ :notify => [ 'arn1', 'arn2'],
16
17
  :defined_parameters => {:mail_server => {}, :time_out => {}}
17
18
  },
18
19
  :web => {
@@ -20,6 +21,7 @@ describe "Config Validation" do
20
21
  :config_dir => 'samples/simple',
21
22
  :'before-destroy'=> "puts 'destroying'",
22
23
  :capabilities => ['CAPABILITIES_IAM'],
24
+ :notify => 'arn1',
23
25
  :'after-create' => {
24
26
  :file => "after_create_hook.rb",
25
27
  :timeout => 30
@@ -64,6 +64,7 @@ describe CfDeployer::DeploymentStrategy::CnameSwap do
64
64
  allow(before_destroy_hook).to receive(:run) do |arg|
65
65
  @log += "#{arg[:parameters][:name]} deleted."
66
66
  end
67
+ expect(dns_driver).to receive(:delete_record_set).with('foobar.com', 'test.foobar.com')
67
68
  cname_swap.destroy
68
69
  @log.should eq('green deleted.blue deleted.')
69
70
  end
@@ -208,6 +209,7 @@ describe CfDeployer::DeploymentStrategy::CnameSwap do
208
209
  cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
209
210
  expect(blue_stack).to receive(:delete)
210
211
  expect(green_stack).to receive(:delete)
212
+ expect(dns_driver).to receive(:delete_record_set).with('foobar.com', 'test.foobar.com')
211
213
  cname_swap.destroy
212
214
  end
213
215
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe CfDeployer::Hook do
4
4
  before :all do
5
+ Dir.mkdir 'tmp' unless Dir.exists?('tmp')
5
6
  @file = File.expand_path("../../../tmp/test_code.rb", __FILE__)
6
7
  end
7
8
  it 'should eval string as hook' do
@@ -8,14 +8,24 @@ describe CfDeployer::Stack do
8
8
 
9
9
  context '#deploy' do
10
10
  it 'should call CfDeployer::ConfigLoader.component_json to get the JSON for the stack' do
11
- config = { :inputs => { :foo => 'foo' },
12
- :defined_parameters => { :bar => 'bar' },
11
+ config = { :inputs => { :foo => :bar, :goo => :hoo },
12
+ :tags => { :app => 'app1', :env => 'dev'},
13
+ :defined_parameters => { :foo => 'bar' },
14
+ :notify => ['topic1_arn', 'topic2_arn'],
13
15
  :cf_driver => @cf_driver }
14
- CfDeployer::ConfigLoader.should_receive(:component_json).with('web', config)
16
+ template = { :resourses => {}}
17
+ allow(CfDeployer::ConfigLoader).to receive(:component_json).with('web', config).and_return(template)
15
18
  allow(@cf_driver).to receive(:stack_exists?) { false }
16
19
  allow(@cf_driver).to receive(:stack_status) { :create_complete }
20
+ expected_opt = { :disable_rollback => true,
21
+ :capabilities => [],
22
+ :notify => ['topic1_arn', 'topic2_arn'],
23
+ :tags => [{'Key' => 'app', 'Value' => 'app1'},
24
+ {'Key' => 'env', 'Value' => 'dev'}],
25
+ :parameters => {:foo => 'bar'}
26
+ }
27
+ expect(@cf_driver).to receive(:create_stack).with(template, expected_opt)
17
28
  stack = CfDeployer::Stack.new('test','web', config)
18
- allow(stack).to receive(:create_stack)
19
29
  stack.deploy
20
30
  end
21
31
  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.2.8
4
+ version: 1.2.11
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-06-25 00:00:00.000000000 Z
14
+ date: 2014-06-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: aws-sdk
@@ -163,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
163
  version: '0'
164
164
  requirements: []
165
165
  rubyforge_project:
166
- rubygems_version: 2.0.3
166
+ rubygems_version: 2.3.0
167
167
  signing_key:
168
168
  specification_version: 4
169
169
  summary: Support multiple components deployment using CloudFormation templates with