lono 4.2.7 → 5.0.1

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.
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