lono-cfn 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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