stack_master 0.0.1 → 0.0.2

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: be7dbbbfb5ac18598037c53174655253fef33f73
4
- data.tar.gz: e1b2cb9dd3846993f497dc51a606053fc7670907
3
+ metadata.gz: e2c8551f813ec21a55926c685bf281cf9aae2f33
4
+ data.tar.gz: eabcb88f9bc07a017659ebac349e5a476aca605c
5
5
  SHA512:
6
- metadata.gz: 5fea5f38e52302091b2ab880cb59ca68ad9e60547a8f09b7eceb52611aa23475b4c3131fa62b9074580264315f7a75c0796c9fe9bebb1fd535c63c11ebe7d130
7
- data.tar.gz: 4442a6772b1a8efd992660b9ba720f73208935a1bb0f5760a5e4b2b8ea5d4a1c3675964f4f4c3288d968bf45d3c752c4b05fba49f8487894f995e8cfc4537dbb
6
+ metadata.gz: 2b3242170d4615b24c88b85f1bf948bd69c00eacb83bafb6120dd37b247928648e4f9f0890e0ba3516b4e5e10eaa7bfacb5d1806bc7c3771a53bd5d02a7cd187
7
+ data.tar.gz: 0466d851277d7ec84ff4d1d1ed619f078a08bd0da8de70aad2c5e020537faa99a2a7ab6fa6c5c7229504a9455b5d2db87b140e7438707bfda86d4f21ebda73dd
data/README.md CHANGED
@@ -160,6 +160,15 @@ notification_topic:
160
160
  sns_topic: PagerDuty
161
161
  ```
162
162
 
163
+ ### Latest AMI by Tag
164
+
165
+ Looks up the latest AMI ID by a given set of tags.
166
+
167
+ ```yaml
168
+ web_ami:
169
+ latest_ami_by_tags: role=web,application=myapp
170
+ ```
171
+
163
172
  ## Commands
164
173
 
165
174
  ```bash
@@ -172,7 +181,7 @@ stack_master delete [region-or-alias] [stack-name] # Delete a stack
172
181
  stack_master events [region-or-alias] [stack-name] # Display events for a stack
173
182
  stack_master outputs [region-or-alias] [stack-name] # Display outputs for a stack
174
183
  stack_master resources [region-or-alias] [stack-name] # Display outputs for a stack
175
- stack_master status # Displays the status of each stacks
184
+ stack_master status # Displays the status of each stack
176
185
  ```
177
186
 
178
187
  ## Applying updates
@@ -80,7 +80,7 @@ Feature: Apply command
80
80
  | + "Vpc": { |
81
81
  | Parameters diff: |
82
82
  | KeyName: my-key |
83
- | 2020-10-29 00:00:00 +1100 myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE |
83
+ And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
84
84
  Then the exit status should be 0
85
85
 
86
86
  Scenario: Run apply and don't create the stack
@@ -94,8 +94,7 @@ Feature: Apply command
94
94
  | Parameters diff: |
95
95
  | KeyName: my-key |
96
96
  | aborted |
97
- And the output should not contain all of these lines:
98
- | 2020-10-29 00:00:00 +1100 myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE |
97
+ And the output should not match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
99
98
  Then the exit status should be 0
100
99
 
101
100
  Scenario: Run apply on an existing stack
@@ -171,7 +170,7 @@ Feature: Apply command
171
170
  | + "TestSg": { |
172
171
  | Parameters diff: |
173
172
  | VpcId: vpc-xxxxxx |
174
- | 2020-10-29 00:00:00 +1100 myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE |
173
+ And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
175
174
  Then the exit status should be 0
176
175
 
177
176
  Scenario: Create a stack with a notification ARN and a stack update policy
@@ -16,8 +16,7 @@ Feature: Delete command
16
16
  | stack_id | event_id | stack_name | logical_resource_id | resource_status | resource_type | timestamp |
17
17
  | 1 | 1 | myapp-vpc | myapp-vpc | DELETE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
18
18
  When I run `stack_master delete us-east-1 myapp-vpc --trace` interactively
19
- And the output should contain all of these lines:
20
- | 2020-10-29 00:00:00 +1100 myapp-vpc AWS::CloudFormation::Stack DELETE_COMPLETE |
19
+ And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack DELETE_COMPLETE/
21
20
  Then the exit status should be 0
22
21
 
23
22
  Scenario: Run a delete command on a stack that does not exists
@@ -34,5 +34,4 @@ Feature: Events command
34
34
  | 1 | 1 | myapp-vpc | TestSg | CREATE_COMPLETE | AWS::EC2::SecurityGroup | 2020-10-29 00:00:00 |
35
35
  | 1 | 1 | myapp-vpc | myapp-vpc | CREATE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
36
36
  When I run `stack_master events us-east-1 myapp-vpc --trace`
37
- And the output should contain all of these lines:
38
- | 2020-10-29 00:00:00 +1100 myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE |
37
+ And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
@@ -62,5 +62,5 @@ Feature: Region aliases
62
62
  | + "Vpc": { |
63
63
  | Parameters diff: |
64
64
  | KeyName: my-key |
65
- | 2020-10-29 00:00:00 +1100 myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE |
65
+ And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
66
66
  Then the exit status should be 0
data/lib/stack_master.rb CHANGED
@@ -9,6 +9,7 @@ require 'active_support/core_ext/string'
9
9
  require "erb"
10
10
  require 'sparkle_formation'
11
11
  require 'dotgpg'
12
+ require 'ruby-progressbar'
12
13
 
13
14
  require "stack_master/ctrl_c"
14
15
  require "stack_master/command"
@@ -29,6 +30,7 @@ require "stack_master/parameter_resolvers/stack_output"
29
30
  require "stack_master/parameter_resolvers/secret"
30
31
  require "stack_master/parameter_resolvers/sns_topic_name"
31
32
  require "stack_master/parameter_resolvers/security_group"
33
+ require "stack_master/parameter_resolvers/latest_ami_by_tags"
32
34
  require "stack_master/utils"
33
35
  require "stack_master/config"
34
36
  require "stack_master/stack_definition"
@@ -127,8 +127,15 @@ module StackMaster
127
127
  say "Invalid arguments. stack_master delete [region] [stack_name]"
128
128
  return
129
129
  end
130
- StackMaster.cloud_formation_driver.set_region(args[0])
131
- StackMaster::Commands::Delete.perform(*args)
130
+ # Because delete can work without a stack_master.yml
131
+ if options.config and File.file?(options.config)
132
+ config = load_config(options.config)
133
+ region = Utils.underscore_to_hyphen(config.unalias_region(args[0]))
134
+ else
135
+ region = args[0]
136
+ end
137
+ StackMaster.cloud_formation_driver.set_region(region)
138
+ StackMaster::Commands::Delete.perform(region, args[1])
132
139
  end
133
140
  end
134
141
 
@@ -3,17 +3,31 @@ module StackMaster
3
3
  class Status
4
4
  include Command
5
5
 
6
- def initialize(config)
6
+ def initialize(config, show_progress = true)
7
7
  @config = config
8
+ @show_progress = show_progress
8
9
  end
9
10
 
10
11
  def perform
12
+ progress if @show_progress
13
+ status = @config.stacks.map do |stack_definition|
14
+ status = get_status(stack_definition)
15
+ progress.increment if @show_progress
16
+ status
17
+ end
11
18
  tp.set :io, StackMaster.stdout
12
- tp @config.stacks.map { |stack_definition| get_status(stack_definition) }
19
+ tp status
20
+ StackMaster.stdout.puts " * No echo parameters can't be diffed"
13
21
  end
14
22
 
15
23
  private
16
24
 
25
+ def progress
26
+ @progress ||= ProgressBar.create(title: "Fetching stack information",
27
+ total: @config.stacks.size,
28
+ output: StackMaster.stdout)
29
+ end
30
+
17
31
  def sort_params(hash)
18
32
  hash.sort.to_h
19
33
  end
@@ -30,6 +44,7 @@ module StackMaster
30
44
  differ = StackMaster::StackDiffer.new(proposed_stack, stack)
31
45
  different = differ.body_different? || differ.params_different?
32
46
  stack_status = stack.stack_status
47
+ noecho = !differ.noecho_keys.empty?
33
48
  else
34
49
  different = true
35
50
  stack_status = nil
@@ -39,7 +54,7 @@ module StackMaster
39
54
  different = true
40
55
  end
41
56
 
42
- { region: region, stack_name: stack_name, stack_status: stack_status, different: different ? "Yes" : "No" }
57
+ { region: region, stack_name: stack_name, stack_status: stack_status, different: different ? "Yes" : (noecho ? "No *" : "No") }
43
58
  end
44
59
 
45
60
  end
@@ -0,0 +1,36 @@
1
+ module StackMaster
2
+ module ParameterResolvers
3
+ class LatestAmiByTags
4
+ def initialize(config, stack_definition)
5
+ @config = config
6
+ @stack_definition = stack_definition
7
+ end
8
+
9
+ def resolve(value)
10
+ filters = build_filters(value)
11
+ find_latest_ami(filters).try(:image_id)
12
+ end
13
+
14
+ private
15
+
16
+ def ec2
17
+ @ec2 ||= Aws::EC2::Client.new(region: @stack_definition.region)
18
+ end
19
+
20
+ def build_filters(value)
21
+ value.split(',').map do |tag_with_value|
22
+ tag, value = tag_with_value.strip.split('=')
23
+ { name: "tag:#{tag}", values: [value] }
24
+ end
25
+ end
26
+
27
+ def find_latest_ami(filters)
28
+ images = ec2.describe_images(filters: filters).images
29
+ sorted_images = images.sort do |a, b|
30
+ Time.parse(a.creation_date) <=> Time.parse(b.creation_date)
31
+ end
32
+ sorted_images.last
33
+ end
34
+ end
35
+ end
36
+ end
@@ -8,7 +8,6 @@ module StackMaster
8
8
  end
9
9
 
10
10
  def find(reference)
11
- STDERR.puts "Resolving security group reference '#{reference}'"
12
11
  raise ArgumentError, 'Security group references must be non-empty strings' unless reference.is_a?(String) && !reference.empty?
13
12
 
14
13
  groups = @resource.security_groups({
@@ -7,7 +7,6 @@ module StackMaster
7
7
  end
8
8
 
9
9
  def find(reference)
10
- $stderr.puts "Resolving SNS topic reference '#{reference}'"
11
10
  raise ArgumentError, 'SNS topic references must be non-empty strings' unless reference.is_a?(String) && !reference.empty?
12
11
 
13
12
  topic = @resource.topics.detect { |t| topic_name_from_arn(t.arn) == reference }
@@ -18,7 +18,14 @@ module StackMaster
18
18
  end
19
19
 
20
20
  def proposed_parameters
21
- YAML.dump(sort_params(@proposed_stack.parameters_with_defaults))
21
+ # **** out any secret parameters in the current stack.
22
+ params = @proposed_stack.parameters_with_defaults
23
+ if @current_stack
24
+ noecho_keys.each do |key|
25
+ params[key] = "****"
26
+ end
27
+ end
28
+ YAML.dump(sort_params(params))
22
29
  end
23
30
 
24
31
  def body_different?
@@ -33,6 +40,9 @@ module StackMaster
33
40
  if @current_stack
34
41
  text_diff('Stack', current_template, proposed_template, context: 7, include_diff_info: true)
35
42
  text_diff('Parameters', current_parameters, proposed_parameters)
43
+ unless noecho_keys.empty?
44
+ StackMaster.stdout.puts " * can not tell if NoEcho parameters are different."
45
+ end
36
46
  else
37
47
  text_diff('Stack', '', proposed_template)
38
48
  text_diff('Parameters', '', proposed_parameters)
@@ -40,6 +50,16 @@ module StackMaster
40
50
  end
41
51
  end
42
52
 
53
+ def noecho_keys
54
+ if @current_stack
55
+ @current_stack.parameters_with_defaults.select do |key, value|
56
+ value == "****"
57
+ end.keys
58
+ else
59
+ []
60
+ end
61
+ end
62
+
43
63
  private
44
64
 
45
65
  def text_diff(thing, current, proposed, diff_opts = {})
@@ -1,3 +1,3 @@
1
1
  module StackMaster
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  RSpec.describe StackMaster::Commands::Status do
2
- subject(:status) { described_class.new(config) }
2
+ subject(:status) { described_class.new(config, false) }
3
3
  let(:config) { instance_double(StackMaster::Config, stacks: stacks) }
4
4
  let(:stacks) { [stack_definition_1, stack_definition_2] }
5
5
  let(:stack_definition_1) { double(:stack_definition_1, region: 'us-east-1', stack_name: 'stack1') }
@@ -19,7 +19,7 @@ RSpec.describe StackMaster::Commands::Status do
19
19
  let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", parameters_with_defaults: {a: 1}) }
20
20
  let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", parameters_with_defaults: {a: 1}) }
21
21
  it "returns the status of call stacks" do
22
- out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | No \nus-east-1 | stack2 | CREATE_COMPLETE | Yes \n"
22
+ out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | No \nus-east-1 | stack2 | CREATE_COMPLETE | Yes \n * No echo parameters can't be diffed\n"
23
23
  expect { status.perform }.to output(out).to_stdout
24
24
  end
25
25
  end
@@ -30,7 +30,7 @@ RSpec.describe StackMaster::Commands::Status do
30
30
  let(:proposed_stack1) { double(:proposed_stack1, template_body: "{}", parameters_with_defaults: {a: 1}) }
31
31
  let(:proposed_stack2) { double(:proposed_stack2, template_body: "{}", parameters_with_defaults: {a: 1}) }
32
32
  it "returns the status of call stacks" do
33
- out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | Yes \nus-east-1 | stack2 | CREATE_COMPLETE | No \n"
33
+ out = "REGION | STACK_NAME | STACK_STATUS | DIFFERENT\n----------|------------|-----------------|----------\nus-east-1 | stack1 | UPDATE_COMPLETE | Yes \nus-east-1 | stack2 | CREATE_COMPLETE | No \n * No echo parameters can't be diffed\n"
34
34
  expect { status.perform }.to output(out).to_stdout
35
35
  end
36
36
  end
@@ -0,0 +1,33 @@
1
+ RSpec.describe StackMaster::ParameterResolvers::LatestAmiByTags do
2
+ let(:config) { double(base_dir: '/base') }
3
+ let(:stack_definition) { double(stack_name: 'mystack', region: 'us-east-1') }
4
+ subject(:resolver) { described_class.new(config, stack_definition) }
5
+ let(:ec2) { Aws::EC2::Client.new }
6
+
7
+ before do
8
+ allow(Aws::EC2::Client).to receive(:new).and_return(ec2)
9
+ end
10
+
11
+ context 'when matches are found' do
12
+ before do
13
+ ec2.stub_responses(:describe_images, images: [
14
+ { image_id: '1', creation_date: '2015-01-02 00:00:00', tags: [{ key: 'my-tag', value: 'my-value' }] },
15
+ { image_id: '2', creation_date: '2015-01-03 00:00:00', tags: [{ key: 'my-tag', value: 'my-value' }] }
16
+ ])
17
+ end
18
+
19
+ it 'returns the latest one' do
20
+ expect(resolver.resolve('my-tag=my-value')).to eq '2'
21
+ end
22
+ end
23
+
24
+ context 'when no matches are found' do
25
+ before do
26
+ ec2.stub_responses(:describe_images, images: [])
27
+ end
28
+
29
+ it 'returns nil' do
30
+ expect(resolver.resolve('my-tag=my-value')).to be_nil
31
+ end
32
+ end
33
+ end
@@ -1,17 +1,27 @@
1
1
  RSpec.describe StackMaster::StackDiffer do
2
2
  subject(:differ) { described_class.new(proposed_stack, stack) }
3
+ let(:current_params) { Hash.new }
4
+ let(:proposed_params) { { 'param1' => 'hello'} }
3
5
  let(:stack) { StackMaster::Stack.new(stack_name: stack_name,
4
6
  region: region,
5
7
  stack_id: 123,
6
8
  template_body: '{}',
7
- parameters: {}) }
9
+ parameters: current_params) }
8
10
  let(:proposed_stack) { StackMaster::Stack.new(stack_name: stack_name,
9
11
  region: region,
10
- parameters: { 'param1' => 'hello'},
12
+ parameters: proposed_params,
11
13
  template_body: "{\"a\": 1}") }
12
14
  let(:stack_name) { 'myapp-vpc' }
13
15
  let(:region) { 'us-east-1' }
14
16
 
17
+ describe "#proposed_parameters" do
18
+ let(:current_params) { { 'param1' => 'hello',
19
+ 'param2' => '****'} }
20
+ it "stars out noecho params" do
21
+ expect(differ.proposed_parameters).to eq "---\nparam1: hello\nparam2: \"****\"\n"
22
+ end
23
+ end
24
+
15
25
  describe "#output_diff" do
16
26
  context "entirely new stack" do
17
27
  let(:stack) { nil }
@@ -12,7 +12,7 @@ RSpec.describe StackMaster::StackEvents::Presenter do
12
12
  subject(:print_event) { described_class.print_event($stdout, event) }
13
13
 
14
14
  it "nicely presents event data" do
15
- expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 +1100 MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
15
+ expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 #{Time.now.strftime('%z')} MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
16
16
  end
17
17
  end
18
18
  end
data/stack_master.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "cucumber"
26
26
  spec.add_development_dependency "aruba"
27
27
  spec.add_development_dependency "timecop"
28
+ spec.add_dependency "ruby-progressbar"
28
29
  spec.add_dependency "commander"
29
30
  spec.add_dependency "virtus"
30
31
  spec.add_dependency "aws-sdk"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack_master
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Hodgkiss
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-11-04 00:00:00.000000000 Z
12
+ date: 2015-11-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -109,6 +109,20 @@ dependencies:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: ruby-progressbar
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
112
126
  - !ruby/object:Gem::Dependency
113
127
  name: commander
114
128
  requirement: !ruby/object:Gem::Requirement
@@ -302,6 +316,7 @@ files:
302
316
  - lib/stack_master/ctrl_c.rb
303
317
  - lib/stack_master/parameter_loader.rb
304
318
  - lib/stack_master/parameter_resolver.rb
319
+ - lib/stack_master/parameter_resolvers/latest_ami_by_tags.rb
305
320
  - lib/stack_master/parameter_resolvers/secret.rb
306
321
  - lib/stack_master/parameter_resolvers/security_group.rb
307
322
  - lib/stack_master/parameter_resolvers/sns_topic_name.rb
@@ -338,6 +353,7 @@ files:
338
353
  - spec/stack_master/config_spec.rb
339
354
  - spec/stack_master/parameter_loader_spec.rb
340
355
  - spec/stack_master/parameter_resolver_spec.rb
356
+ - spec/stack_master/parameter_resolvers/latest_ami_by_tag_spec.rb
341
357
  - spec/stack_master/parameter_resolvers/secret_spec.rb
342
358
  - spec/stack_master/parameter_resolvers/security_group_spec.rb
343
359
  - spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb
@@ -409,6 +425,7 @@ test_files:
409
425
  - spec/stack_master/config_spec.rb
410
426
  - spec/stack_master/parameter_loader_spec.rb
411
427
  - spec/stack_master/parameter_resolver_spec.rb
428
+ - spec/stack_master/parameter_resolvers/latest_ami_by_tag_spec.rb
412
429
  - spec/stack_master/parameter_resolvers/secret_spec.rb
413
430
  - spec/stack_master/parameter_resolvers/security_group_spec.rb
414
431
  - spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb