jets 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +1 -1
  4. data/jets.gemspec +2 -0
  5. data/lib/jets.rb +1 -0
  6. data/lib/jets/application.rb +45 -21
  7. data/lib/jets/aws_services.rb +2 -0
  8. data/lib/jets/aws_services/s3_bucket.rb +26 -0
  9. data/lib/jets/booter.rb +82 -2
  10. data/lib/jets/builders/code_builder.rb +20 -7
  11. data/lib/jets/builders/handler_generator.rb +20 -6
  12. data/lib/jets/cfn/builders/base_child_builder.rb +5 -17
  13. data/lib/jets/cfn/builders/parent_builder.rb +1 -1
  14. data/lib/jets/commands/build.rb +6 -0
  15. data/lib/jets/commands/deploy.rb +10 -0
  16. data/lib/jets/commands/templates/skeleton/config/environments/development.rb +4 -1
  17. data/lib/jets/commands/templates/skeleton/config/environments/production.rb +6 -1
  18. data/lib/jets/commands/templates/skeleton/config/environments/test.rb +7 -0
  19. data/lib/jets/controller/base.rb +1 -2
  20. data/lib/jets/controller/rendering/rack_renderer.rb +11 -3
  21. data/lib/jets/core.rb +5 -72
  22. data/lib/jets/internal/app/controllers/jets/mailers_controller.rb +97 -0
  23. data/lib/jets/internal/app/helpers/jets/mailers_helper.rb +9 -0
  24. data/lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb +43 -0
  25. data/lib/jets/internal/app/views/jets/mailers/email.html.erb +145 -0
  26. data/lib/jets/internal/app/views/jets/mailers/index.html.erb +8 -0
  27. data/lib/jets/internal/app/views/jets/mailers/mailer.html.erb +6 -0
  28. data/lib/jets/job/base.rb +10 -0
  29. data/lib/jets/job/dsl.rb +2 -0
  30. data/lib/jets/job/dsl/s3_event.rb +36 -0
  31. data/lib/jets/job/dsl/sns_event.rb +8 -0
  32. data/lib/jets/job/s3_event_helper.rb +13 -0
  33. data/lib/jets/lambda/dsl.rb +11 -1
  34. data/lib/jets/mailer.rb +51 -0
  35. data/lib/jets/resource/child_stack/app_class.rb +6 -15
  36. data/lib/jets/resource/child_stack/shared.rb +3 -1
  37. data/lib/jets/resource/events/rule.rb +1 -1
  38. data/lib/jets/resource/lambda/event_source_mapping.rb +1 -1
  39. data/lib/jets/resource/permission.rb +1 -1
  40. data/lib/jets/resource/replacer.rb +8 -0
  41. data/lib/jets/resource/s3.rb +3 -17
  42. data/lib/jets/resource/s3/bucket.rb +24 -0
  43. data/lib/jets/resource/sns.rb +1 -0
  44. data/lib/jets/resource/sns/subscription.rb +1 -1
  45. data/lib/jets/resource/sns/topic.rb +1 -1
  46. data/lib/jets/resource/sns/topic_policy.rb +40 -0
  47. data/lib/jets/resource/sqs/queue.rb +1 -1
  48. data/lib/jets/stack.rb +19 -3
  49. data/lib/jets/stack/builder.rb +6 -1
  50. data/lib/jets/stack/depends.rb +36 -0
  51. data/lib/jets/stack/depends/item.rb +9 -0
  52. data/lib/jets/stack/function.rb +19 -10
  53. data/lib/jets/stack/main/dsl.rb +4 -0
  54. data/lib/jets/stack/main/extensions/iam.rb +8 -0
  55. data/lib/jets/stack/main/extensions/lambda.rb +20 -7
  56. data/lib/jets/stack/main/extensions/s3.rb +12 -0
  57. data/lib/jets/stack/main/extensions/sns.rb +4 -0
  58. data/lib/jets/stack/s3_event.rb +87 -0
  59. data/lib/jets/turbine.rb +11 -0
  60. data/lib/jets/version.rb +1 -1
  61. metadata +48 -2
@@ -17,7 +17,12 @@ class Jets::Stack
17
17
 
18
18
  def build_section(section)
19
19
  elements = build_elements(section)
20
- @template[section] = elements if elements
20
+ return unless elements
21
+
22
+ if section == :parameters
23
+ elements["GemLayer"] = {"Type"=>"String"} unless Jets.poly_only?
24
+ end
25
+ @template[section] = elements
21
26
  end
22
27
 
23
28
  def build_elements(section)
@@ -0,0 +1,36 @@
1
+ class Jets::Stack
2
+ class Depends
3
+ autoload :Item, "jets/stack/depends/item"
4
+
5
+ def initialize(items)
6
+ @items = items
7
+ end
8
+
9
+ def params
10
+ result = {}
11
+ @items.each do |item|
12
+ logical_id = item.stack.to_s.camelize # logical_id
13
+ dependency_outputs(logical_id).each do |output|
14
+ dependency_class = logical_id.to_s.classify
15
+ output_key = item.options[:class_prefix] ?
16
+ "#{dependency_class}#{output}" : # already camelized
17
+ output
18
+
19
+ output_value = "!GetAtt #{dependency_class}.Outputs.#{output}"
20
+ result[output_key] = output_value
21
+ end
22
+ end
23
+ result
24
+ end
25
+
26
+ def stack_list
27
+ @items.map do |item|
28
+ item.stack.to_s.camelize # logical_id # logical_id
29
+ end
30
+ end
31
+
32
+ def dependency_outputs(logical_id)
33
+ logical_id.to_s.classify.constantize.output_keys
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ class Jets::Stack::Depends
2
+ class Item
3
+ attr_reader :stack, :options
4
+ def initialize(stack, options={})
5
+ @stack = stack
6
+ @options = options
7
+ end
8
+ end
9
+ end
@@ -14,6 +14,8 @@ class Jets::Stack
14
14
  end
15
15
 
16
16
  def lang
17
+ return if internal?
18
+
17
19
  if source_file
18
20
  # Detect language from file extension
19
21
  ext = File.extname(source_file).sub(/^\./,'').to_sym
@@ -37,24 +39,31 @@ class Jets::Stack
37
39
  memoize :source_file
38
40
 
39
41
  def search_expression
42
+ base_search_expression.sub('handlers/shared/', "#{Jets.root}/app/shared/")
43
+ end
44
+
45
+ def internal_search_expression
46
+ internal = File.expand_path("../internal", File.dirname(__FILE__))
47
+ base_search_expression.sub('handlers/shared/', "#{internal}/app/shared/")
48
+ end
49
+
50
+ def base_search_expression
40
51
  attributes = @template.values.first
41
52
  handler = attributes['Properties']['Handler']
42
- search_expression = handler.split('.')[0..-2].join('.') + '.*'
43
- search_expression.sub('handlers/shared/', "#{Jets.root}/app/shared/")
53
+ handler.split('.')[0..-2].join('.') + '.*' # search_expression
54
+ # Example: handlers/shared/functions/jets/s3_bucket_config.*
55
+ end
56
+
57
+ # Internal flag is mainly used to disable WARN messages
58
+ def internal?
59
+ !!Dir.glob(internal_search_expression).first
44
60
  end
45
61
 
46
62
  # Relative path
47
63
  # app/shared/functions/kevin.py => handlers/shared/functions/kevin.py
48
64
  def handler_dest
49
65
  return unless source_file
50
-
51
- dest = source_file.sub(%r{.*/app/}, "handlers/")
52
- if lang == :ruby
53
- filename = dest.split('.').first
54
- filename + '.js' # change extension to .js because ruby uses a node shim
55
- else
56
- dest
57
- end
66
+ source_file.sub(%r{.*/app/}, "handlers/")
58
67
  end
59
68
  end
60
69
  end
@@ -4,14 +4,18 @@ class Jets::Stack
4
4
  extend ActiveSupport::Concern
5
5
  autoload :Base, 'jets/stack/main/extensions/base'
6
6
  autoload :Cloudwatch, 'jets/stack/main/extensions/cloudwatch'
7
+ autoload :Iam, 'jets/stack/main/extensions/iam'
7
8
  autoload :Lambda, 'jets/stack/main/extensions/lambda'
9
+ autoload :S3, 'jets/stack/main/extensions/s3'
8
10
  autoload :Sns, 'jets/stack/main/extensions/sns'
9
11
  autoload :Sqs, 'jets/stack/main/extensions/sqs'
10
12
 
11
13
  class_methods do
12
14
  include Base
13
15
  include Cloudwatch
16
+ include Iam
14
17
  include Lambda
18
+ include S3
15
19
  include Sns
16
20
  include Sqs
17
21
  end
@@ -0,0 +1,8 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Iam
3
+ def iam_role(id, props={})
4
+ resource(id, "AWS::IAM::Role", props)
5
+ output(id) # IAM Arn
6
+ end
7
+ end
8
+ end
@@ -1,5 +1,6 @@
1
1
  module Jets::Stack::Main::Dsl
2
2
  module Lambda
3
+ MAX_FUNCTION_NAME_SIZE = 64
3
4
  # Example:
4
5
  #
5
6
  # function(:hello,
@@ -20,39 +21,45 @@ module Jets::Stack::Main::Dsl
20
21
  #
21
22
  def function(id, props={})
22
23
  # Required: code, handler, role, runtime Docs: https://amzn.to/2pdot7S
23
- meth = id.to_s.underscore
24
+ meth = sanitize_method_name(id)
24
25
  class_namespace = self.to_s.underscore.gsub('/','-') # IE: Jets::Domain => jets-domain
25
- function_name = "#{Jets.config.project_namespace}-#{class_namespace}-#{id.to_s.underscore}"
26
+ description = "#{self.to_s} #{meth}" # not bother adding extension
26
27
  defaults = {
27
- function_name: function_name,
28
28
  code: {
29
29
  s3_bucket: "!Ref S3Bucket",
30
30
  s3_key: code_s3_key
31
31
  },
32
32
  role: "!Ref IamRole",
33
- handler: "#{meth}.handle", # default ruby convention
33
+ handler: "#{id}.lambda_handler", # default ruby convention
34
34
  runtime: :ruby,
35
35
  timeout: Jets.config.function.timeout,
36
36
  memory_size: Jets.config.function.memory_size,
37
+ description: description,
37
38
  }
39
+
40
+ function_name = "#{Jets.config.project_namespace}-#{class_namespace}-#{meth}"
41
+ function_name.size > MAX_FUNCTION_NAME_SIZE ? nil : function_name
42
+ defaults[:function_name] = function_name if function_name
43
+
38
44
  props = defaults.merge(props)
39
45
  props[:runtime] = "ruby2.5" if props[:runtime].to_s == "ruby"
40
46
  props[:handler] = handler(props[:handler])
41
47
 
42
- resource(id, "AWS::Lambda::Function", props)
48
+ logical_id = id.to_s.gsub('/','_')
49
+ resource(logical_id, "AWS::Lambda::Function", props)
43
50
  end
44
51
  alias_method :ruby_function, :function
45
52
  alias_method :lambda_function, :function
46
53
 
47
54
  def python_function(id, props={})
48
- meth = id.to_s.underscore
55
+ meth = sanitize_method_name(id)
49
56
  props[:handler] ||= "#{meth}.lambda_handler" # default python convention
50
57
  props[:runtime] = "python3.6"
51
58
  function(id, props)
52
59
  end
53
60
 
54
61
  def node_function(id, props={})
55
- meth = id.to_s.underscore
62
+ meth = sanitize_method_name(id)
56
63
  props[:handler] ||= "#{meth}.handler" # default python convention
57
64
  props[:runtime] = "nodejs8.10"
58
65
  function(id, props)
@@ -67,5 +74,11 @@ module Jets::Stack::Main::Dsl
67
74
  props = defaults.merge(props)
68
75
  resource(id, "AWS::Lambda::Permission", props)
69
76
  end
77
+
78
+ private
79
+ # demo-dev-hard_job-dig_me
80
+ def sanitize_method_name(id)
81
+ id.to_s.gsub('/','-')
82
+ end
70
83
  end
71
84
  end
@@ -0,0 +1,12 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module S3
3
+ def s3_bucket(id, props={})
4
+ resource(id, "AWS::S3::Bucket", props)
5
+ output(id) # Bucket name
6
+ end
7
+
8
+ def s3_bucket_configuration(id, props={})
9
+ resource(id, "Custom::S3BucketConfiguration", props)
10
+ end
11
+ end
12
+ end
@@ -5,6 +5,10 @@ module Jets::Stack::Main::Dsl
5
5
  output(id) # Topic Arn
6
6
  end
7
7
 
8
+ def sns_topic_policy(id, props={})
9
+ resource(id, "AWS::SNS::TopicPolicy", props)
10
+ end
11
+
8
12
  def sns_subscription(id, props={})
9
13
  resource(id, "AWS::SNS::Subscription", props)
10
14
  end
@@ -0,0 +1,87 @@
1
+ class Jets::Stack
2
+ class S3Event
3
+ def initialize(bucket_name)
4
+ @bucket_name = bucket_name
5
+ end
6
+
7
+ # Stack names can only contain alpha numeric chars.
8
+ # Bucket names are limit to 64 chars: https://amzn.to/2SIzvme
9
+ # Stack names are limit to 128 chars: https://amzn.to/2SFkrG0
10
+ # This gsub should handle this.
11
+ def stack_name
12
+ @bucket_name.gsub(/[^0-9a-z\-_]/i, '').gsub('-','_').camelize
13
+ end
14
+
15
+ def build_stack
16
+ # assign to local variable so its available in the block
17
+ bucket = @bucket_name
18
+
19
+ Jets::Stack.new_class(stack_name) do
20
+ s3_bucket_configuration(:s3_bucket_configuration,
21
+ service_token: "!GetAtt JetsS3BucketConfig.Arn", # Cannot change this w/o changing the logical id
22
+ # These properties correspond to the ruby aws-sdk s3.put_bucket_notification_configuration
23
+ # in jets/s3_bucket_config.rb, not the CloudFormation Bucket properties. The CloudFormation
24
+ # bucket properties have a similiar structure but is slightly different so it can be confusing.
25
+ #
26
+ # Ruby aws-sdk S3 Docs: https://amzn.to/2N7m5Lr
27
+ bucket: bucket,
28
+ notification_configuration: Jets.config.s3_event.notification_configuration,
29
+ ) if Jets.config.s3_event.configure_bucket
30
+
31
+ # Important note: If we change the name of this function we should also change the
32
+ # logical id of the s3_bucket_configuration custom resource or we'll get this error:
33
+ # Modifying service token is not allowed.
34
+ function("jets/s3_bucket_config",
35
+ role: "!GetAtt BucketConfigIamRole.Arn",
36
+ layers: ["!Ref GemLayer"],
37
+ )
38
+
39
+ sns_topic(:sns_topic)
40
+ sns_topic_policy(:sns_topic_policy,
41
+ policy_document: {
42
+ version: "2012-10-17",
43
+ statement: {
44
+ effect: "Allow",
45
+ principal: { service: "s3.amazonaws.com"},
46
+ action: "sns:Publish",
47
+ resource: "!Ref SnsTopic",
48
+ condition: {
49
+ arn_like: {
50
+ "aws:SourceArn" => "!Sub arn:aws:s3:*:*:#{bucket}"
51
+ }
52
+ }
53
+ }
54
+ },
55
+ topics: ["!Ref SnsTopic"],
56
+ )
57
+
58
+ iam_role(:bucket_config_iam_role,
59
+ assume_role_policy_document: {
60
+ version: '2012-10-17',
61
+ statement: [
62
+ effect: "Allow",
63
+ principal: {service: ["lambda.amazonaws.com"]},
64
+ action: ['sts:AssumeRole'],
65
+ ]
66
+ },
67
+ path: "/",
68
+ managed_policy_arns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],
69
+ policies: [
70
+ policy_name: "S3Policy",
71
+ policy_document: {
72
+ version: '2012-10-17',
73
+ statement: [
74
+ effect: "Allow",
75
+ action: [
76
+ 's3:GetBucketNotification',
77
+ 's3:PutBucketNotification',
78
+ ],
79
+ resource: "*"
80
+ ]
81
+ }
82
+ ]
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
data/lib/jets/turbine.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Jets
2
2
  class Turbine
3
+ class_attribute :after_initializers
3
4
  class_attribute :initializers
4
5
  class_attribute :on_exceptions
5
6
 
@@ -12,6 +13,11 @@ module Jets
12
13
  subclasses << base
13
14
  end
14
15
 
16
+ def after_initializer(label, &block)
17
+ self.after_initializers ||= {}
18
+ self.after_initializers[label] = block
19
+ end
20
+
15
21
  def initializer(label, &block)
16
22
  self.initializers ||= {}
17
23
  self.initializers[label] = block
@@ -25,6 +31,11 @@ module Jets
25
31
  def exception_reporter(label, &block)
26
32
  on_exception(label, &block)
27
33
  end
34
+
35
+ # Make config available in Turbine. Note only available outside of hooks like initializers.
36
+ def config
37
+ Jets.application.config
38
+ end
28
39
  end
29
40
  end
30
41
  end
data/lib/jets/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jets
2
- VERSION = "1.7.2"
2
+ VERSION = "1.8.0"
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jets
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-09 00:00:00.000000000 Z
11
+ date: 2019-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionmailer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.1
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activerecord
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +164,20 @@ dependencies:
150
164
  - - ">="
151
165
  - !ruby/object:Gem::Version
152
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: cfnresponse
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
153
181
  - !ruby/object:Gem::Dependency
154
182
  name: dotenv
155
183
  requirement: !ruby/object:Gem::Requirement
@@ -452,6 +480,7 @@ files:
452
480
  - lib/jets/application.rb
453
481
  - lib/jets/aws_info.rb
454
482
  - lib/jets/aws_services.rb
483
+ - lib/jets/aws_services/s3_bucket.rb
455
484
  - lib/jets/aws_services/stack_status.rb
456
485
  - lib/jets/booter.rb
457
486
  - lib/jets/builders.rb
@@ -581,6 +610,7 @@ files:
581
610
  - lib/jets/commands/templates/skeleton/config/dynamodb.yml
582
611
  - lib/jets/commands/templates/skeleton/config/environments/development.rb
583
612
  - lib/jets/commands/templates/skeleton/config/environments/production.rb
613
+ - lib/jets/commands/templates/skeleton/config/environments/test.rb
584
614
  - lib/jets/commands/templates/skeleton/config/routes.rb
585
615
  - lib/jets/commands/templates/skeleton/db/.gitkeep
586
616
  - lib/jets/commands/templates/skeleton/public/404.html
@@ -643,17 +673,25 @@ files:
643
673
  - lib/jets/generator/templates/rails/scaffold_controller/api_controller.rb
644
674
  - lib/jets/generator/templates/rails/scaffold_controller/controller.rb
645
675
  - lib/jets/inflections.rb
676
+ - lib/jets/internal/app/controllers/jets/mailers_controller.rb
646
677
  - lib/jets/internal/app/controllers/jets/public_controller.rb
647
678
  - lib/jets/internal/app/controllers/jets/rack_controller.rb
648
679
  - lib/jets/internal/app/functions/jets/base_path.rb
680
+ - lib/jets/internal/app/helpers/jets/mailers_helper.rb
649
681
  - lib/jets/internal/app/jobs/jets/preheat_job.rb
682
+ - lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb
683
+ - lib/jets/internal/app/views/jets/mailers/email.html.erb
684
+ - lib/jets/internal/app/views/jets/mailers/index.html.erb
685
+ - lib/jets/internal/app/views/jets/mailers/mailer.html.erb
650
686
  - lib/jets/io.rb
651
687
  - lib/jets/job.rb
652
688
  - lib/jets/job/base.rb
653
689
  - lib/jets/job/dsl.rb
654
690
  - lib/jets/job/dsl/event_source_mapping.rb
691
+ - lib/jets/job/dsl/s3_event.rb
655
692
  - lib/jets/job/dsl/sns_event.rb
656
693
  - lib/jets/job/dsl/sqs_event.rb
694
+ - lib/jets/job/s3_event_helper.rb
657
695
  - lib/jets/klass.rb
658
696
  - lib/jets/lambda.rb
659
697
  - lib/jets/lambda/dsl.rb
@@ -662,6 +700,7 @@ files:
662
700
  - lib/jets/lambda/functions.rb
663
701
  - lib/jets/lambda/task.rb
664
702
  - lib/jets/logger.rb
703
+ - lib/jets/mailer.rb
665
704
  - lib/jets/mega.rb
666
705
  - lib/jets/mega/hash_converter.rb
667
706
  - lib/jets/mega/request.rb
@@ -744,9 +783,11 @@ files:
744
783
  - lib/jets/resource/route53.rb
745
784
  - lib/jets/resource/route53/record_set.rb
746
785
  - lib/jets/resource/s3.rb
786
+ - lib/jets/resource/s3/bucket.rb
747
787
  - lib/jets/resource/sns.rb
748
788
  - lib/jets/resource/sns/subscription.rb
749
789
  - lib/jets/resource/sns/topic.rb
790
+ - lib/jets/resource/sns/topic_policy.rb
750
791
  - lib/jets/resource/sqs.rb
751
792
  - lib/jets/resource/sqs/queue.rb
752
793
  - lib/jets/resource/standardizer.rb
@@ -763,12 +804,16 @@ files:
763
804
  - lib/jets/stack.rb
764
805
  - lib/jets/stack/builder.rb
765
806
  - lib/jets/stack/definition.rb
807
+ - lib/jets/stack/depends.rb
808
+ - lib/jets/stack/depends/item.rb
766
809
  - lib/jets/stack/function.rb
767
810
  - lib/jets/stack/main.rb
768
811
  - lib/jets/stack/main/dsl.rb
769
812
  - lib/jets/stack/main/extensions/base.rb
770
813
  - lib/jets/stack/main/extensions/cloudwatch.rb
814
+ - lib/jets/stack/main/extensions/iam.rb
771
815
  - lib/jets/stack/main/extensions/lambda.rb
816
+ - lib/jets/stack/main/extensions/s3.rb
772
817
  - lib/jets/stack/main/extensions/sns.rb
773
818
  - lib/jets/stack/main/extensions/sqs.rb
774
819
  - lib/jets/stack/output.rb
@@ -778,6 +823,7 @@ files:
778
823
  - lib/jets/stack/parameter/dsl.rb
779
824
  - lib/jets/stack/resource.rb
780
825
  - lib/jets/stack/resource/dsl.rb
826
+ - lib/jets/stack/s3_event.rb
781
827
  - lib/jets/tmp_loader.rb
782
828
  - lib/jets/turbine.rb
783
829
  - lib/jets/turbo.rb