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 +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
|
+
[](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
|