jets 0.2.0 → 0.5.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.
Files changed (292) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/bin/commit_docs.sh +26 -0
  3. data/.circleci/config.yml +126 -0
  4. data/.codebuild/README.md +57 -0
  5. data/.codebuild/bin/jets +3 -0
  6. data/.codebuild/buildspec-base.yml +14 -0
  7. data/.codebuild/integration.sh +54 -0
  8. data/.codebuild/jets.postman_collection.json +323 -0
  9. data/.codebuild/scripts/install-docker.sh +12 -0
  10. data/.codebuild/scripts/install-dynamodb-local.sh +22 -0
  11. data/.codebuild/scripts/install-java.sh +20 -0
  12. data/.codebuild/scripts/install-node.sh +4 -0
  13. data/.gitignore +3 -0
  14. data/.gitmodules +9 -0
  15. data/.python-version +1 -0
  16. data/.rspec +2 -0
  17. data/.ruby-version +1 -1
  18. data/CHANGELOG.md +3 -0
  19. data/Dockerfile +16 -0
  20. data/Dockerfile.base +53 -0
  21. data/Gemfile +8 -1
  22. data/Gemfile.lock +132 -28
  23. data/LICENSE.txt +1 -1
  24. data/Procfile +2 -0
  25. data/README.md +116 -17
  26. data/Rakefile +6 -0
  27. data/buildspec.yml +18 -0
  28. data/exe/jets +14 -0
  29. data/jets.gemspec +31 -3
  30. data/lib/jets.rb +56 -8
  31. data/lib/jets/application.rb +121 -0
  32. data/lib/jets/application/middleware.rb +23 -0
  33. data/lib/jets/aws_services.rb +71 -0
  34. data/lib/jets/booter.rb +95 -0
  35. data/lib/jets/builders.rb +6 -0
  36. data/lib/jets/builders/code_builder.rb +431 -0
  37. data/lib/jets/builders/deducer.rb +73 -0
  38. data/lib/jets/builders/gem_replacer.rb +154 -0
  39. data/lib/jets/builders/handler_generator.rb +76 -0
  40. data/lib/jets/builders/node-hello.js +73 -0
  41. data/lib/jets/builders/node-shim.js +151 -0
  42. data/lib/jets/cfn.rb +5 -4
  43. data/lib/jets/cfn/ship.rb +178 -0
  44. data/lib/jets/cfn/status.rb +208 -0
  45. data/lib/jets/cfn/template_builders.rb +21 -0
  46. data/lib/jets/cfn/template_builders/api_gateway_builder.rb +73 -0
  47. data/lib/jets/cfn/template_builders/api_gateway_deployment_builder.rb +38 -0
  48. data/lib/jets/cfn/template_builders/base_child_builder.rb +38 -0
  49. data/lib/jets/cfn/template_builders/controller_builder.rb +107 -0
  50. data/lib/jets/cfn/template_builders/function_builder.rb +20 -0
  51. data/lib/jets/cfn/template_builders/function_properties.rb +6 -0
  52. data/lib/jets/cfn/template_builders/function_properties/base_builder.rb +106 -0
  53. data/lib/jets/cfn/template_builders/function_properties/node_builder.rb +12 -0
  54. data/lib/jets/cfn/template_builders/function_properties/python_builder.rb +12 -0
  55. data/lib/jets/cfn/template_builders/function_properties/ruby_builder.rb +13 -0
  56. data/lib/jets/cfn/template_builders/interface.rb +91 -0
  57. data/lib/jets/cfn/template_builders/job_builder.rb +63 -0
  58. data/lib/jets/cfn/template_builders/parent_builder.rb +97 -0
  59. data/lib/jets/cfn/template_builders/rule_builder.rb +55 -0
  60. data/lib/jets/cfn/template_builders/templates/minimal-stack.yml +45 -0
  61. data/lib/jets/cfn/template_mappers.rb +23 -0
  62. data/lib/jets/cfn/template_mappers/api_gateway_deployment_mapper.rb +48 -0
  63. data/lib/jets/cfn/template_mappers/api_gateway_mapper.rb +4 -0
  64. data/lib/jets/cfn/template_mappers/child_mapper.rb +41 -0
  65. data/lib/jets/cfn/template_mappers/config_rule_mapper.rb +34 -0
  66. data/lib/jets/cfn/template_mappers/controller_mapper.rb +36 -0
  67. data/lib/jets/cfn/template_mappers/events_rule_mapper.rb +40 -0
  68. data/lib/jets/cfn/template_mappers/function_mapper.rb +4 -0
  69. data/lib/jets/cfn/template_mappers/gateway_method_mapper.rb +56 -0
  70. data/lib/jets/cfn/template_mappers/gateway_resource_mapper.rb +62 -0
  71. data/lib/jets/cfn/template_mappers/job_mapper.rb +4 -0
  72. data/lib/jets/cfn/template_mappers/lambda_function_mapper.rb +50 -0
  73. data/lib/jets/cfn/template_mappers/rule_mapper.rb +5 -0
  74. data/lib/jets/cli.rb +180 -15
  75. data/lib/jets/commands.rb +22 -0
  76. data/lib/jets/commands/base.rb +151 -0
  77. data/lib/jets/commands/build.rb +186 -0
  78. data/lib/jets/commands/call.rb +175 -0
  79. data/lib/jets/commands/call/anonymous_guesser.rb +96 -0
  80. data/lib/jets/commands/call/autoload_guesser.rb +112 -0
  81. data/lib/jets/commands/call/base_guesser.rb +33 -0
  82. data/lib/jets/commands/call/guesser.rb +51 -0
  83. data/lib/jets/commands/console.rb +12 -0
  84. data/lib/jets/commands/db.rb +15 -0
  85. data/lib/jets/commands/db/environment-task.rake +3 -0
  86. data/lib/jets/commands/db/tasks.rb +30 -0
  87. data/lib/jets/commands/dbconsole.rb +131 -0
  88. data/lib/jets/commands/delete.rb +119 -0
  89. data/lib/jets/commands/deploy.rb +53 -0
  90. data/lib/jets/commands/dynamodb.rb +22 -0
  91. data/lib/jets/commands/dynamodb/migrate.rb +9 -0
  92. data/lib/jets/commands/dynamodb/migrator.rb +36 -0
  93. data/lib/jets/commands/help.rb +9 -0
  94. data/lib/jets/commands/help/build.md +1 -0
  95. data/lib/jets/commands/help/call.md +55 -0
  96. data/lib/jets/commands/help/console.md +4 -0
  97. data/lib/jets/commands/help/db/generate.md +8 -0
  98. data/lib/jets/commands/help/dbconsole.md +5 -0
  99. data/lib/jets/commands/help/delete.md +9 -0
  100. data/lib/jets/commands/help/deploy.md +5 -0
  101. data/lib/jets/commands/help/dynamodb/generate.md +50 -0
  102. data/lib/jets/commands/help/dynamodb/migrate.md +4 -0
  103. data/lib/jets/commands/help/generate.md +5 -0
  104. data/lib/jets/commands/help/new.md +9 -0
  105. data/lib/jets/commands/help/process/controller.md +6 -0
  106. data/lib/jets/commands/help/process/function.md +5 -0
  107. data/lib/jets/commands/help/process/job.md +5 -0
  108. data/lib/jets/commands/help/process/rule.md +5 -0
  109. data/lib/jets/commands/help/routes.md +3 -0
  110. data/lib/jets/commands/help/server.md +6 -0
  111. data/lib/jets/commands/help/status.md +1 -0
  112. data/lib/jets/commands/help/url.md +5 -0
  113. data/lib/jets/commands/main.rb +111 -0
  114. data/lib/jets/commands/markdown.rb +8 -0
  115. data/lib/jets/commands/markdown/creator.rb +58 -0
  116. data/lib/jets/commands/markdown/index.rb +27 -0
  117. data/lib/jets/commands/markdown/page.rb +125 -0
  118. data/lib/jets/commands/markdown/shell.rb +11 -0
  119. data/lib/jets/commands/new.rb +110 -0
  120. data/lib/jets/commands/rake_command.rb +61 -0
  121. data/lib/jets/commands/rake_tasks.rb +45 -0
  122. data/lib/jets/commands/sequence.rb +44 -0
  123. data/lib/jets/commands/stack_info.rb +30 -0
  124. data/lib/jets/commands/templates/skeleton/.env +2 -0
  125. data/lib/jets/commands/templates/skeleton/.env.development.tt +3 -0
  126. data/lib/jets/commands/templates/skeleton/.env.test +3 -0
  127. data/lib/jets/commands/templates/skeleton/.gitignore +14 -0
  128. data/lib/jets/commands/templates/skeleton/.jetskeep +1 -0
  129. data/lib/jets/commands/templates/skeleton/Gemfile.tt +27 -0
  130. data/lib/jets/commands/templates/skeleton/Procfile +7 -0
  131. data/lib/jets/commands/templates/skeleton/README.md +4 -0
  132. data/lib/jets/commands/templates/skeleton/Rakefile +2 -0
  133. data/lib/jets/commands/templates/skeleton/app/controllers/application_controller.rb +2 -0
  134. data/lib/jets/commands/templates/skeleton/app/helpers/application_helper.rb +2 -0
  135. data/lib/jets/commands/templates/skeleton/app/jobs/application_job.rb +2 -0
  136. data/lib/jets/commands/templates/skeleton/app/models/application_item.rb +2 -0
  137. data/lib/jets/commands/templates/skeleton/app/models/application_record.rb +3 -0
  138. data/lib/jets/commands/templates/skeleton/app/views/layouts/application.html.erb.tt +24 -0
  139. data/lib/jets/commands/templates/skeleton/bin/ruby_server +18 -0
  140. data/lib/jets/commands/templates/skeleton/bin/ruby_server.rb +2 -0
  141. data/lib/jets/commands/templates/skeleton/config.ru +4 -0
  142. data/lib/jets/commands/templates/skeleton/config/application.rb.tt +26 -0
  143. data/lib/jets/commands/templates/skeleton/config/database.yml.tt +27 -0
  144. data/lib/jets/commands/templates/skeleton/config/dynamodb.yml +25 -0
  145. data/lib/jets/commands/templates/skeleton/config/routes.rb +8 -0
  146. data/lib/jets/commands/templates/skeleton/db/.gitkeep +0 -0
  147. data/lib/jets/commands/templates/skeleton/public/404.html +67 -0
  148. data/lib/jets/commands/templates/skeleton/public/422.html +67 -0
  149. data/lib/jets/commands/templates/skeleton/public/500.html +66 -0
  150. data/lib/jets/commands/templates/skeleton/public/favicon.ico +0 -0
  151. data/lib/jets/commands/templates/skeleton/public/index.html.tt +79 -0
  152. data/lib/jets/commands/templates/skeleton/spec/controllers/posts_controller_spec.rb +18 -0
  153. data/lib/jets/commands/templates/skeleton/spec/fixtures/payloads/posts-index.json +51 -0
  154. data/lib/jets/commands/templates/skeleton/spec/fixtures/payloads/posts-show.json +53 -0
  155. data/lib/jets/commands/templates/skeleton/spec/spec_helper.rb.tt +27 -0
  156. data/lib/jets/commands/templates/webpacker/app/javascript/packs/application.js.tt +14 -0
  157. data/lib/jets/commands/templates/webpacker/app/javascript/packs/theme.scss.tt +24 -0
  158. data/lib/jets/commands/templates/webpacker/app/javascript/src/jets/crud.js +87 -0
  159. data/lib/jets/commands/url.rb +45 -0
  160. data/lib/jets/commands/webpacker_template.rb +19 -0
  161. data/lib/jets/controller.rb +9 -0
  162. data/lib/jets/controller/base.rb +50 -0
  163. data/lib/jets/controller/callbacks.rb +43 -0
  164. data/lib/jets/controller/layout.rb +17 -0
  165. data/lib/jets/controller/params.rb +53 -0
  166. data/lib/jets/controller/redirection.rb +55 -0
  167. data/lib/jets/controller/renderers.rb +5 -0
  168. data/lib/jets/controller/renderers/aws_proxy_renderer.rb +69 -0
  169. data/lib/jets/controller/renderers/base_renderer.rb +16 -0
  170. data/lib/jets/controller/renderers/template_renderer.rb +107 -0
  171. data/lib/jets/controller/rendering.rb +80 -0
  172. data/lib/jets/controller/request.rb +55 -0
  173. data/lib/jets/core.rb +81 -0
  174. data/lib/jets/default/application.rb +20 -0
  175. data/lib/jets/dotenv.rb +37 -0
  176. data/lib/jets/erb.rb +51 -0
  177. data/lib/jets/generator.rb +40 -0
  178. data/lib/jets/generator/templates/erb/controller/view.html.erb +2 -0
  179. data/lib/jets/generator/templates/erb/scaffold/_form.html.erb +39 -0
  180. data/lib/jets/generator/templates/erb/scaffold/edit.html.erb +6 -0
  181. data/lib/jets/generator/templates/erb/scaffold/index.html.erb +29 -0
  182. data/lib/jets/generator/templates/erb/scaffold/new.html.erb +5 -0
  183. data/lib/jets/generator/templates/erb/scaffold/show.html.erb +9 -0
  184. data/lib/jets/generator/templates/rails/assets/javascript.js +2 -0
  185. data/lib/jets/generator/templates/rails/assets/stylesheet.css +4 -0
  186. data/lib/jets/generator/templates/rails/controller/controller.rb +13 -0
  187. data/lib/jets/generator/templates/rails/helper/helper.rb +4 -0
  188. data/lib/jets/generator/templates/rails/scaffold/scaffold.css +80 -0
  189. data/lib/jets/generator/templates/rails/scaffold_controller/api_controller.rb +62 -0
  190. data/lib/jets/generator/templates/rails/scaffold_controller/controller.rb +71 -0
  191. data/lib/jets/internal/app/controllers/jets/public_controller.rb +30 -0
  192. data/lib/jets/internal/app/controllers/jets/public_controller/python/show.py +47 -0
  193. data/lib/jets/internal/app/controllers/jets/public_controller/python/show.pyc +0 -0
  194. data/lib/jets/internal/app/controllers/jets/welcome_controller.rb +22 -0
  195. data/lib/jets/internal/app/controllers/jets/welcome_controller/python/index.py +24 -0
  196. data/lib/jets/internal/app/jobs/jets/preheat_job.rb +52 -0
  197. data/lib/jets/job.rb +5 -0
  198. data/lib/jets/job/base.rb +29 -0
  199. data/lib/jets/job/dsl.rb +58 -0
  200. data/lib/jets/job/task.rb +17 -0
  201. data/lib/jets/klass.rb +71 -0
  202. data/lib/jets/lambda.rb +18 -0
  203. data/lib/jets/lambda/dsl.rb +153 -0
  204. data/lib/jets/lambda/function.rb +29 -0
  205. data/lib/jets/lambda/function_constructor.rb +60 -0
  206. data/lib/jets/lambda/functions.rb +22 -0
  207. data/lib/jets/lambda/task.rb +77 -0
  208. data/lib/jets/naming.rb +61 -0
  209. data/lib/jets/pascalize.rb +30 -0
  210. data/lib/jets/poly_fun.rb +61 -0
  211. data/lib/jets/poly_fun/base_executor.rb +129 -0
  212. data/lib/jets/poly_fun/lambda_executor.rb +16 -0
  213. data/lib/jets/poly_fun/node_error.rb +8 -0
  214. data/lib/jets/poly_fun/node_executor.rb +54 -0
  215. data/lib/jets/poly_fun/python_error.rb +8 -0
  216. data/lib/jets/poly_fun/python_executor.rb +23 -0
  217. data/lib/jets/preheat.rb +72 -0
  218. data/lib/jets/processors.rb +4 -0
  219. data/lib/jets/processors/deducer.rb +54 -0
  220. data/lib/jets/processors/main_processor.rb +57 -0
  221. data/lib/jets/rails_overrides.rb +4 -0
  222. data/lib/jets/rails_overrides/asset_tag_helper.rb +41 -0
  223. data/lib/jets/rails_overrides/common_methods.rb +13 -0
  224. data/lib/jets/rails_overrides/rendering_helper.rb +26 -0
  225. data/lib/jets/rails_overrides/url_helper.rb +26 -0
  226. data/lib/jets/route.rb +145 -0
  227. data/lib/jets/router.rb +115 -0
  228. data/lib/jets/ruby_server.rb +91 -0
  229. data/lib/jets/rule.rb +5 -0
  230. data/lib/jets/rule/base.rb +19 -0
  231. data/lib/jets/rule/dsl.rb +64 -0
  232. data/lib/jets/rule/task.rb +44 -0
  233. data/lib/jets/server.rb +16 -0
  234. data/lib/jets/server/api_gateway.rb +39 -0
  235. data/lib/jets/server/lambda_aws_proxy.rb +122 -0
  236. data/lib/jets/server/route_matcher.rb +96 -0
  237. data/lib/jets/server/timing_middleware.rb +16 -0
  238. data/lib/jets/server/webpacker_setup.rb +7 -0
  239. data/lib/jets/timing.rb +65 -0
  240. data/lib/jets/timing/report.rb +82 -0
  241. data/lib/jets/util.rb +7 -12
  242. data/lib/jets/version.rb +1 -1
  243. data/support/clean +3 -0
  244. data/support/console +3 -0
  245. metadata +473 -76
  246. data/bin/jets +0 -14
  247. data/lib/jets/base_controller.rb +0 -54
  248. data/lib/jets/build.rb +0 -46
  249. data/lib/jets/build/handler_generator.rb +0 -46
  250. data/lib/jets/build/lambda_deducer.rb +0 -23
  251. data/lib/jets/build/templates/handler.js +0 -149
  252. data/lib/jets/build/traveling_ruby.rb +0 -133
  253. data/lib/jets/cfn/base.rb +0 -17
  254. data/lib/jets/cfn/builder.rb +0 -53
  255. data/lib/jets/cfn/namer.rb +0 -30
  256. data/lib/jets/cli/help.rb +0 -19
  257. data/lib/jets/command.rb +0 -25
  258. data/lib/jets/process.rb +0 -18
  259. data/lib/jets/process/base_processor.rb +0 -23
  260. data/lib/jets/process/controller_processor.rb +0 -36
  261. data/lib/jets/process/help.rb +0 -11
  262. data/lib/jets/process/processor_deducer.rb +0 -51
  263. data/lib/jets/project.rb +0 -23
  264. data/notes/design.md +0 -107
  265. data/notes/faq.md +0 -3
  266. data/notes/lambda_ruby_info.md +0 -34
  267. data/notes/traveling-ruby-packaging-jets.md +0 -26
  268. data/notes/traveling-ruby-packaging.md +0 -103
  269. data/notes/traveling-ruby-structure.md +0 -6
  270. data/notes/traveling-ruby.md +0 -82
  271. data/spec/fixtures/classes.rb +0 -5
  272. data/spec/fixtures/project/.gitignore +0 -3
  273. data/spec/fixtures/project/.ruby-version +0 -1
  274. data/spec/fixtures/project/Gemfile +0 -4
  275. data/spec/fixtures/project/Gemfile.lock +0 -23
  276. data/spec/fixtures/project/app/controllers/application_controller.rb +0 -2
  277. data/spec/fixtures/project/app/controllers/posts_controller.rb +0 -12
  278. data/spec/fixtures/project/bin/jets +0 -22
  279. data/spec/fixtures/project/config/routes.rb +0 -6
  280. data/spec/fixtures/project/handlers/controllers/posts.js +0 -212
  281. data/spec/lib/cli_spec.rb +0 -20
  282. data/spec/lib/jets/base_controller_spec.rb +0 -18
  283. data/spec/lib/jets/build/handler_generator_spec.rb +0 -20
  284. data/spec/lib/jets/build/lambda_deducer_spec.rb +0 -15
  285. data/spec/lib/jets/build_spec.rb +0 -34
  286. data/spec/lib/jets/cfn/builder_spec.rb +0 -18
  287. data/spec/lib/jets/cfn/namer_spec.rb +0 -16
  288. data/spec/lib/jets/process/controller_processor_spec.rb +0 -22
  289. data/spec/lib/jets/process/infer_spec.rb +0 -24
  290. data/spec/lib/jets/process_spec.rb +0 -18
  291. data/spec/lib/jets/project_spec.rb +0 -14
  292. data/spec/spec_helper.rb +0 -28
@@ -1,5 +1,6 @@
1
1
  class Jets::Cfn
2
- autoload :Base, "jets/cfn/base"
3
- autoload :Builder, "jets/cfn/builder"
4
- autoload :Namer, "jets/cfn/namer"
5
- end
2
+ autoload :TemplateMappers, "jets/cfn/template_mappers"
3
+ autoload :TemplateBuilders, "jets/cfn/template_builders"
4
+ autoload :Ship, "jets/cfn/ship"
5
+ autoload :Status, "jets/cfn/status"
6
+ end
@@ -0,0 +1,178 @@
1
+ require 'action_view'
2
+
3
+ class Jets::Cfn
4
+ class Ship
5
+ include Jets::Timing
6
+ include Jets::AwsServices
7
+ include ActionView::Helpers::NumberHelper # number_to_human_size
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ @parent_stack_name = Jets::Naming.parent_stack_name
12
+ @template_path = Jets::Naming.parent_template_path
13
+ end
14
+
15
+ def run
16
+ upload_to_s3 if @options[:stack_type] == :full # s3 bucket is available
17
+ # only when stack_type is full
18
+
19
+ stack_in_progress?(@parent_stack_name)
20
+
21
+ puts "Deploying CloudFormation stack with jets app!"
22
+ begin
23
+ save_stack
24
+ rescue Aws::CloudFormation::Errors::InsufficientCapabilitiesException => e
25
+ capabilities = e.message.match(/\[(.*)\]/)[1]
26
+ confirm = prompt_for_iam(capabilities)
27
+ if confirm =~ /^y/
28
+ @options.merge!(capabilities: [capabilities])
29
+ puts "Re-running: #{command_with_iam(capabilities).colorize(:green)}"
30
+ retry
31
+ else
32
+ puts "Exited"
33
+ exit 1
34
+ end
35
+ end
36
+
37
+ wait_for_stack
38
+ prewarm
39
+ show_api_endpoint
40
+ end
41
+ time :run
42
+
43
+ def save_stack
44
+ if stack_exists?(@parent_stack_name)
45
+ update_stack
46
+ else
47
+ create_stack
48
+ end
49
+ end
50
+
51
+ def create_stack
52
+ # parent stack template is on filesystem and child stacks templates is on s3
53
+ template_body = IO.read(@template_path)
54
+ cfn.create_stack(stack_options)
55
+ end
56
+ time :create_stack
57
+
58
+ def update_stack
59
+ begin
60
+ cfn.update_stack(stack_options)
61
+ rescue Aws::CloudFormation::Errors::ValidationError => e
62
+ puts "ERROR: #{e.message}".red
63
+ error = true
64
+ end
65
+ end
66
+ time :update_stack
67
+
68
+ # options common to both create_stack and update_stack
69
+ def stack_options
70
+ {
71
+ stack_name: @parent_stack_name,
72
+ template_body: IO.read(@template_path),
73
+ capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
74
+ # disable_rollback: !@options[:rollback],
75
+ }
76
+ end
77
+
78
+ # check for /(_COMPLETE|_FAILED)$/ status
79
+ def wait_for_stack
80
+ Jets::Cfn::Status.new(@options).wait
81
+ end
82
+ time :wait_for_stack
83
+
84
+ def prewarm
85
+ return unless @options[:stack_type] == :full # s3 bucket is available
86
+ puts "Prewarming application..."
87
+ if Jets::PreheatJob::CONCURRENCY > 1
88
+ Jets::PreheatJob.perform_now(:torch, {quiet: true})
89
+ else
90
+ Jets::PreheatJob.perform_now(:warm, {quiet: true})
91
+ end
92
+ end
93
+
94
+ def show_api_endpoint
95
+ return unless @options[:stack_type] == :full # s3 bucket is available
96
+ return if Jets::Router.routes.empty?
97
+
98
+ resp = cfn.describe_stack_resources(stack_name: @parent_stack_name)
99
+ resources = resp.stack_resources
100
+ api_gateway = resources.find { |resource| resource.logical_resource_id == "ApiGateway" }
101
+ stack_id = api_gateway["physical_resource_id"]
102
+
103
+ resp = cfn.describe_stacks(stack_name: stack_id)
104
+ stack = resp["stacks"].first
105
+ output = stack["outputs"].find { |o| o["output_key"] == "RestApiUrl" }
106
+ endpoint = output["output_value"]
107
+ puts "API Gateway Endpoint: #{endpoint}"
108
+ end
109
+
110
+ # All CloudFormation states listed here:
111
+ # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
112
+ def stack_status
113
+ resp = cfn.describe_stacks(stack_name: @parent_stack_name)
114
+ status = resp.stacks[0].stack_status
115
+ [resp, status]
116
+ end
117
+
118
+ def prompt_for_iam(capabilities)
119
+ puts "This stack will create IAM resources. Please approve to run the command again with #{capabilities} capabilities."
120
+ puts " #{command_with_iam(capabilities)}"
121
+
122
+ puts "Please confirm (y/n)"
123
+ confirm = $stdin.gets
124
+ end
125
+
126
+ def command_with_iam(capabilities)
127
+ "#{File.basename($0)} #{ARGV.join(' ')} --capabilities #{capabilities}"
128
+ end
129
+
130
+ def capabilities
131
+ ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"] # TODO: remove capabilities hardcode
132
+ # return @options[:capabilities] if @options[:capabilities]
133
+ # if @options[:iam]
134
+ # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
135
+ # end
136
+ end
137
+
138
+ # Upload both code and child templates to s3
139
+ def upload_to_s3
140
+ raise "Did not specify @options[:s3_bucket] #{@options[:s3_bucket].inspect}" unless @options[:s3_bucket]
141
+
142
+ bucket_name = @options[:s3_bucket]
143
+
144
+ puts "Uploading child CloudFormation templates to S3"
145
+ expression = "#{Jets::Naming.template_path_prefix}-*"
146
+ Dir.glob(expression).each do |path|
147
+ next unless File.file?(path)
148
+
149
+ key = "jets/cfn-templates/#{File.basename(path)}"
150
+ obj = s3_resource.bucket(bucket_name).object(key)
151
+ obj.upload_file(path)
152
+ end
153
+
154
+ md5_code_zipfile = Jets::Naming.md5_code_zipfile
155
+ file_size = number_to_human_size(File.size(md5_code_zipfile))
156
+
157
+ puts "Uploading #{md5_code_zipfile} (#{file_size}) to S3"
158
+ start_time = Time.now
159
+ key = Jets::Naming.code_s3_key
160
+ obj = s3_resource.bucket(bucket_name).object(key)
161
+ obj.upload_file(md5_code_zipfile)
162
+ puts "Time to upload code to s3: #{pretty_time(Time.now-start_time).colorize(:green)}"
163
+ end
164
+ time :upload_to_s3
165
+
166
+ # http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
167
+ def pretty_time(total_seconds)
168
+ minutes = (total_seconds / 60) % 60
169
+ seconds = total_seconds % 60
170
+ if total_seconds < 60
171
+ "#{seconds.to_i}s"
172
+ else
173
+ "#{minutes.to_i}m #{seconds.to_i}s"
174
+ end
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,208 @@
1
+ class Jets::Cfn
2
+ class Status
3
+ include Jets::AwsServices
4
+
5
+ attr_reader :events
6
+ def initialize(options={})
7
+ @options = options
8
+ @stack_name = Jets::Naming.parent_stack_name
9
+ reset
10
+ end
11
+
12
+ # used for the jets status command
13
+ def run
14
+ unless stack_exists?(@stack_name)
15
+ puts "The stack #{@stack_name.colorize(:green)} does not exist."
16
+ return
17
+ end
18
+
19
+ resp = cfn.describe_stacks(stack_name: @stack_name)
20
+ stack = resp.stacks.first
21
+
22
+ puts "The current status for the stack #{@stack_name.colorize(:green)} is #{stack.stack_status.colorize(:green)}"
23
+ if stack.stack_status =~ /_IN_PROGRESS$/
24
+ puts "Stack events (tailing):"
25
+ # tail all events until done
26
+ @hide_time_took = true
27
+ wait
28
+ else
29
+ puts "Stack events:"
30
+ # show the last events that was user initiated
31
+ refresh_events
32
+ show_events(true)
33
+ end
34
+ end
35
+
36
+ def reset
37
+ @events = [] # constantly replaced with recent events
38
+ @last_shown_event_id = nil
39
+ @stack_deletion_completed = nil
40
+ end
41
+
42
+ # check for /(_COMPLETE|_FAILED)$/ status
43
+ def wait
44
+ start_time = Time.now
45
+
46
+ refresh_events
47
+ until completed || @stack_deletion_completed
48
+ show_events
49
+ end
50
+ show_events(true) # show the final event
51
+
52
+ if @stack_deletion_completed
53
+ puts "Stack #{@stack_name} deleted."
54
+ return
55
+ end
56
+
57
+ if last_event_status =~ /_FAILED/
58
+ puts "Stack failed: #{last_event_status}".colorize(:red)
59
+ puts "Stack reason #{@events[0]["resource_status_reason"]}".colorize(:red)
60
+ elsif last_event_status =~ /_ROLLBACK_/
61
+ puts "Stack rolled back: #{last_event_status}".colorize(:red)
62
+ else # success
63
+ puts "Stack success status: #{last_event_status}".colorize(:green)
64
+ end
65
+
66
+ # Never gets here when deleting a stack because the describe stack returns nothing
67
+ # once the stack is deleted. Gets here for stack create and update though.
68
+ return if @hide_time_took # set in run
69
+ took = Time.now - start_time
70
+ puts "Time took for stack deployment: #{pretty_time(took).green}."
71
+ end
72
+
73
+ def completed
74
+ last_event_status =~ /(_COMPLETE|_FAILED)$/ &&
75
+ @events[0]["logical_resource_id"] == @stack_name &&
76
+ @events[0]["resource_type"] == "AWS::CloudFormation::Stack"
77
+ end
78
+
79
+ def last_event_status
80
+ @events[0]["resource_status"]
81
+ end
82
+
83
+ # Only shows new events
84
+ def show_events(final=false)
85
+ if @last_shown_event_id.nil?
86
+ i = find_index(:start)
87
+ print_events(i)
88
+ else
89
+ i = find_index(:last_shown)
90
+ # puts "last_shown index #{i}"
91
+ print_events(i-1) unless i == 0
92
+ end
93
+
94
+ return if final
95
+ sleep 5 unless ENV['TEST']
96
+ refresh_events
97
+ end
98
+
99
+ def print_events(i)
100
+ @events[0..i].reverse.each do |e|
101
+ print_event(e)
102
+ end
103
+ @last_shown_event_id = @events[0]["event_id"]
104
+ # puts "@last_shown_event_id #{@last_shown_event_id.inspect}"
105
+ end
106
+
107
+ def print_event(e)
108
+ message = [
109
+ event_time(e["timestamp"]),
110
+ e["resource_status"],
111
+ e["resource_type"],
112
+ e["logical_resource_id"],
113
+ e["resource_status_reason"]
114
+ ].join(" ")
115
+ message = message.colorize(:red) if e["resource_status"] =~ /_FAILED/
116
+ puts message
117
+ end
118
+
119
+ # https://stackoverflow.com/questions/18000432/rails-12-hour-am-pm-range-for-a-day
120
+ def event_time(timestamp)
121
+ Time.parse(timestamp.to_s).localtime.strftime("%I:%M:%S%p")
122
+ end
123
+
124
+ # refreshes the loaded events in memory
125
+ def refresh_events
126
+ resp = cfn.describe_stack_events(stack_name: @stack_name)
127
+ @events = resp["stack_events"]
128
+ rescue Aws::CloudFormation::Errors::ValidationError => e
129
+ if e.message =~ /Stack .* does not exis/
130
+ @stack_deletion_completed = true
131
+ else
132
+ raise
133
+ end
134
+ end
135
+
136
+ def find_index(name)
137
+ send("#{name}_index")
138
+ end
139
+
140
+ def start_index
141
+ @events.find_index do |event|
142
+ event["resource_type"] == "AWS::CloudFormation::Stack" &&
143
+ event["resource_status_reason"] == "User Initiated"
144
+ end
145
+ end
146
+
147
+ def last_shown_index
148
+ @events.find_index do |event|
149
+ event["event_id"] == @last_shown_event_id
150
+ end
151
+ end
152
+
153
+ def success?
154
+ resource_status = @events[0]["resource_status"]
155
+ %w[CREATE_COMPLETE UPDATE_COMPLETE].include?(resource_status)
156
+ end
157
+
158
+ def update_rollback?
159
+ @events[0]["resource_status"] == "UPDATE_ROLLBACK_COMPLETE"
160
+ end
161
+
162
+ def find_update_failed_event
163
+ i = @events.find_index do |event|
164
+ event["resource_type"] == "AWS::CloudFormation::Stack" &&
165
+ event["resource_status_reason"] == "User Initiated"
166
+ end
167
+
168
+ @events[0..i].reverse.find do |e|
169
+ e["resource_status"] == "UPDATE_FAILED"
170
+ end
171
+ end
172
+
173
+ def rollback_error_message
174
+ return unless update_rollback?
175
+
176
+ event = find_update_failed_event
177
+ return unless event
178
+
179
+ reason = event["resource_status_reason"]
180
+ messages_map.each do |pattern, message|
181
+ if reason =~ pattern
182
+ return message
183
+ end
184
+ end
185
+
186
+ reason # default message is original reason if not found in messages map
187
+ end
188
+
189
+ def messages_map
190
+ {
191
+ /CloudFormation cannot update a stack when a custom-named resource requires replacing/ => "A workaround is to run ufo again with STATIC_NAME=0 and to switch to dynamic names for resources. Then run ufo again with STATIC_NAME=1 to get back to statically name resources. Note, there are caveats with the workaround.",
192
+ /cannot be associated with more than one load balancer/ => "There's was an issue updating the stack. Target groups can only be associated with one load balancer at a time. The workaround for this is to use UFO_FORCE_TARGET_GROUP=1 and run the command again. This will force the recreation of the target group resource.",
193
+ /SetSubnets is not supported for load balancers of type/ => "Changing subnets for Network Load Balancers is currently not supported. You can try workarouding this with UFO_FORCE_ELB=1 and run the command again. This will force the recreation of the elb resource."
194
+ }
195
+ end
196
+
197
+ # http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
198
+ def pretty_time(total_seconds)
199
+ minutes = (total_seconds / 60) % 60
200
+ seconds = total_seconds % 60
201
+ if total_seconds < 60
202
+ "#{seconds.to_i}s"
203
+ else
204
+ "#{minutes.to_i}m #{seconds.to_i}s"
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'yaml'
3
+
4
+ class Jets::Cfn
5
+ class TemplateBuilders
6
+ autoload :Interface, "jets/cfn/template_builders/interface"
7
+ autoload :ParentBuilder, "jets/cfn/template_builders/parent_builder"
8
+
9
+ # These build the app/controllers, app/jobs, and app/functions
10
+ autoload :BaseChildBuilder, "jets/cfn/template_builders/base_child_builder"
11
+ autoload :ControllerBuilder, "jets/cfn/template_builders/controller_builder"
12
+ autoload :JobBuilder, "jets/cfn/template_builders/job_builder"
13
+ autoload :FunctionBuilder, "jets/cfn/template_builders/function_builder"
14
+ autoload :RuleBuilder, "jets/cfn/template_builders/rule_builder"
15
+
16
+ autoload :ApiGatewayBuilder, "jets/cfn/template_builders/api_gateway_builder"
17
+ autoload :ApiGatewayDeploymentBuilder, "jets/cfn/template_builders/api_gateway_deployment_builder"
18
+ # separate beast:
19
+ autoload :FunctionProperties, "jets/cfn/template_builders/function_properties"
20
+ end
21
+ end
@@ -0,0 +1,73 @@
1
+ class Jets::Cfn::TemplateBuilders
2
+ class ApiGatewayBuilder
3
+ include Interface
4
+ include Jets::AwsServices
5
+
6
+ def initialize(options={})
7
+ @options = options
8
+ @template = ActiveSupport::HashWithIndifferentAccess.new(Resources: {})
9
+ end
10
+
11
+ # compose is an interface method
12
+ def compose
13
+ return if @options[:stack_type] == :minimal
14
+
15
+ puts "Building API Gateway template."
16
+ add_gateway_rest_api
17
+ add_gateway_routes
18
+ end
19
+
20
+ # template_path is an interface method
21
+ def template_path
22
+ Jets::Naming.api_gateway_template_path
23
+ end
24
+
25
+ # do not bother writing a template if routes are empty
26
+ def write
27
+ super unless Jets::Router.routes.empty?
28
+ end
29
+
30
+ # If the are routes in config/routes.rb add Gateway API in parent stack
31
+ def add_gateway_rest_api
32
+ add_resource("RestApi", "AWS::ApiGateway::RestApi",
33
+ Name: Jets::Naming.gateway_api_name
34
+ )
35
+
36
+ stage_name = Jets::Cfn::TemplateMappers::ApiGatewayDeploymentMapper.stage_name
37
+ add_output("RestApi", Value: "!Ref RestApi")
38
+ add_output("RestApiUrl", Value: "!Sub 'https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/#{stage_name}/'")
39
+ add_output("Region", Value: "!Ref AWS::Region")
40
+ add_output("RootResourceId", Value: "!GetAtt RestApi.RootResourceId")
41
+ end
42
+
43
+ # Adds route related Resources and Outputs
44
+ def add_gateway_routes
45
+ # The routes required a Gateway Resource to contain them.
46
+ # TODO: outputing all routes in 1 template will hit the 60 routes limit
47
+ # Will have to either output them as a joined string or
48
+ # break this up to multiple tempaltes.
49
+ # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
50
+ # Outputs: Maximum number of outputs that you can declare in your AWS CloudFormation template. 60 outputs
51
+ # Output name: Maximum size of an output name. 255 characters.
52
+ #
53
+ # Note we must use .all_paths, not .routes here because we need to
54
+ # build the parent ApiGateway::Resource nodes also
55
+ Jets::Router.all_paths.each do |path|
56
+ homepage = path == ''
57
+ next if homepage # handled by RootResourceId output already
58
+
59
+ map = Jets::Cfn::TemplateMappers::GatewayResourceMapper.new(path)
60
+
61
+ unless homepage # no AWS::ApiGateway::Resource for the top level route
62
+ add_resource(map.logical_id, "AWS::ApiGateway::Resource",
63
+ ParentId: map.parent_id,
64
+ PathPart: map.path_part,
65
+ RestApiId: "!Ref RestApi"
66
+ )
67
+ end
68
+
69
+ add_output(map.logical_id, Value: "!Ref #{map.logical_id}")
70
+ end
71
+ end
72
+ end
73
+ end