lono 4.2.7 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/.cody/README.md +6 -0
  3. data/.cody/acceptance.sh +30 -0
  4. data/.cody/buildspec.yml +21 -0
  5. data/.cody/demo.rb +38 -0
  6. data/.cody/project.rb +12 -0
  7. data/.cody/role.rb +1 -0
  8. data/.gitignore +2 -0
  9. data/.gitmodules +6 -3
  10. data/.travis.yml +7 -0
  11. data/CHANGELOG.md +10 -0
  12. data/Gemfile +0 -1
  13. data/LICENSE.txt +1 -22
  14. data/README.md +46 -55
  15. data/lib/lono.rb +20 -27
  16. data/lib/lono/app_file.rb +5 -0
  17. data/lib/lono/app_file/base.rb +19 -0
  18. data/lib/lono/app_file/build.rb +78 -0
  19. data/lib/lono/app_file/registry.rb +14 -0
  20. data/lib/lono/app_file/registry/item.rb +46 -0
  21. data/lib/lono/app_file/upload.rb +39 -0
  22. data/lib/lono/autoloader.rb +22 -0
  23. data/lib/lono/aws_services.rb +46 -0
  24. data/lib/lono/aws_services/util.rb +49 -0
  25. data/lib/lono/blueprint.rb +113 -0
  26. data/lib/lono/blueprint/find.rb +90 -0
  27. data/lib/lono/blueprint/helper.rb +18 -0
  28. data/lib/lono/blueprint/info.rb +10 -0
  29. data/lib/lono/blueprint/list.rb +14 -0
  30. data/lib/lono/blueprint/root.rb +43 -0
  31. data/lib/lono/cfn.rb +31 -19
  32. data/lib/lono/cfn/aws_service.rb +16 -0
  33. data/lib/lono/cfn/base.rb +244 -261
  34. data/lib/lono/cfn/create.rb +36 -32
  35. data/lib/lono/cfn/current.rb +1 -1
  36. data/lib/lono/cfn/delete.rb +2 -2
  37. data/lib/lono/cfn/deploy.rb +11 -0
  38. data/lib/lono/cfn/diff.rb +1 -1
  39. data/lib/lono/cfn/preview.rb +3 -3
  40. data/lib/lono/cfn/rollback.rb +26 -0
  41. data/lib/lono/cfn/status.rb +2 -203
  42. data/lib/lono/cfn/suffix.rb +67 -0
  43. data/lib/lono/cfn/update.rb +61 -53
  44. data/lib/lono/cli.rb +42 -23
  45. data/lib/lono/completer.rb +0 -2
  46. data/lib/lono/configure.rb +37 -0
  47. data/lib/lono/configure/aws_services.rb +18 -0
  48. data/lib/lono/configure/base.rb +94 -0
  49. data/lib/lono/configure/helpers.rb +128 -0
  50. data/lib/lono/conventions.rb +11 -0
  51. data/lib/lono/core.rb +42 -12
  52. data/lib/lono/core/config.rb +5 -4
  53. data/lib/lono/default/settings.yml +0 -11
  54. data/lib/lono/file_uploader.rb +9 -4
  55. data/lib/lono/help.rb +1 -2
  56. data/lib/lono/help/blueprint.md +46 -0
  57. data/lib/lono/help/cfn.md +5 -4
  58. data/lib/lono/help/cfn/create.md +14 -9
  59. data/lib/lono/help/cfn/deploy.md +92 -0
  60. data/lib/lono/help/cfn/diff.md +0 -1
  61. data/lib/lono/help/cfn/update.md +16 -15
  62. data/lib/lono/help/completion.md +3 -3
  63. data/lib/lono/help/generate.md +0 -1
  64. data/lib/lono/help/new.md +40 -34
  65. data/lib/lono/help/param.md +1 -1
  66. data/lib/lono/help/param/generate.md +1 -1
  67. data/lib/lono/help/template.md +2 -2
  68. data/lib/lono/help/xgraph.md +1 -1
  69. data/lib/lono/inspector.rb +1 -1
  70. data/lib/lono/inspector/base.rb +26 -10
  71. data/lib/lono/inspector/graph.rb +7 -3
  72. data/lib/lono/inspector/summary.rb +15 -3
  73. data/lib/lono/md5.rb +46 -0
  74. data/lib/lono/new.rb +40 -28
  75. data/lib/lono/new/helper.rb +2 -3
  76. data/lib/lono/param.rb +12 -11
  77. data/lib/lono/param/generator.rb +96 -42
  78. data/lib/lono/project_checker.rb +27 -8
  79. data/lib/lono/s3.rb +23 -0
  80. data/lib/lono/s3/bucket.rb +123 -0
  81. data/lib/lono/script.rb +4 -8
  82. data/lib/lono/script/base.rb +7 -2
  83. data/lib/lono/script/build.rb +7 -8
  84. data/lib/lono/script/upload.rb +4 -20
  85. data/lib/lono/sequence.rb +19 -16
  86. data/lib/lono/setting.rb +19 -27
  87. data/lib/lono/template.rb +22 -26
  88. data/lib/lono/template/base.rb +13 -0
  89. data/lib/lono/template/context.rb +4 -56
  90. data/lib/lono/template/context/loader.rb +70 -0
  91. data/lib/lono/template/dsl.rb +15 -151
  92. data/lib/lono/template/dsl/builder.rb +60 -0
  93. data/lib/lono/template/dsl/builder/base.rb +14 -0
  94. data/lib/lono/template/dsl/builder/condition.rb +26 -0
  95. data/lib/lono/template/dsl/builder/fn.rb +114 -0
  96. data/lib/lono/template/dsl/builder/helper.rb +64 -0
  97. data/lib/lono/template/dsl/builder/mapping.rb +24 -0
  98. data/lib/lono/template/dsl/builder/output.rb +37 -0
  99. data/lib/lono/template/dsl/builder/parameter.rb +39 -0
  100. data/lib/lono/template/dsl/builder/resource.rb +38 -0
  101. data/lib/lono/template/dsl/builder/section.rb +12 -0
  102. data/lib/lono/template/dsl/builder/syntax.rb +58 -0
  103. data/lib/lono/template/erb.rb +82 -0
  104. data/lib/lono/template/evaluate.rb +39 -0
  105. data/lib/lono/template/generator.rb +29 -0
  106. data/lib/lono/template/helper.rb +7 -29
  107. data/lib/lono/template/post_processor.rb +69 -0
  108. data/lib/lono/template/template.rb +4 -9
  109. data/lib/lono/template/upload.rb +103 -133
  110. data/lib/lono/template/util.rb +48 -0
  111. data/lib/lono/upgrade.rb +5 -3
  112. data/lib/lono/upgrade/upgrade5.rb +55 -0
  113. data/lib/lono/user_data.rb +4 -4
  114. data/lib/lono/version.rb +1 -1
  115. data/lib/templates/blueprint/%blueprint_name%.gemspec.tt +44 -0
  116. data/lib/templates/blueprint/.gitignore +14 -0
  117. data/lib/templates/blueprint/.lono/config.yml.tt +3 -0
  118. data/lib/templates/blueprint/.meta/config.yml.tt +3 -0
  119. data/lib/templates/blueprint/CHANGELOG.md +7 -0
  120. data/lib/templates/blueprint/Gemfile +4 -0
  121. data/lib/templates/blueprint/README.md +37 -0
  122. data/lib/templates/blueprint/Rakefile +6 -0
  123. data/lib/templates/blueprint/setup/configs.rb +54 -0
  124. data/lib/templates/blueprint_configs/configs/%blueprint_name%/params/base.txt +2 -0
  125. data/lib/{starter_projects/skeleton/app/definitions/base.rb → templates/blueprint_configs/configs/%blueprint_name%/params/development.txt} +0 -0
  126. data/lib/templates/blueprint_configs/configs/%blueprint_name%/variables/base.rb +2 -0
  127. data/lib/templates/blueprint_configs/configs/%blueprint_name%/variables/development.rb +0 -0
  128. data/lib/templates/blueprint_types/dsl/app/templates/%blueprint_name%.rb +37 -0
  129. data/lib/templates/blueprint_types/dsl/app/user_data/bootstrap.sh +2 -0
  130. data/lib/templates/blueprint_types/erb/app/definitions/base.rb.tt +1 -0
  131. data/lib/templates/blueprint_types/erb/app/templates/%blueprint_name%.yml +8 -0
  132. data/lib/{starter_projects/autoscaling → templates/skeleton}/.gitignore +1 -0
  133. data/lib/templates/skeleton/Gemfile +3 -0
  134. data/lib/{starter_projects/autoscaling → templates/skeleton}/Guardfile +2 -2
  135. data/lib/templates/skeleton/README.md +58 -0
  136. data/lib/templates/skeleton/configs/settings.yml +17 -0
  137. data/lib/templates/upgrade5/blueprints/main/.lono/config.yml +3 -0
  138. data/lib/templates/upgrade5/blueprints/main/.meta/config.yml +3 -0
  139. data/lono.gemspec +12 -8
  140. data/vendor/cfn-status/CHANGELOG.md +10 -0
  141. data/vendor/cfn-status/Gemfile +4 -0
  142. data/vendor/cfn-status/LICENSE.txt +21 -0
  143. data/vendor/cfn-status/README.md +56 -0
  144. data/vendor/cfn-status/Rakefile +6 -0
  145. data/vendor/cfn-status/bin/console +14 -0
  146. data/vendor/cfn-status/bin/setup +8 -0
  147. data/vendor/cfn-status/cfn-status.gemspec +30 -0
  148. data/vendor/cfn-status/lib/cfn-status.rb +1 -0
  149. data/vendor/cfn-status/lib/cfn/aws_service.rb +51 -0
  150. data/vendor/cfn-status/lib/cfn/status.rb +219 -0
  151. data/vendor/cfn-status/lib/cfn/status/version.rb +5 -0
  152. data/vendor/cfn-status/spec/cfn/status_spec.rb +81 -0
  153. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-complete.json +1080 -0
  154. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-in-progress.json +1080 -0
  155. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-update-rollback-complete.json +1086 -0
  156. data/vendor/cfn-status/spec/spec_helper.rb +14 -0
  157. data/vendor/cfn_camelizer/CHANGELOG.md +20 -0
  158. data/vendor/cfn_camelizer/Gemfile +4 -0
  159. data/vendor/cfn_camelizer/LICENSE.txt +21 -0
  160. data/vendor/cfn_camelizer/README.md +40 -0
  161. data/vendor/cfn_camelizer/Rakefile +6 -0
  162. data/vendor/cfn_camelizer/bin/console +14 -0
  163. data/vendor/cfn_camelizer/bin/setup +8 -0
  164. data/vendor/cfn_camelizer/cfn_camelizer.gemspec +32 -0
  165. data/vendor/cfn_camelizer/lib/camelizer.yml +37 -0
  166. data/vendor/cfn_camelizer/lib/cfn_camelizer.rb +94 -0
  167. data/vendor/cfn_camelizer/lib/cfn_camelizer/version.rb +3 -0
  168. data/vendor/cfn_camelizer/spec/cfn_camelizer_spec.rb +86 -0
  169. data/vendor/cfn_camelizer/spec/spec_helper.rb +14 -0
  170. metadata +189 -62
  171. data/.circleci/bin/commit_docs.sh +0 -26
  172. data/.circleci/config.yml +0 -72
  173. data/bin/release +0 -9
  174. data/lib/lono/help/import.md +0 -54
  175. data/lib/lono/importer.rb +0 -134
  176. data/lib/lono/new/message.rb +0 -35
  177. data/lib/starter_projects/autoscaling/Gemfile +0 -3
  178. data/lib/starter_projects/autoscaling/README.md +0 -118
  179. data/lib/starter_projects/autoscaling/app/definitions/base.rb +0 -2
  180. data/lib/starter_projects/autoscaling/app/templates/autoscaling.yml +0 -682
  181. data/lib/starter_projects/autoscaling/config/params/base/autoscaling.txt +0 -6
  182. data/lib/starter_projects/autoscaling/config/settings.yml +0 -33
  183. data/lib/starter_projects/ec2/.gitignore +0 -2
  184. data/lib/starter_projects/ec2/Gemfile +0 -3
  185. data/lib/starter_projects/ec2/Guardfile +0 -12
  186. data/lib/starter_projects/ec2/README.md +0 -86
  187. data/lib/starter_projects/ec2/app/definitions/base.rb +0 -2
  188. data/lib/starter_projects/ec2/app/definitions/development.rb +0 -1
  189. data/lib/starter_projects/ec2/app/definitions/production.rb +0 -1
  190. data/lib/starter_projects/ec2/app/helpers/my_custom_helper.rb +0 -17
  191. data/lib/starter_projects/ec2/app/partials/user_data/bootstrap.sh +0 -4
  192. data/lib/starter_projects/ec2/app/templates/example.yml +0 -430
  193. data/lib/starter_projects/ec2/config/params/base/example.txt +0 -2
  194. data/lib/starter_projects/ec2/config/params/development/example.txt +0 -3
  195. data/lib/starter_projects/ec2/config/params/production/example.txt +0 -2
  196. data/lib/starter_projects/ec2/config/settings.yml +0 -33
  197. data/lib/starter_projects/ec2/config/variables/base.rb +0 -3
  198. data/lib/starter_projects/ec2/config/variables/development.rb +0 -2
  199. data/lib/starter_projects/ec2/config/variables/production.rb +0 -2
  200. data/lib/starter_projects/ec2/welcome.txt +0 -8
  201. data/lib/starter_projects/skeleton/.gitignore +0 -2
  202. data/lib/starter_projects/skeleton/Gemfile +0 -3
  203. data/lib/starter_projects/skeleton/Guardfile +0 -12
  204. data/lib/starter_projects/skeleton/README.md +0 -53
  205. data/lib/starter_projects/skeleton/config/settings.yml +0 -33
  206. data/lib/starter_projects/skeleton/welcome.txt +0 -7
  207. data/vendor/plissken/Gemfile +0 -14
  208. data/vendor/plissken/LICENSE.txt +0 -20
  209. data/vendor/plissken/README.md +0 -46
  210. data/vendor/plissken/Rakefile +0 -56
  211. data/vendor/plissken/VERSION +0 -1
  212. data/vendor/plissken/lib/plissken.rb +0 -1
  213. data/vendor/plissken/lib/plissken/ext/hash/to_snake_keys.rb +0 -45
  214. data/vendor/plissken/plissken.gemspec +0 -61
  215. data/vendor/plissken/spec/lib/to_snake_keys_spec.rb +0 -177
  216. data/vendor/plissken/spec/spec_helper.rb +0 -90
  217. data/vendor/plissken/test/helper.rb +0 -20
  218. data/vendor/plissken/test/plissken/ext/hash/to_snake_keys_test.rb +0 -184
  219. data/vendor/plissken/test/test_plissken.rb +0 -2
@@ -0,0 +1,10 @@
1
+ # Small wrapper class to keep blueprint name and path.
2
+ # Found this to be a little cleaner than using a hash.
3
+ class Lono::Blueprint
4
+ class Info
5
+ attr_reader :name, :path
6
+ def initialize(name, path)
7
+ @name, @path = name, path
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class Lono::Blueprint
2
+ class List
3
+ class << self
4
+ def available
5
+ puts "Current available blueprints:"
6
+ Find.all_blueprints.each do |b|
7
+ full_path = Find.find(b)
8
+ path = full_path.sub("#{Lono.root}/", "")
9
+ puts " #{b}: #{path}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ require "bundler"
2
+
3
+ class Lono::Blueprint
4
+ module Root
5
+ # Switch the lono root
6
+ # TODO: account multimode or only have multimode?
7
+ # TODO: switch to gem path
8
+ def set_blueprint_root(blueprint)
9
+ # puts "blueprint #{blueprint}"
10
+ # puts caller[0..2]
11
+
12
+ blueprint_root = find_blueprint_root(blueprint)
13
+ if blueprint_root
14
+ Lono.blueprint_root = blueprint_root
15
+ else
16
+ puts "ERROR: Unable to find the blueprint #{blueprint}. " \
17
+ "Are you sure it's in your Gemfile or in the blueprints folder "\
18
+ "with the correct blueprint_name in .lono/config.yml?".color(:red)
19
+ List.available
20
+ exit 1
21
+ end
22
+ end
23
+
24
+ def find_blueprint_root(blueprint)
25
+ require_bundle_gems # ensures that gem will be found so we can switch to it
26
+
27
+ Find.find(blueprint) # blueprint_root
28
+ end
29
+
30
+ def bundler_groups
31
+ [:default, Lono.env.to_sym]
32
+ end
33
+
34
+ def require_bundle_gems
35
+ # NOTE: Dont think ENV['BUNDLE_GEMFILE'] is quite working right. We still need
36
+ # to be in the project directory. Leaving logic in here for when it gets fix.
37
+ if ENV['BUNDLE_GEMFILE'] || File.exist?("Gemfile")
38
+ require "bundler/setup"
39
+ Bundler.require(*bundler_groups)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,24 +1,13 @@
1
1
  require "thor"
2
+ require "cfn/status"
2
3
 
3
4
  class Lono::Cfn < Lono::Command
4
- autoload :AwsService, 'lono/cfn/aws_service'
5
- autoload :Base, 'lono/cfn/base'
6
- autoload :CLI, 'lono/cfn/cli'
7
- autoload :Create, 'lono/cfn/create'
8
- autoload :Current, 'lono/cfn/current'
9
- autoload :Delete, 'lono/cfn/delete'
10
- autoload :Diff, 'lono/cfn/diff'
11
- autoload :Download, 'lono/cfn/download'
12
- autoload :Preview, 'lono/cfn/preview'
13
- autoload :Status, 'lono/cfn/status'
14
- autoload :Update, 'lono/cfn/update'
15
- autoload :Util, 'lono/cfn/util'
16
-
17
5
  class_option :verbose, type: :boolean
18
6
  class_option :noop, type: :boolean
19
7
 
20
8
  base_options = Proc.new do
21
9
  # common to create and update
10
+ option :blueprint, desc: "override convention and specify the template file to use"
22
11
  option :template, desc: "override convention and specify the template file to use"
23
12
  option :param, desc: "override convention and specify the param file to use"
24
13
  option :lono, type: :boolean, desc: "invoke lono to generate CloudFormation templates", default: true
@@ -30,9 +19,19 @@ class Lono::Cfn < Lono::Command
30
19
  wait_option = Proc.new do
31
20
  option :wait, type: :boolean, desc: "Wait for stack operation to complete.", default: true
32
21
  end
22
+ suffix_option = Proc.new do
23
+ option :suffix, desc: "Suffix for stack name."
24
+ end
25
+ update_options = Proc.new do
26
+ option :change_set, type: :boolean, default: true, desc: "Uses generated change set to update the stack. If false, will perform normal update-stack."
27
+ option :diff, type: :boolean, default: true, desc: "Show diff of the source code template changes before continuing."
28
+ option :preview, type: :boolean, default: true, desc: "Show preview of the stack changes before continuing."
29
+ option :sure, type: :boolean, desc: "Skips are you sure prompt"
30
+ end
33
31
 
34
32
  desc "create STACK", "Create a CloudFormation stack using the generated template."
35
33
  base_options.call
34
+ suffix_option.call
36
35
  wait_option.call
37
36
  long_desc Lono::Help.text("cfn/create")
38
37
  def create(stack_name)
@@ -41,16 +40,23 @@ class Lono::Cfn < Lono::Command
41
40
 
42
41
  desc "update STACK", "Update a CloudFormation stack using the generated template."
43
42
  long_desc Lono::Help.text("cfn/update")
44
- option :change_set, type: :boolean, default: true, desc: "Uses generated change set to update the stack. If false, will perform normal update-stack."
45
- option :diff, type: :boolean, default: true, desc: "Show diff of the source code template changes before continuing."
46
- option :preview, type: :boolean, default: true, desc: "Show preview of the stack changes before continuing."
47
- option :sure, type: :boolean, desc: "Skips are you sure prompt"
48
43
  base_options.call
44
+ update_options.call
49
45
  wait_option.call
50
46
  def update(stack_name=:current)
51
47
  Update.new(stack_name, options).run
52
48
  end
53
49
 
50
+ desc "deploy STACK", "Create or update a CloudFormation stack using the generated template."
51
+ long_desc Lono::Help.text("cfn/deploy")
52
+ base_options.call
53
+ suffix_option.call
54
+ update_options.call
55
+ wait_option.call
56
+ def deploy(stack_name=:current)
57
+ Deploy.new(stack_name, options).run
58
+ end
59
+
54
60
  desc "delete STACK", "Delete a CloudFormation stack."
55
61
  long_desc Lono::Help.text("cfn/delete")
56
62
  option :sure, type: :boolean, desc: "Skips are you sure prompt"
@@ -65,6 +71,7 @@ class Lono::Cfn < Lono::Command
65
71
  option :keep, type: :boolean, desc: "keep the changeset instead of deleting it afterwards"
66
72
  option :diff, type: :boolean, default: true, desc: "Show diff of the source code template changes also."
67
73
  base_options.call
74
+ suffix_option.call
68
75
  def preview(stack_name=:current)
69
76
  Diff.new(stack_name, options).run if options[:diff]
70
77
  Preview.new(stack_name, options).run
@@ -73,6 +80,7 @@ class Lono::Cfn < Lono::Command
73
80
  desc "diff STACK", "Diff newly generated template vs existing template."
74
81
  long_desc Lono::Help.text("cfn/diff")
75
82
  base_options.call
83
+ suffix_option.call
76
84
  def diff(stack_name=:current)
77
85
  Diff.new(stack_name, options).run
78
86
  end
@@ -81,6 +89,7 @@ class Lono::Cfn < Lono::Command
81
89
  long_desc Lono::Help.text("cfn/download")
82
90
  option :name, desc: "Name you want to save the template as. Default: existing stack name."
83
91
  base_options.call
92
+ suffix_option.call
84
93
  def download(stack_name=:current)
85
94
  Download.new(stack_name, options).run
86
95
  end
@@ -89,14 +98,17 @@ class Lono::Cfn < Lono::Command
89
98
  long_desc Lono::Help.text("cfn/current")
90
99
  option :rm, type: :boolean, desc: "Remove all current settings. Removes `.lono/current`"
91
100
  option :name, desc: "Current stack name."
92
- option :suffix, desc: "Current suffix for stack name."
101
+ suffix_option.call
93
102
  def current
94
103
  Current.new(options).run
95
104
  end
96
105
 
97
106
  desc "status", "Shows the current status for the stack."
98
107
  long_desc Lono::Help.text("cfn/status")
108
+ suffix_option.call
99
109
  def status(stack_name=:current)
100
- Status.new(stack_name, options).run
110
+ status = Lono::Cfn::Status.new(stack_name, options)
111
+ success = status.run
112
+ exit 3 unless success
101
113
  end
102
114
  end
@@ -31,6 +31,22 @@ module Lono::Cfn::AwsService
31
31
  exist
32
32
  end
33
33
 
34
+ def find_stack(stack_name)
35
+ resp = cfn.describe_stacks(stack_name: stack_name)
36
+ resp.stacks.first
37
+ rescue Aws::CloudFormation::Errors::ValidationError => e
38
+ # example: Stack with id demo-web does not exist
39
+ if e.message =~ /Stack with/ && e.message =~ /does not exist/
40
+ nil
41
+ else
42
+ raise
43
+ end
44
+ end
45
+
46
+ def rollback_complete?(stack)
47
+ stack.stack_status == 'ROLLBACK_COMPLETE'
48
+ end
49
+
34
50
  def testing_update?
35
51
  ENV['TEST'] && self.class.name == "LonoCfn::Update"
36
52
  end
@@ -1,318 +1,301 @@
1
1
  require "lono"
2
2
 
3
- class Lono::Cfn::Base
4
- include Lono::Cfn::AwsService
5
- include Lono::Cfn::Util
6
-
7
- def initialize(stack_name, options={})
8
- @options = options # options must be set first because @option used in append_suffix
9
- stack_name = switch_current(stack_name)
10
- @stack_name = append_suffix(stack_name)
11
- Lono::ProjectChecker.check unless options[:lono] # already ran checker in lono generate
12
-
13
- @template_name = options[:template] || remove_suffix(@stack_name)
14
- @param_name = options[:param] || @template_name
15
- @template_path = get_source_path(@template_name, :template)
16
- @param_path = get_source_path(@param_name, :param)
17
- puts "Using template: #{@template_path}" unless @options[:mute_using]
18
- puts "Using parameters: #{@param_path}" unless @options[:mute_using]
19
- end
3
+ class Lono::Cfn
4
+ class Base
5
+ extend Memoist
6
+ include Lono::AwsServices
7
+ include Lono::Blueprint::Root
8
+ include Lono::Conventions
9
+ include Suffix
10
+ include Util
20
11
 
21
- def switch_current(stack_name)
22
- Lono::Cfn::Current.name!(stack_name)
23
- end
12
+ def initialize(stack_name, options={})
13
+ @options = options # options must be set first because @option used in append_suffix
24
14
 
25
- def starting_message
26
- action = self.class.to_s.split('::').last
27
- action = action[0..-2] + 'ing' # create => creating
28
- puts "#{action} #{@stack_name.color(:green)} stack..."
29
- end
15
+ stack_name = switch_current(stack_name)
16
+ @stack_name = append_suffix(stack_name)
17
+ Lono::ProjectChecker.check
30
18
 
31
- def run
32
- starting_message
33
- params = generate_all
34
- begin
35
- save_stack(params) # defined in the sub class
36
- rescue Aws::CloudFormation::Errors::InsufficientCapabilitiesException => e
37
- capabilities = e.message.match(/\[(.*)\]/)[1]
38
- confirm = prompt_for_iam(capabilities)
39
- if confirm =~ /^y/
40
- @options.merge!(capabilities: [capabilities])
41
- puts "Re-running: #{command_with_iam(capabilities).color(:green)}"
42
- retry
43
- else
44
- puts "Exited"
45
- exit 1
46
- end
47
- end
19
+ @blueprint = options[:blueprint] || remove_suffix(@stack_name)
20
+ @template, @param = template_param_convention(options)
48
21
 
49
- return unless @options[:wait]
50
- status.wait unless @options[:noop]
51
- end
22
+ # Add template and param to options because used later for Lono::Param::Generator
23
+ @options[:blueprint], @options[:template], @options[:param] = @blueprint, @template, @param
52
24
 
53
- def status
54
- @status ||= Lono::Cfn::Status.new(@stack_name)
55
- end
25
+ set_blueprint_root(@blueprint)
56
26
 
57
- def prompt_for_iam(capabilities)
58
- puts "This stack will create IAM resources. Please approve to run the command again with #{capabilities} capabilities."
59
- puts " #{command_with_iam(capabilities)}"
27
+ @template_path = "#{Lono.config.output_path}/#{@blueprint}/templates/#{@template}.yml"
28
+ end
60
29
 
61
- puts "Please confirm (y/n)"
62
- $stdin.gets
63
- end
30
+ def switch_current(stack_name)
31
+ Current.name!(stack_name)
32
+ end
64
33
 
65
- def command_with_iam(capabilities)
66
- "#{File.basename($0)} #{ARGV.join(' ')} --capabilities #{capabilities}"
67
- end
34
+ def starting_message
35
+ action = self.class.to_s.split('::').last
36
+ puts "#{action} #{@stack_name.color(:green)} stack..."
37
+ end
68
38
 
69
- def generate_all
70
- if @options[:lono]
71
- build_scripts
72
- generate_templates
73
- unless @options[:noop]
74
- upload_scripts
75
- upload_files
76
- upload_templates
39
+ def run
40
+ starting_message
41
+ params = generate_all
42
+ begin
43
+ save_stack(params) # defined in the sub class
44
+ rescue Aws::CloudFormation::Errors::InsufficientCapabilitiesException => e
45
+ capabilities = e.message.match(/\[(.*)\]/)[1]
46
+ confirm = prompt_for_iam(capabilities)
47
+ if confirm =~ /^y/
48
+ @options.merge!(capabilities: [capabilities])
49
+ puts "Re-running: #{command_with_iam(capabilities).color(:green)}"
50
+ retry
51
+ else
52
+ puts "Exited"
53
+ exit
54
+ end
55
+ rescue Aws::CloudFormation::Errors::ValidationError => e
56
+ if e.message.include?("No updates") # No updates are to be performed.
57
+ puts "WARN: #{e.message}".color(:yellow)
58
+ elsif e.message.include?("UPDATE_ROLLBACK_FAILED") # https://amzn.to/2IiEjc5
59
+ continue_update_rollback
60
+ else
61
+ puts "ERROR: #{e.message}".color(:red)
62
+ exit 1
63
+ end
77
64
  end
65
+
66
+ return unless @options[:wait]
67
+
68
+ success = false
69
+ if !@options[:noop]
70
+ success = status.wait
71
+ end
72
+
73
+ # exit code for cfn.rb cli, so there's less duplication
74
+ exit 1 unless success
75
+ success
78
76
  end
79
- params = generate_params(mute: @options[:mute_params])
80
- check_for_errors
81
- params
82
- end
83
77
 
84
- def build_scripts
85
- Lono::Script::Build.new.run
86
- end
78
+ def continue_update_rollback_sure?
79
+ puts <<~EOL
80
+ The stack is in the UPDATE_ROLLBACK_FAILED state. More info here: https://amzn.to/2IiEjc5
81
+ Would you like to try to continue the update rollback? (y/N)
82
+ EOL
87
83
 
88
- def generate_templates
89
- Lono::Template::DSL.new.run
90
- end
84
+ sure = @options[:sure] ? "y" : $stdin.gets
85
+ unless sure =~ /^y/
86
+ puts "Exiting without continuing the update rollback."
87
+ exit 0
88
+ end
89
+ end
91
90
 
92
- # only upload templates if s3_folder configured in settings
93
- def upload_templates
94
- Lono::Template::Upload.new.run if s3_folder
95
- end
91
+ def continue_update_rollback
92
+ continue_update_rollback_sure?
93
+ params = {stack_name: @stack_name}
94
+ show_parameters(params, "cfn.continue_update_rollback")
95
+ begin
96
+ cfn.continue_update_rollback(params)
97
+ rescue Aws::CloudFormation::Errors::ValidationError => e
98
+ puts "ERROR5: #{e.message}".red
99
+ exit 1
100
+ end
101
+ end
96
102
 
97
- # only upload templates if s3_folder configured in settings
98
- def upload_scripts
99
- return unless s3_folder
100
- Lono::Script::Upload.new.run
101
- end
103
+ def delete_rollback_stack
104
+ rollback = Rollback.new(@stack_name)
105
+ rollback.delete_stack
106
+ end
102
107
 
103
- def upload_files
104
- return unless s3_folder
105
- Lono::FileUploader.new.upload_all
106
- end
108
+ def status
109
+ @status ||= Cfn::Status.new(@stack_name)
110
+ end
107
111
 
108
- def generate_params(options={})
109
- generator_options = {
110
- path: @param_path,
111
- allow_no_file: true
112
- }.merge(options)
113
- generator = Lono::Param::Generator.new(@param_name, generator_options)
114
- generator.generate # Writes the json file in CamelCase keys format
115
- generator.params # Returns Array in underscore keys format
116
- end
112
+ def prompt_for_iam(capabilities)
113
+ puts "This stack will create IAM resources. Please approve to run the command again with #{capabilities} capabilities."
114
+ puts " #{command_with_iam(capabilities)}"
117
115
 
118
- # Maps to CloudFormation format. Example:
119
- #
120
- # {"a"=>"1", "b"=>"2"}
121
- # To
122
- # [{key: "a", value: "1"}, {key: "b", value: "2"}]
123
- #
124
- def tags
125
- tags = @options[:tags] || []
126
- tags = tags.map do |k,v|
127
- { key: k, value: v }
116
+ puts "Please confirm (y/n)"
117
+ $stdin.gets
128
118
  end
129
119
 
130
- update_operation = %w[Lono::Cfn::Preview Lono::Cfn::Update].include?(self.class.to_s)
131
- if tags.empty? && update_operation
132
- resp = cfn.describe_stacks(stack_name: @stack_name)
133
- tags = resp.stacks.first.tags
134
- tags = tags.map(&:to_h)
120
+ def command_with_iam(capabilities)
121
+ "#{File.basename($0)} #{ARGV.join(' ')} --capabilities #{capabilities}"
135
122
  end
136
123
 
137
- tags
138
- end
124
+ # Use class variable to cache this only runs once across all classes. base.rb, diff.rb, preview.rb
125
+ @@generate_all = nil
126
+ def generate_all
127
+ return @@generate_all if @@generate_all
128
+
129
+ if @options[:lono]
130
+ ensure_s3_bucket_exist
131
+
132
+ build_scripts
133
+ generate_templates # generates with some placeholders for build_files IE: file://app/files/my.rb
134
+ build_files # builds app/files to output/BLUEPRINT/files
135
+
136
+ post_process_templates
137
+
138
+ unless @options[:noop]
139
+ upload_files
140
+ upload_scripts
141
+ upload_templates
142
+ end
143
+ end
139
144
 
140
- def check_for_errors
141
- errors, warns = check_files
142
- unless errors.empty?
143
- puts "Please double check the command you ran. There were some errors."
144
- puts "ERROR: #{errors.join("\n")}".color(:red)
145
- exit
145
+ # Pass down all options to generate_params because it eventually uses template
146
+ param_generator.generate # Writes the json file in CamelCase keys format
147
+ @@generate_all = param_generator.params # Returns Array in underscore keys format
148
+
149
+ # At this point we have the info about params path used so we can display it.
150
+ # We display other useful info here too so it's together logically.
151
+ unless @options[:mute_using]
152
+ puts "Using template: #{pretty_path(@template_path)}"
153
+ param_generator.puts_param_message(:base)
154
+ param_generator.puts_param_message(:env)
155
+ end
156
+
157
+ check_for_errors
158
+ @@generate_all
146
159
  end
147
- unless warns.empty?
148
- puts "Please double check the command you ran. There were some warnings."
149
- puts "WARN: #{warns.join("\n")}".color(:yellow)
160
+
161
+ def param_generator
162
+ generator_options = {
163
+ regenerate: false,
164
+ allow_not_exists: true
165
+ }.merge(@options)
166
+ Lono::Param::Generator.new(@blueprint, generator_options)
150
167
  end
151
- end
168
+ memoize :param_generator
152
169
 
153
- def check_files
154
- errors, warns = [], []
155
- unless File.exist?(@template_path)
156
- errors << "Template file missing: could not find #{@template_path}"
170
+ def ensure_s3_bucket_exist
171
+ bucket = Lono::S3::Bucket.new
172
+ return if bucket.exist?
173
+ bucket.create
157
174
  end
158
- # Examples:
159
- # @param_path = params/prod/ecs.txt
160
- # => output/params/prod/ecs.json
161
- output_param_path = @param_path.sub(/\.txt/, '.json')
162
- output_param_path = "#{Lono.config.output_path}/#{output_param_path}"
163
- if @options[:param] && !File.exist?(output_param_path)
164
- warns << "Parameters file missing: could not find #{output_param_path}"
175
+
176
+ def build_scripts
177
+ Lono::Script::Build.new(@blueprint, @options).run
165
178
  end
166
- [errors, warns]
167
- end
168
179
 
169
- # if existing in params path then use that
170
- # if it doesnt assume it is a full path and check that
171
- # else fall back to convention, which also eventually gets checked in check_for_errors
172
- #
173
- # Type - :param or :template
174
- def get_source_path(path, type)
175
- if path.nil?
176
- convention_path(@stack_name, type) # default convention
177
- else
178
- # convention path based on the input from the user
179
- convention_path(path, type)
180
+ def build_files
181
+ Lono::AppFile::Build.new(@blueprint, @options).run
180
182
  end
181
- end
182
183
 
183
- def convention_path(name, type)
184
- path = case type
185
- when :template
186
- "#{Lono.config.output_path}/templates/#{name}.yml"
187
- when :param
188
- "#{Lono.config.params_path}/#{Lono.env}/#{name}.txt"
189
- else
190
- raise "hell: dont come here"
184
+ def generate_templates
185
+ Lono::Template::Generator.new(@blueprint, @options).run
191
186
  end
192
- path.sub(/^\.\//, '')
193
- end
194
187
 
195
- # All CloudFormation states listed here:
196
- # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
197
- def stack_status(stack_name)
198
- return true if testing_update?
199
- return false if @options[:noop]
188
+ def post_process_templates
189
+ Lono::Template::PostProcessor.new(@blueprint, @options).run
190
+ end
200
191
 
201
- resp = cfn.describe_stacks(stack_name: stack_name)
202
- resp.stacks[0].stack_status
203
- end
192
+ def upload_templates
193
+ Lono::Template::Upload.new(@blueprint).run
194
+ end
204
195
 
205
- def exit_unless_updatable!(status)
206
- return true if testing_update?
207
- return false if @options[:noop]
196
+ def upload_scripts
197
+ Lono::Script::Upload.new(@blueprint).run
198
+ end
208
199
 
209
- unless status =~ /_COMPLETE$/
210
- puts "Cannot create a change set for the stack because the #{@stack_name} is not in an updatable state. Stack status: #{status}".color(:red)
211
- quit(1)
200
+ def upload_files
201
+ Lono::AppFile::Upload.new(@blueprint).upload
212
202
  end
213
- end
214
203
 
215
- # To allow mocking in specs
216
- def quit(signal)
217
- exit signal
218
- end
204
+ # Maps to CloudFormation format. Example:
205
+ #
206
+ # {"a"=>"1", "b"=>"2"}
207
+ # To
208
+ # [{key: "a", value: "1"}, {key: "b", value: "2"}]
209
+ #
210
+ def tags
211
+ tags = @options[:tags] || []
212
+ tags = tags.map do |k,v|
213
+ { key: k, value: v }
214
+ end
219
215
 
220
- # Appends a short suffix at the end of a stack name.
221
- # Lono internally strips this same suffix for the template name.
222
- # Makes it convenient for the development flow.
223
- #
224
- # lono cfn current --suffix 1
225
- # lono cfn create demo => demo-1
226
- # lono cfn update demo-1
227
- #
228
- # Instead of typing:
229
- #
230
- # lono cfn create demo-1 --template demo
231
- # lono cfn update demo-1 --template demo
232
- #
233
- # The suffix can be specified at the CLI but can also be saved as a
234
- # preference.
235
- #
236
- # A random suffix can be specified with random. Example:
237
- #
238
- # lono cfn current --suffix random
239
- # lono cfn create demo => demo-[RANDOM], example: demo-abc
240
- # lono cfn update demo-abc
241
- #
242
- # It is not a default setting because it might confuse new lono users.
243
- def append_suffix(stack_name)
244
- suffix = Lono.suffix == 'random' ? random_suffix : Lono.suffix
245
- [stack_name, suffix].compact.join('-')
246
- end
216
+ update_operation = %w[Preview Update].include?(self.class.to_s)
217
+ if tags.empty? && update_operation
218
+ resp = cfn.describe_stacks(stack_name: @stack_name)
219
+ tags = resp.stacks.first.tags
220
+ tags = tags.map(&:to_h)
221
+ end
247
222
 
248
- def remove_suffix(stack_name)
249
- return stack_name unless Lono.suffix
223
+ tags
224
+ end
250
225
 
251
- if stack_name_suffix == 'random'
252
- stack_name.sub(/-(\w{3})$/,'') # strip the random suffix at the end
253
- elsif stack_name_suffix
254
- pattern = Regexp.new("-#{stack_name_suffix}$",'')
255
- stack_name.sub(pattern, '') # strip suffix
256
- else
257
- stack_name
226
+ def check_for_errors
227
+ errors = check_files
228
+ unless errors.empty?
229
+ puts "Please double check the command you ran. There were some errors."
230
+ puts "ERROR: #{errors.join("\n")}".color(:red)
231
+ exit
232
+ end
258
233
  end
259
- end
260
234
 
261
- # only generate random suffix for Lono::Cfn::Create class
262
- def random_suffix
263
- return nil unless self.class.name.to_s =~ /Create/
264
- (0...3).map { (65 + rand(26)).chr }.join.downcase # Ex: jhx
265
- end
235
+ def check_files
236
+ errors = []
237
+ unless File.exist?(@template_path)
238
+ errors << "Template file missing: could not find #{@template_path}"
239
+ end
240
+ errors
241
+ end
242
+
243
+ # All CloudFormation states listed here:
244
+ # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
245
+ def stack_status(stack_name)
246
+ return true if testing_update?
247
+ return false if @options[:noop]
266
248
 
267
- def stack_name_suffix
268
- if @options[:suffix] && !@options[:suffix].nil?
269
- return @options[:suffix] # CLI option takes highest precedence
249
+ resp = cfn.describe_stacks(stack_name: stack_name)
250
+ resp.stacks[0].stack_status
270
251
  end
271
252
 
272
- # otherwise use the settings preference
273
- settings = Lono::Setting.new
274
- settings.data['stack_name_suffix']
275
- end
253
+ def exit_unless_updatable!(status)
254
+ return true if testing_update?
255
+ return false if @options[:noop]
256
+
257
+ unless status =~ /_COMPLETE$/ || status == "UPDATE_ROLLBACK_FAILED"
258
+ puts "Cannot create a change set for the stack because the #{@stack_name} is not in an updatable state. Stack status: #{status}".color(:red)
259
+ quit(1)
260
+ end
261
+ end
276
262
 
277
- def capabilities
278
- return @options[:capabilities] if @options[:capabilities]
279
- if @options[:iam]
280
- ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
263
+ # To allow mocking in specs
264
+ def quit(signal)
265
+ exit signal
281
266
  end
282
- end
283
267
 
284
- def show_parameters(params, meth=nil)
285
- params = params.clone.compact
286
- params[:template_body] = "Hidden due to size... View at: #{@template_path}"
287
- to = meth || "AWS API"
288
- puts "Parameters passed to #{to}:"
289
- puts YAML.dump(params.deep_stringify_keys)
290
- end
268
+ def capabilities
269
+ return @options[:capabilities] if @options[:capabilities]
270
+ if @options[:iam]
271
+ ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
272
+ end
273
+ end
291
274
 
292
- def s3_folder
293
- setting = Lono::Setting.new
294
- setting.s3_folder
295
- end
275
+ def show_parameters(params, meth=nil)
276
+ params = params.clone.compact
277
+ params[:template_body] = "Hidden due to size... View at: #{pretty_path(@template_path)}"
278
+ to = meth || "AWS API"
279
+ puts "Parameters passed to #{to}:"
280
+ puts YAML.dump(params.deep_stringify_keys)
281
+ end
296
282
 
297
- # Either set the templmate_body or template_url attribute based on
298
- # if template was uploaded to s3.
299
- # Nice to reference s3 url because the max size of the template body is
300
- # greater if the template body is on s3. Limits:
301
- #
302
- # template_body: 51,200 bytes
303
- # template_url: 460,800 bytes
304
- #
305
- # Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
306
- def set_template_body!(params)
307
- # if s3_folder is set this means s3 upload is enabled
308
- if s3_folder # s3_folder defined in cfn/base.rb
309
- upload = Lono::Template::Upload.new
310
- url = upload.s3_presigned_url(@template_path)
283
+ # Lono always uploads the template to s3 so we can use much larger templates.
284
+ #
285
+ # template_body: 51,200 bytes - filesystem limit
286
+ # template_url: 460,800 bytes - s3 limit
287
+ #
288
+ # Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
289
+ def set_template_body!(params)
290
+ upload = Lono::Template::Upload.new(@blueprint)
291
+ url_path = @template_path.sub("#{Lono.root}/",'')
292
+ url = upload.s3_presigned_url(url_path)
311
293
  params[:template_url] = url
312
- else
313
- params[:template_body] = IO.read(@template_path)
294
+ params
314
295
  end
315
296
 
316
- params
297
+ def pretty_path(path)
298
+ path.sub("#{Lono.root}/",'')
299
+ end
317
300
  end
318
- end
301
+ end