lono-cfn 0.1.0 → 1.0.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: 2b769083a5eafc274765550bd3d69b95e05f8e4e
4
- data.tar.gz: 7b810af1565e85e98f74e25d02631a6aaab8ba28
3
+ metadata.gz: 9582b27f0f630faf5a34fa7a07766a6804b373b9
4
+ data.tar.gz: e14a14ae1d175851827819043716d6df1f8ca7b9
5
5
  SHA512:
6
- metadata.gz: 2b310191b6dd9d58dc8f6f1bc5d92d2c16092976ec23347ba9d455604d0f6616bb4e4a12a525f192fbedf6006fa980047a3f89117bf649f0c115842daf37d98f
7
- data.tar.gz: c9d4f8dbd62aaa7cbb11fa0832ea27bf2d7520beb064236e75cc342767d0f3af5f7aa12dfcd4a9feaa78cbb8f04eefa0dea622add6dd36803777c63ebba47e24
6
+ metadata.gz: ad9ffb22a764dd837de2e01bb037985f3c06ed1d2cc2e2de4a7b331af633266d2bed9070362370a5b277c5dafd9cface44dea5933a1d03be689a96be347e7735
7
+ data.tar.gz: 296c15395bcaef3a043127303faa7b932a1ecc90d296ed753c342f69b6583afe75140977cab9e2fe6aa8baf70446ac15458641ed7c0780a1bdf066f337ee908b
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.0
data/CHANGELOG.md CHANGED
@@ -3,13 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.0.0]
7
+
8
+ - add lono-cfn plan
9
+ - fix edge cases and show helpful error messages when stacks are not in updatable statuses
10
+
6
11
  ## [0.0.8]
7
12
 
8
- - vendor/lono-params
13
+ - vendor/lono-params
9
14
 
10
15
  ## [0.0.7]
11
16
 
12
- - allow missing params file
17
+ - allow missing params file
13
18
 
14
19
  ## [0.0.6] add delete stack command
15
20
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lono-cfn (0.0.9)
4
+ lono-cfn (1.0.0)
5
5
  aws-sdk
6
6
  colorize
7
7
  hashie
@@ -20,25 +20,25 @@ GEM
20
20
  minitest (~> 5.1)
21
21
  thread_safe (~> 0.3, >= 0.3.4)
22
22
  tzinfo (~> 1.1)
23
- aws-sdk (2.9.15)
24
- aws-sdk-resources (= 2.9.15)
25
- aws-sdk-core (2.9.15)
23
+ aws-sdk (2.9.24)
24
+ aws-sdk-resources (= 2.9.24)
25
+ aws-sdk-core (2.9.24)
26
26
  aws-sigv4 (~> 1.0)
27
27
  jmespath (~> 1.0)
28
- aws-sdk-resources (2.9.15)
29
- aws-sdk-core (= 2.9.15)
28
+ aws-sdk-resources (2.9.24)
29
+ aws-sdk-core (= 2.9.24)
30
30
  aws-sigv4 (1.0.0)
31
31
  builder (3.2.3)
32
32
  byebug (9.0.6)
33
- codeclimate-test-reporter (0.6.0)
34
- simplecov (>= 0.7.1, < 1.0.0)
33
+ codeclimate-test-reporter (1.0.8)
34
+ simplecov (<= 0.13)
35
35
  coderay (1.1.1)
36
36
  colorize (0.8.1)
37
- diff-lcs (1.2.5)
37
+ diff-lcs (1.3)
38
38
  docile (1.1.5)
39
- ffi (1.9.14)
39
+ ffi (1.9.18)
40
40
  formatador (0.2.5)
41
- guard (2.14.0)
41
+ guard (2.14.1)
42
42
  formatador (>= 0.2.4)
43
43
  listen (>= 2.7, < 4.0)
44
44
  lumberjack (~> 1.0)
@@ -47,30 +47,23 @@ GEM
47
47
  pry (>= 0.9.12)
48
48
  shellany (~> 0.0)
49
49
  thor (>= 0.18.1)
50
- guard-bundler (2.1.0)
51
- bundler (~> 1.0)
52
- guard (~> 2.2)
53
- guard-compat (~> 1.1)
54
50
  guard-cloudformation (0.0.3)
55
51
  colorize
56
52
  guard
57
53
  guard-compat (1.2.1)
58
- guard-lono (0.0.2)
54
+ guard-lono (1.0.1)
59
55
  colorize
60
56
  guard
61
- guard-rspec (4.7.3)
62
- guard (~> 2.1)
63
- guard-compat (~> 1.1)
64
- rspec (>= 2.99.0, < 4.0)
57
+ guard-compat
65
58
  hashie (3.5.5)
66
59
  i18n (0.8.1)
67
60
  jmespath (1.3.1)
68
- json (2.0.2)
61
+ json (2.1.0)
69
62
  listen (3.1.5)
70
63
  rb-fsevent (~> 0.9, >= 0.9.4)
71
64
  rb-inotify (~> 0.9, >= 0.9.7)
72
65
  ruby_dep (~> 1.2)
73
- lono (1.0.0)
66
+ lono (1.1.1)
74
67
  colorize
75
68
  guard
76
69
  guard-cloudformation
@@ -79,9 +72,9 @@ GEM
79
72
  rake
80
73
  rb-fsevent
81
74
  thor
82
- lumberjack (1.0.10)
75
+ lumberjack (1.0.12)
83
76
  method_source (0.8.2)
84
- minitest (5.10.1)
77
+ minitest (5.10.2)
85
78
  nenv (0.3.0)
86
79
  notiffany (0.1.1)
87
80
  nenv (~> 0.1)
@@ -92,36 +85,36 @@ GEM
92
85
  coderay (~> 1.1.0)
93
86
  method_source (~> 0.8.1)
94
87
  slop (~> 3.4)
95
- rake (11.3.0)
96
- rb-fsevent (0.9.7)
97
- rb-inotify (0.9.7)
88
+ rake (12.0.0)
89
+ rb-fsevent (0.9.8)
90
+ rb-inotify (0.9.8)
98
91
  ffi (>= 0.5.0)
99
- rspec (3.5.0)
100
- rspec-core (~> 3.5.0)
101
- rspec-expectations (~> 3.5.0)
102
- rspec-mocks (~> 3.5.0)
103
- rspec-core (3.5.3)
104
- rspec-support (~> 3.5.0)
105
- rspec-expectations (3.5.0)
92
+ rspec (3.6.0)
93
+ rspec-core (~> 3.6.0)
94
+ rspec-expectations (~> 3.6.0)
95
+ rspec-mocks (~> 3.6.0)
96
+ rspec-core (3.6.0)
97
+ rspec-support (~> 3.6.0)
98
+ rspec-expectations (3.6.0)
106
99
  diff-lcs (>= 1.2.0, < 2.0)
107
- rspec-support (~> 3.5.0)
108
- rspec-mocks (3.5.0)
100
+ rspec-support (~> 3.6.0)
101
+ rspec-mocks (3.6.0)
109
102
  diff-lcs (>= 1.2.0, < 2.0)
110
- rspec-support (~> 3.5.0)
111
- rspec-support (3.5.0)
112
- ruby_dep (1.4.0)
103
+ rspec-support (~> 3.6.0)
104
+ rspec-support (3.6.0)
105
+ ruby_dep (1.5.0)
113
106
  shellany (0.0.1)
114
- simplecov (0.12.0)
107
+ simplecov (0.13.0)
115
108
  docile (~> 1.1.0)
116
109
  json (>= 1.8, < 3)
117
110
  simplecov-html (~> 0.10.0)
118
- simplecov-html (0.10.0)
111
+ simplecov-html (0.10.1)
119
112
  slop (3.6.0)
120
113
  symbolize (4.5.2)
121
114
  activemodel (>= 3.2, < 5)
122
115
  activesupport (>= 3.2, < 5)
123
116
  i18n
124
- thor (0.19.1)
117
+ thor (0.19.4)
125
118
  thread_safe (0.3.6)
126
119
  tzinfo (1.2.3)
127
120
  thread_safe (~> 0.1)
@@ -133,11 +126,9 @@ DEPENDENCIES
133
126
  bundler (~> 1.3)
134
127
  byebug
135
128
  codeclimate-test-reporter
136
- guard
137
- guard-bundler
138
- guard-rspec
139
129
  lono-cfn!
140
130
  rake
131
+ rspec
141
132
 
142
133
  BUNDLED WITH
143
134
  1.14.6
data/README.md CHANGED
@@ -52,6 +52,8 @@ The template by convention defaults to the name of the stack. In turn, the para
52
52
 
53
53
  The conventions allows the command to be a very nice short command that can be easily remembered. For example, these 2 commands are the same:
54
54
 
55
+ ### lono-cfn create
56
+
55
57
  Long form:
56
58
 
57
59
  ```
@@ -85,6 +87,44 @@ $ lono-cfn create my-stack --template different-name3 --params different-name4
85
87
 
86
88
  The template that will be use is output/different-name3.json and the parameters will use params/different-name4.json.
87
89
 
90
+ ### lono-cfn update
91
+
92
+ To update stacks you can use `lono-cfn update`:
93
+
94
+ ```
95
+ $ lono-cfn update my-stack --template template-name --params params-name
96
+ ```
97
+
98
+ By default the update command will display a preview of the stack changes before applying the update and prompt to check if you are sure. If you want to bypass the are you sure prompt, use the `--sure` option.
99
+
100
+ ```
101
+ $ lono-cfn update my-stack --template template-name --params params-name --sure
102
+ ```
103
+
104
+ ### lono-cfn delete
105
+
106
+ ```
107
+ $ lono-cfn delete my-stack --sure
108
+ ```
109
+
110
+ ### lono-cfn plan
111
+
112
+ If you want to see the CloudFormation plan without updating the stack you can also use the `lono-cfn plan` command.
113
+
114
+ ```
115
+ $ lono-cfn plan example --template single_instance --params single_instance
116
+ Using template: output/single_instance.yml
117
+ Using parameters: params/single_instance.txt
118
+ Generating CloudFormation templates:
119
+ ./output/single_instance.yml
120
+ Params file generated for example at ./output/params/example.json
121
+ Generating CloudFormation Change Set for plan.....
122
+ CloudFormation plan for 'example' stack update. Changes:
123
+ Remove AWS::Route53::RecordSet: DnsRecord testsubdomain.sub.tongueroo.com
124
+ $
125
+ ```
126
+
127
+
88
128
  ## Contributing
89
129
 
90
130
  1. Fork it
data/lib/lono-cfn.rb CHANGED
@@ -9,9 +9,11 @@ require "lono-params"
9
9
 
10
10
  module LonoCfn
11
11
  autoload :AwsServices, 'lono_cfn/aws_services'
12
+ autoload :Util, 'lono_cfn/util'
12
13
  autoload :CLI, 'lono_cfn/cli'
13
14
  autoload :Base, 'lono_cfn/base'
14
15
  autoload :Create, 'lono_cfn/create'
15
16
  autoload :Update, 'lono_cfn/update'
16
17
  autoload :Delete, 'lono_cfn/delete'
18
+ autoload :Plan, 'lono_cfn/plan'
17
19
  end
@@ -5,5 +5,35 @@ module LonoCfn
5
5
  def cfn
6
6
  @cfn ||= Aws::CloudFormation::Client.new
7
7
  end
8
+
9
+ def stack_exists?(stack_name)
10
+ return true if testing_update?
11
+ return false if @options[:noop]
12
+
13
+ exist = nil
14
+ begin
15
+ # When the stack does not exist an exception is raised. Example:
16
+ # Aws::CloudFormation::Errors::ValidationError: Stack with id blah does not exist
17
+ resp = cfn.describe_stacks(stack_name: stack_name)
18
+ exist = true
19
+ rescue Aws::CloudFormation::Errors::ValidationError => e
20
+ if e.message =~ /does not exist/
21
+ exist = false
22
+ elsif e.message.include?("'stackName' failed to satisfy constraint")
23
+ # Example of e.message when describe_stack with invalid stack name
24
+ # "1 validation error detected: Value 'instance_and_route53' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*"
25
+ puts "Invalid stack name: #{stack_name}"
26
+ puts "Full error message: #{e.message}"
27
+ exit 1
28
+ else
29
+ raise # re-raise exception because unsure what other errors can happen
30
+ end
31
+ end
32
+ exist
33
+ end
34
+
35
+ def testing_update?
36
+ ENV['TEST'] && self.class.name == "LonoCfn::Update"
37
+ end
8
38
  end
9
39
  end
data/lib/lono_cfn/base.rb CHANGED
@@ -4,6 +4,7 @@ require "lono-params"
4
4
  module LonoCfn
5
5
  class Base
6
6
  include AwsServices
7
+ include Util
7
8
 
8
9
  def initialize(stack_name, options={})
9
10
  @stack_name = stack_name
@@ -32,11 +33,13 @@ module LonoCfn
32
33
  ).run
33
34
  end
34
35
 
35
- def generate_params
36
- generator = LonoParams::Generator.new(@stack_name,
36
+ def generate_params(options={})
37
+ generator_options = {
37
38
  project_root: @project_root,
38
39
  path: @params_path,
39
- allow_no_file: true)
40
+ allow_no_file: true
41
+ }.merge(options)
42
+ generator = LonoParams::Generator.new(@stack_name, generator_options)
40
43
  generator.generate # Writes the json file in CamelCase keys format
41
44
  generator.params # Returns Array in underscore keys format
42
45
  end
@@ -65,24 +68,6 @@ module LonoCfn
65
68
  [errors, warns]
66
69
  end
67
70
 
68
- def stack_exists?
69
- return true if testing_update?
70
- return false if @options[:noop]
71
-
72
- exist = true
73
- begin
74
- # When the stack does not exist an exception is raised. Example:
75
- # Aws::CloudFormation::Errors::ValidationError: Stack with id blah does not exist
76
- response = cfn.describe_stacks(stack_name: @stack_name)
77
- rescue Aws::CloudFormation::Errors::ValidationError => e
78
- e.message
79
- if e.message =~ /Stack with/ || e.message =~ /does not exist/
80
- exist = false
81
- end
82
- end
83
- exist
84
- end
85
-
86
71
  # if existing in params path then use that
87
72
  # if it doesnt assume it is a full path and check that
88
73
  # else fall back to convention, which also eventually gets checked in check_for_errors
@@ -97,7 +82,7 @@ module LonoCfn
97
82
  end
98
83
 
99
84
  def convention_path(name, type)
100
- case type
85
+ path = case type
101
86
  when :template
102
87
  format = detect_format
103
88
  "#{@project_root}/output/#{name}.#{format}"
@@ -106,6 +91,7 @@ module LonoCfn
106
91
  else
107
92
  raise "hell: dont come here"
108
93
  end
94
+ path.sub(/^\.\//, '')
109
95
  end
110
96
 
111
97
  # Returns String with value of "yml" or "json".
@@ -124,8 +110,29 @@ module LonoCfn
124
110
  end
125
111
  end
126
112
 
127
- def testing_update?
128
- ENV['TEST'] && self.class.name == "LonoCfn::Update"
113
+ # All CloudFormation states listed here:
114
+ # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
115
+ def stack_status(stack_name)
116
+ return true if testing_update?
117
+ return false if @options[:noop]
118
+
119
+ resp = cfn.describe_stacks(stack_name: stack_name)
120
+ status = resp.stacks[0].stack_status
121
+ end
122
+
123
+ def exist_unless_updatable(status)
124
+ return true if testing_update?
125
+ return false if @options[:noop]
126
+
127
+ unless status =~ /_COMPLETE$/
128
+ puts "Cannot create a change set for the stack because the #{@stack_name} is not in an updatable state. Stack status: #{status}"
129
+ quit(1)
130
+ end
131
+ end
132
+
133
+ # To allow mocking in specs
134
+ def quit(signal)
135
+ exit signal
129
136
  end
130
137
  end
131
138
  end
data/lib/lono_cfn/cli.rb CHANGED
@@ -1,18 +1,18 @@
1
- require 'thor'
2
- require 'lono_cfn/cli/help'
1
+ require "thor"
2
+ require "lono_cfn/cli/help"
3
3
 
4
4
  module LonoCfn
5
5
 
6
6
  class CLI < Thor
7
7
  class_option :verbose, type: :boolean
8
8
  class_option :noop, type: :boolean
9
- class_option :project_root, desc: "Project folder. Defaults to current directory", default: '.'
9
+ class_option :project_root, desc: "Project folder. Defaults to current directory", default: "."
10
10
  class_option :region, desc: "AWS region"
11
11
 
12
12
  # common to create and update
13
- class_option :template, desc: 'override convention and specify the template file to use'
14
- class_option :params, desc: 'override convention and specify the params file to use'
15
- class_option :lono, type: :boolean, desc: 'invoke lono to generate CloudFormation templates', default: true
13
+ class_option :template, desc: "override convention and specify the template file to use"
14
+ class_option :params, desc: "override convention and specify the params file to use"
15
+ class_option :lono, type: :boolean, desc: "invoke lono to generate CloudFormation templates", default: true
16
16
 
17
17
  desc "create STACK", "create a CloudFormation stack"
18
18
  long_desc Help.create
@@ -22,14 +22,25 @@ module LonoCfn
22
22
 
23
23
  desc "update STACK", "update a CloudFormation stack"
24
24
  long_desc Help.update
25
+ option :change_set, type: :boolean, default: true, desc: "Uses generated change set to update the stack. If false, will perform normal update-stack."
26
+ option :preview, type: :boolean, default: true, desc: "Prints preview of the stack changes before continuing."
27
+ option :sure, type: :boolean, desc: "Skips are you sure prompt"
25
28
  def update(name)
26
29
  Update.new(name, options).run
27
30
  end
28
31
 
29
32
  desc "delete STACK", "delete a CloudFormation stack"
30
33
  long_desc Help.delete
34
+ option :sure, type: :boolean, desc: "Skips are you sure prompt"
31
35
  def delete(name)
32
36
  Delete.new(name, options).run
33
37
  end
38
+
39
+ desc "plan STACK", "preview a CloudFormation stack update"
40
+ long_desc Help.plan
41
+ option :keep, type: :boolean, desc: "keep the changeset instead of deleting it afterwards"
42
+ def plan(name)
43
+ Plan.new(name, options).run
44
+ end
34
45
  end
35
46
  end
@@ -63,6 +63,22 @@ Examples:
63
63
  $ lono-cfn delete my-stack
64
64
 
65
65
  The above command will delete my-stack.
66
+ EOL
67
+ end
68
+
69
+ def plan
70
+ <<-EOL
71
+ Generates a CloudFormation plan. This is similar to a `terraform plan` or puppet's dry-run mode.
72
+
73
+ Example output:
74
+
75
+ CloudFormation plan for 'example' stack update. Changes:
76
+
77
+ Remove AWS::Route53::RecordSet: DnsRecord testsubdomain.sub.tongueroo.com
78
+
79
+ Examples:
80
+
81
+ $ lono-cfn plan my-stack
66
82
  EOL
67
83
  end
68
84
  end
@@ -7,13 +7,13 @@ module LonoCfn
7
7
 
8
8
  # aws cloudformation create-stack --stack-name prod-hi-123456789 --parameters file://output/params/prod-hi-123456789.json --template-body file://output/prod-hi.json
9
9
  def create_stack(params)
10
- if stack_exists?
11
- puts "Cannot create a stack because the #{@stack_name} already exists."
10
+ if stack_exists?(@stack_name)
11
+ puts "Cannot create '#{@stack_name}' stack because it already exists."
12
12
  return
13
13
  end
14
14
 
15
15
  template_body = IO.read(@template_path)
16
- message = "#{@stack_name} stack creating."
16
+ message = "Creating #{@stack_name} stack."
17
17
  if @options[:noop]
18
18
  message = "NOOP #{message}"
19
19
  else
@@ -1,6 +1,7 @@
1
1
  module LonoCfn
2
2
  class Delete
3
3
  include AwsServices
4
+ include Util
4
5
 
5
6
  def initialize(stack_name, options={})
6
7
  @stack_name = stack_name
@@ -11,9 +12,11 @@ module LonoCfn
11
12
  def run
12
13
  message = "Deleted #{@stack_name}."
13
14
  if @options[:noop]
14
- message = "NOOP #{message}"
15
+ puts "NOOP #{message}"
15
16
  else
16
- if stack_exist?
17
+ are_you_sure?(:delete)
18
+
19
+ if stack_exist?(@stack_name)
17
20
  cfn.delete_stack(stack_name: @stack_name)
18
21
  puts message
19
22
  else
@@ -21,14 +24,5 @@ module LonoCfn
21
24
  end
22
25
  end
23
26
  end
24
-
25
- def stack_exist?
26
- begin
27
- cfn.describe_stacks(stack_name: @stack_name)
28
- true
29
- rescue Aws::CloudFormation::Errors::ValidationError
30
- false
31
- end
32
- end
33
27
  end
34
28
  end
@@ -0,0 +1,145 @@
1
+ module LonoCfn
2
+ # Inherit from Base because the initializer is the same
3
+ class Plan < Base
4
+ include AwsServices
5
+
6
+ def setup
7
+ generate_templates if @options[:lono]
8
+ check_for_errors
9
+ @params = generate_params(mute: @options[:mute_params])
10
+ end
11
+
12
+ # Override run from Base superclass, the run method is different enough with Plan
13
+ def run
14
+ setup
15
+
16
+ if @options[:noop]
17
+ puts "NOOP CloudFormation plan for #{@stack_name} update"
18
+ else
19
+ preview_change_set
20
+ delete_change_set unless @options[:keep] # Clean up and delete the change set
21
+ end
22
+ end
23
+
24
+ def preview_change_set
25
+ create_change_set
26
+ display_change_set
27
+ end
28
+
29
+ def create_change_set
30
+ unless stack_exists?(@stack_name)
31
+ puts "Cannot create a change set for the stack because the #{@stack_name} does not exists."
32
+ return
33
+ end
34
+ exist_unless_updatable(stack_status(@stack_name))
35
+
36
+ template_body = IO.read(@template_path)
37
+ begin
38
+ cfn.create_change_set(
39
+ change_set_name: change_set_name,
40
+ stack_name: @stack_name,
41
+ template_body: template_body,
42
+ parameters: @params
43
+ )
44
+ rescue Aws::CloudFormation::Errors::ValidationError => e
45
+ if e.message =~ /^Parameters: /
46
+ puts "Error creating CloudFormation plan because invalid CloudFormation parameters. Full error message:".colorize(:red)
47
+ puts e.message
48
+ quit(1)
49
+ else
50
+ raise
51
+ end
52
+ end
53
+ end
54
+
55
+ def display_change_set
56
+ print "Generating CloudFormation Change Set for plan.."
57
+ change_set = describe_change_set
58
+ until change_set_finished?(change_set) do
59
+ change_set = describe_change_set
60
+ sleep 1
61
+ print '.'
62
+ end
63
+ puts
64
+
65
+ case change_set.status
66
+ when "CREATE_COMPLETE"
67
+ puts "CloudFormation plan for '#{@stack_name}' stack update. Changes:"
68
+ change_set.changes.each do |change|
69
+ display_change(change)
70
+ end
71
+ when "FAILED"
72
+ puts "Fail to create a CloudFormation plan for '#{@stack_name}' stack update. Reason:".colorize(:red)
73
+ puts change_set.status_reason
74
+ quit(1)
75
+ else
76
+ raise "hell: never come here"
77
+ end
78
+ end
79
+
80
+ def delete_change_set
81
+ cfn.delete_change_set(
82
+ change_set_name: change_set_name,
83
+ stack_name: @stack_name
84
+ )
85
+ end
86
+
87
+ def execute_change_set
88
+ cfn.execute_change_set(
89
+ change_set_name: change_set_name,
90
+ stack_name: @stack_name
91
+ )
92
+ end
93
+
94
+ # generates a change set name
95
+ def change_set_name
96
+ @change_set_name ||= "changeset-#{Time.now.strftime("%Y%d%m%H%M%S")}"
97
+ end
98
+
99
+ private
100
+ # Private: formats a Aws::CloudFormation::Types::Change in pretty human readable form
101
+ #
102
+ # change - Aws::CloudFormation::Types::Change
103
+ #
104
+ # Examples
105
+ #
106
+ # display_change(change)
107
+ # => Remove AWS::Route53::RecordSet: DnsRecord testsubdomain.sub.tongueroo.com
108
+ #
109
+ # Returns nil
110
+ #
111
+ # change.to_h
112
+ # {:type=>"Resource",
113
+ # :resource_change=>
114
+ # {:action=>"Remove",
115
+ # :logical_resource_id=>"DnsRecord",
116
+ # :physical_resource_id=>"testsubdomain.sub.tongueroo.com",
117
+ # :resource_type=>"AWS::Route53::RecordSet",
118
+ # :scope=>[],
119
+ # :details=>[]}}
120
+ def display_change(change)
121
+ message = if change.type == "Resource"
122
+ c = change.resource_change
123
+ "#{c.action} #{c.resource_type}: #{c.logical_resource_id} #{c.physical_resource_id}"
124
+ else
125
+ change.to_h
126
+ end
127
+
128
+ colors = { Remove: :red, Add: :green, Modify: :yellow }
129
+ action = change.resource_change.action.to_sym
130
+ message = message.colorize(colors[action]) if colors.has_key?(action)
131
+ puts message
132
+ end
133
+
134
+ def change_set_finished?(change_set)
135
+ change_set.status =~ /_COMPLETE/ || change_set.status == "FAILED"
136
+ end
137
+
138
+ def describe_change_set
139
+ cfn.describe_change_set(
140
+ change_set_name: change_set_name,
141
+ stack_name: @stack_name
142
+ )
143
+ end
144
+ end
145
+ end
@@ -7,30 +7,58 @@ module LonoCfn
7
7
 
8
8
  # aws cloudformation update-stack --stack-name prod-hi-123456789 --parameters file://output/params/prod-hi-123456789.json --template-body file://output/prod-hi.json
9
9
  def update_stack(params)
10
- unless stack_exists?
10
+ unless stack_exists?(@stack_name)
11
11
  puts "Cannot update a stack because the #{@stack_name} does not exists."
12
12
  return
13
13
  end
14
+ exist_unless_updatable(stack_status(@stack_name))
14
15
 
15
- template_body = IO.read(@template_path)
16
- message = "#{@stack_name} stack updating."
16
+ message = "Updating #{@stack_name} stack"
17
17
  error = nil
18
18
  if @options[:noop]
19
19
  message = "NOOP #{message}"
20
20
  else
21
- begin
22
- cfn.update_stack(
23
- stack_name: @stack_name,
24
- template_body: template_body,
25
- parameters: params#,
26
- # capabilities: ["CAPABILITY_IAM"]
27
- )
28
- rescue Aws::CloudFormation::Errors::ValidationError => e
29
- puts "ERROR: #{e.message}".red
30
- error = true
21
+ preview_changes if @options[:preview]
22
+ are_you_sure?(:update)
23
+
24
+ if @options[:change_set] # defaults to this
25
+ message << " via change set: #{plan.change_set_name}"
26
+ change_set_update
27
+ else
28
+ standard_update(params)
31
29
  end
32
30
  end
33
31
  puts message unless @options[:mute] || error
34
32
  end
33
+
34
+ def standard_update(params)
35
+ template_body = IO.read(@template_path)
36
+ begin
37
+ cfn.update_stack(
38
+ stack_name: @stack_name,
39
+ template_body: template_body,
40
+ parameters: params#,
41
+ # capabilities: ["CAPABILITY_IAM"]
42
+ )
43
+ rescue Aws::CloudFormation::Errors::ValidationError => e
44
+ puts "ERROR: #{e.message}".red
45
+ error = true
46
+ end
47
+ end
48
+
49
+ def plan
50
+ return @plan if @plan
51
+ @plan = Plan.new(@stack_name, @options.merge(lono: false, mute_params: true))
52
+ @plan.setup
53
+ @plan
54
+ end
55
+
56
+ def preview_changes
57
+ plan.preview_change_set
58
+ end
59
+
60
+ def change_set_update
61
+ plan.execute_change_set
62
+ end
35
63
  end
36
64
  end
@@ -0,0 +1,17 @@
1
+ module LonoCfn
2
+ module Util
3
+ def are_you_sure?(action)
4
+ if @options[:sure]
5
+ sure = 'y'
6
+ else
7
+ puts "Are you sure you want to want to #{action} the stack with the changes? (y/N)"
8
+ sure = $stdin.gets
9
+ end
10
+
11
+ unless sure =~ /^y/
12
+ puts "Exiting without #{action}"
13
+ exit 0
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module LonoCfn
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
data/lono_cfn.gemspec CHANGED
@@ -27,8 +27,6 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.add_development_dependency "bundler", "~> 1.3"
29
29
  spec.add_development_dependency "rake"
30
- spec.add_development_dependency "guard"
31
- spec.add_development_dependency "guard-bundler"
32
- spec.add_development_dependency "guard-rspec"
30
+ spec.add_development_dependency "rspec"
33
31
  spec.add_development_dependency "byebug"
34
32
  end
data/spec/lib/cli_spec.rb CHANGED
@@ -13,17 +13,22 @@ describe LonoCfn::CLI do
13
13
  describe "lono-cfn" do
14
14
  it "creates stack" do
15
15
  out = execute("bin/lono-cfn create my-stack #{@args}")
16
- expect(out).to include("stack creating.")
16
+ expect(out).to include("Creating")
17
17
  end
18
18
 
19
19
  it "updates stack" do
20
20
  out = execute("bin/lono-cfn update my-stack #{@args}")
21
- expect(out).to include("stack updating.")
21
+ expect(out).to include("Updating")
22
22
  end
23
23
 
24
24
  it "deletes stack" do
25
25
  out = execute("bin/lono-cfn delete my-stack #{@args}")
26
26
  expect(out).to include("Deleted")
27
27
  end
28
+
29
+ it "plans stack" do
30
+ out = execute("bin/lono-cfn plan my-stack #{@args}")
31
+ expect(out).to include("CloudFormation plan")
32
+ end
28
33
  end
29
34
  end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  ENV['TEST'] = '1'
2
2
 
3
- require "codeclimate-test-reporter"
4
- CodeClimate::TestReporter.start
5
-
6
3
  require "pp"
7
4
 
8
5
  root = File.expand_path('../../', __FILE__)
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [0.0.6]
7
+
8
+ - add internal mute option
9
+
6
10
  ## [0.0.5] do not generate param if the value is not set
7
11
 
8
12
  - do not generate param if the value is not set
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lono-params (0.0.5)
4
+ lono-params (0.0.6)
5
5
  colorize
6
6
  hashie
7
7
  plissken
@@ -54,7 +54,7 @@ GEM
54
54
  ruby_dep (~> 1.2)
55
55
  lumberjack (1.0.10)
56
56
  method_source (0.8.2)
57
- minitest (5.10.1)
57
+ minitest (5.10.2)
58
58
  nenv (0.3.0)
59
59
  notiffany (0.1.1)
60
60
  nenv (~> 0.1)
@@ -1,5 +1,7 @@
1
1
  # Lono Params
2
2
 
3
+ [![CircleCI](https://circleci.com/gh/tongueroo/lono-params.svg?style=svg)](https://circleci.com/gh/tongueroo/lono-params)
4
+
3
5
  Tool to generate a CloudFormation params json formatted file from a simple env file.
4
6
 
5
7
  Given:
@@ -9,7 +11,6 @@ Param1=1
9
11
  # comments are fine
10
12
  Param2=2 # comments can go after the line too
11
13
  Param3=use_previous_value # treated specially
12
- $
13
14
  ```
14
15
 
15
16
  This is produced:
@@ -20,7 +20,7 @@ module LonoParams
20
20
  data = convert_to_cfn_format(contents)
21
21
  json = JSON.pretty_generate(data)
22
22
  write_output(json)
23
- puts "Params file generated for #{@name} at #{output_path}"
23
+ puts "Params file generated for #{@name} at #{output_path}" unless @options[:mute]
24
24
  else
25
25
  puts "#{@source_path} could not be found? Are you sure it exist?"
26
26
  exit 1
@@ -1,3 +1,3 @@
1
1
  module LonoParams
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "ParameterKey": "Param1",
4
+ "ParameterValue": "1"
5
+ },
6
+ {
7
+ "ParameterKey": "Param2",
8
+ "ParameterValue": "2"
9
+ },
10
+ {
11
+ "ParameterKey": "Param3",
12
+ "UsePreviousValue": true
13
+ }
14
+ ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lono-cfn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-05 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -123,35 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: guard
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: guard-bundler
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: guard-rspec
126
+ name: rspec
155
127
  requirement: !ruby/object:Gem::Requirement
156
128
  requirements:
157
129
  - - ">="
@@ -190,6 +162,7 @@ files:
190
162
  - ".gitignore"
191
163
  - ".gitmodules"
192
164
  - ".rspec"
165
+ - ".ruby-version"
193
166
  - CHANGELOG.md
194
167
  - Gemfile
195
168
  - Gemfile.lock
@@ -206,7 +179,9 @@ files:
206
179
  - lib/lono_cfn/cli/help.rb
207
180
  - lib/lono_cfn/create.rb
208
181
  - lib/lono_cfn/delete.rb
182
+ - lib/lono_cfn/plan.rb
209
183
  - lib/lono_cfn/update.rb
184
+ - lib/lono_cfn/util.rb
210
185
  - lib/lono_cfn/version.rb
211
186
  - lono_cfn.gemspec
212
187
  - spec/fixtures/my_project/config/lono.rb
@@ -229,6 +204,8 @@ files:
229
204
  - vendor/lono-params/lib/lono_params/generator.rb
230
205
  - vendor/lono-params/lib/lono_params/version.rb
231
206
  - vendor/lono-params/lono_params.gemspec
207
+ - vendor/lono-params/pkg/lono-params-0.0.6.gem
208
+ - vendor/lono-params/spec/fixtures/my_project/output/params/my-stack.json
232
209
  - vendor/lono-params/spec/fixtures/my_project/params/my-stack.txt
233
210
  - vendor/lono-params/spec/lib/cli_spec.rb
234
211
  - vendor/lono-params/spec/spec_helper.rb
@@ -252,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
252
229
  version: '0'
253
230
  requirements: []
254
231
  rubyforge_project:
255
- rubygems_version: 2.5.2
232
+ rubygems_version: 2.6.8
256
233
  signing_key:
257
234
  specification_version: 4
258
235
  summary: Wrapper cfn tool to quickly create CloudFormation stacks from lono templates