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 +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +7 -2
- data/Gemfile.lock +36 -45
- data/README.md +40 -0
- data/lib/lono-cfn.rb +2 -0
- data/lib/lono_cfn/aws_services.rb +30 -0
- data/lib/lono_cfn/base.rb +31 -24
- data/lib/lono_cfn/cli.rb +17 -6
- data/lib/lono_cfn/cli/help.rb +16 -0
- data/lib/lono_cfn/create.rb +3 -3
- data/lib/lono_cfn/delete.rb +5 -11
- data/lib/lono_cfn/plan.rb +145 -0
- data/lib/lono_cfn/update.rb +41 -13
- data/lib/lono_cfn/util.rb +17 -0
- data/lib/lono_cfn/version.rb +1 -1
- data/lono_cfn.gemspec +1 -3
- data/spec/lib/cli_spec.rb +7 -2
- data/spec/spec_helper.rb +0 -3
- data/vendor/lono-params/CHANGELOG.md +4 -0
- data/vendor/lono-params/Gemfile.lock +2 -2
- data/vendor/lono-params/README.md +2 -1
- data/vendor/lono-params/lib/lono_params/generator.rb +1 -1
- data/vendor/lono-params/lib/lono_params/version.rb +1 -1
- data/vendor/lono-params/spec/fixtures/my_project/output/params/my-stack.json +14 -0
- metadata +9 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9582b27f0f630faf5a34fa7a07766a6804b373b9
|
4
|
+
data.tar.gz: e14a14ae1d175851827819043716d6df1f8ca7b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
-
|
13
|
+
- vendor/lono-params
|
9
14
|
|
10
15
|
## [0.0.7]
|
11
16
|
|
12
|
-
-
|
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
|
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.
|
24
|
-
aws-sdk-resources (= 2.9.
|
25
|
-
aws-sdk-core (2.9.
|
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.
|
29
|
-
aws-sdk-core (= 2.9.
|
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.
|
34
|
-
simplecov (
|
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.
|
37
|
+
diff-lcs (1.3)
|
38
38
|
docile (1.1.5)
|
39
|
-
ffi (1.9.
|
39
|
+
ffi (1.9.18)
|
40
40
|
formatador (0.2.5)
|
41
|
-
guard (2.14.
|
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 (
|
54
|
+
guard-lono (1.0.1)
|
59
55
|
colorize
|
60
56
|
guard
|
61
|
-
|
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
|
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.
|
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.
|
75
|
+
lumberjack (1.0.12)
|
83
76
|
method_source (0.8.2)
|
84
|
-
minitest (5.10.
|
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 (
|
96
|
-
rb-fsevent (0.9.
|
97
|
-
rb-inotify (0.9.
|
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.
|
100
|
-
rspec-core (~> 3.
|
101
|
-
rspec-expectations (~> 3.
|
102
|
-
rspec-mocks (~> 3.
|
103
|
-
rspec-core (3.
|
104
|
-
rspec-support (~> 3.
|
105
|
-
rspec-expectations (3.
|
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.
|
108
|
-
rspec-mocks (3.
|
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.
|
111
|
-
rspec-support (3.
|
112
|
-
ruby_dep (1.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|
-
|
128
|
-
|
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
|
2
|
-
require
|
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:
|
14
|
-
class_option :params, desc:
|
15
|
-
class_option :lono, type: :boolean, desc:
|
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
|
data/lib/lono_cfn/cli/help.rb
CHANGED
@@ -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
|
data/lib/lono_cfn/create.rb
CHANGED
@@ -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
|
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
|
16
|
+
message = "Creating #{@stack_name} stack."
|
17
17
|
if @options[:noop]
|
18
18
|
message = "NOOP #{message}"
|
19
19
|
else
|
data/lib/lono_cfn/delete.rb
CHANGED
@@ -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
|
-
|
15
|
+
puts "NOOP #{message}"
|
15
16
|
else
|
16
|
-
|
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
|
data/lib/lono_cfn/update.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
data/lib/lono_cfn/version.rb
CHANGED
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 "
|
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("
|
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("
|
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
@@ -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.
|
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.
|
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
|
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:
|
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-
|
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:
|
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.
|
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
|