jets 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +1 -1
- data/jets.gemspec +2 -0
- data/lib/jets.rb +1 -0
- data/lib/jets/application.rb +45 -21
- data/lib/jets/aws_services.rb +2 -0
- data/lib/jets/aws_services/s3_bucket.rb +26 -0
- data/lib/jets/booter.rb +82 -2
- data/lib/jets/builders/code_builder.rb +20 -7
- data/lib/jets/builders/handler_generator.rb +20 -6
- data/lib/jets/cfn/builders/base_child_builder.rb +5 -17
- data/lib/jets/cfn/builders/parent_builder.rb +1 -1
- data/lib/jets/commands/build.rb +6 -0
- data/lib/jets/commands/deploy.rb +10 -0
- data/lib/jets/commands/templates/skeleton/config/environments/development.rb +4 -1
- data/lib/jets/commands/templates/skeleton/config/environments/production.rb +6 -1
- data/lib/jets/commands/templates/skeleton/config/environments/test.rb +7 -0
- data/lib/jets/controller/base.rb +1 -2
- data/lib/jets/controller/rendering/rack_renderer.rb +11 -3
- data/lib/jets/core.rb +5 -72
- data/lib/jets/internal/app/controllers/jets/mailers_controller.rb +97 -0
- data/lib/jets/internal/app/helpers/jets/mailers_helper.rb +9 -0
- data/lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb +43 -0
- data/lib/jets/internal/app/views/jets/mailers/email.html.erb +145 -0
- data/lib/jets/internal/app/views/jets/mailers/index.html.erb +8 -0
- data/lib/jets/internal/app/views/jets/mailers/mailer.html.erb +6 -0
- data/lib/jets/job/base.rb +10 -0
- data/lib/jets/job/dsl.rb +2 -0
- data/lib/jets/job/dsl/s3_event.rb +36 -0
- data/lib/jets/job/dsl/sns_event.rb +8 -0
- data/lib/jets/job/s3_event_helper.rb +13 -0
- data/lib/jets/lambda/dsl.rb +11 -1
- data/lib/jets/mailer.rb +51 -0
- data/lib/jets/resource/child_stack/app_class.rb +6 -15
- data/lib/jets/resource/child_stack/shared.rb +3 -1
- data/lib/jets/resource/events/rule.rb +1 -1
- data/lib/jets/resource/lambda/event_source_mapping.rb +1 -1
- data/lib/jets/resource/permission.rb +1 -1
- data/lib/jets/resource/replacer.rb +8 -0
- data/lib/jets/resource/s3.rb +3 -17
- data/lib/jets/resource/s3/bucket.rb +24 -0
- data/lib/jets/resource/sns.rb +1 -0
- data/lib/jets/resource/sns/subscription.rb +1 -1
- data/lib/jets/resource/sns/topic.rb +1 -1
- data/lib/jets/resource/sns/topic_policy.rb +40 -0
- data/lib/jets/resource/sqs/queue.rb +1 -1
- data/lib/jets/stack.rb +19 -3
- data/lib/jets/stack/builder.rb +6 -1
- data/lib/jets/stack/depends.rb +36 -0
- data/lib/jets/stack/depends/item.rb +9 -0
- data/lib/jets/stack/function.rb +19 -10
- data/lib/jets/stack/main/dsl.rb +4 -0
- data/lib/jets/stack/main/extensions/iam.rb +8 -0
- data/lib/jets/stack/main/extensions/lambda.rb +20 -7
- data/lib/jets/stack/main/extensions/s3.rb +12 -0
- data/lib/jets/stack/main/extensions/sns.rb +4 -0
- data/lib/jets/stack/s3_event.rb +87 -0
- data/lib/jets/turbine.rb +11 -0
- data/lib/jets/version.rb +1 -1
- metadata +48 -2
@@ -0,0 +1,8 @@
|
|
1
|
+
<% @previews.each do |preview| %>
|
2
|
+
<h3><%= link_to(preview.preview_name.titleize, "/jets/mailers/#{preview.preview_name}") %></h3>
|
3
|
+
<ul>
|
4
|
+
<% preview.emails.each do |email| %>
|
5
|
+
<li><%= link_to(email, "/jets/mailers/#{preview.preview_name}/#{email}") %></li>
|
6
|
+
<% end %>
|
7
|
+
</ul>
|
8
|
+
<% end %>
|
data/lib/jets/job/base.rb
CHANGED
@@ -6,9 +6,19 @@ require 'json'
|
|
6
6
|
# Both Jets::Job::Base and Jets::Lambda::Functions have Dsl modules included.
|
7
7
|
# So the Jets::Job::Dsl overrides some of the Jets::Lambda::Functions behavior.
|
8
8
|
class Jets::Job
|
9
|
+
autoload :S3EventHelper, "jets/job/s3_event_helper"
|
10
|
+
|
9
11
|
class Base < Jets::Lambda::Functions
|
10
12
|
include Dsl
|
11
13
|
|
14
|
+
# non-DSL methods
|
15
|
+
include S3EventHelper
|
16
|
+
|
17
|
+
# Tracks bucket each time an s3_event is declared
|
18
|
+
# Map of bucket_name => stack_name (nested part)
|
19
|
+
cattr_accessor :s3_events # dont want this to be inheritable intentionally
|
20
|
+
self.s3_events = {}
|
21
|
+
|
12
22
|
class << self
|
13
23
|
def process(event, context, meth)
|
14
24
|
job = new(event, context, meth)
|
data/lib/jets/job/dsl.rb
CHANGED
@@ -9,12 +9,14 @@
|
|
9
9
|
module Jets::Job::Dsl
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
autoload :EventSourceMapping, "jets/job/dsl/event_source_mapping" # base for sqs_event, etc
|
12
|
+
autoload :S3Event, "jets/job/dsl/s3_event"
|
12
13
|
autoload :SnsEvent, "jets/job/dsl/sns_event"
|
13
14
|
autoload :SqsEvent, "jets/job/dsl/sqs_event"
|
14
15
|
|
15
16
|
included do
|
16
17
|
class << self
|
17
18
|
include EventSourceMapping
|
19
|
+
include S3Event
|
18
20
|
include SnsEvent
|
19
21
|
include SqsEvent
|
20
22
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Jets::Job::Dsl
|
2
|
+
module S3Event
|
3
|
+
def s3_event(bucket_name, props={})
|
4
|
+
stack_name = declare_s3_bucket_resources(bucket_name) # only set up once per bucket
|
5
|
+
declare_sns_subscription(topic_arn: "!Ref #{stack_name}SnsTopic") # set up subscription every time
|
6
|
+
end
|
7
|
+
|
8
|
+
# Returns stack_name
|
9
|
+
def declare_s3_bucket_resources(bucket_name)
|
10
|
+
# If shared s3 bucket resources have already been declared.
|
11
|
+
# We will not generate them again. However, we still need to always
|
12
|
+
# add the depends_on declaration to ensure that the shared stack parameters
|
13
|
+
# are properly passed to the nested child stack.
|
14
|
+
stack_name = s3_events[bucket_name] # already registered
|
15
|
+
if stack_name
|
16
|
+
depends_on stack_name.underscore.to_sym, class_prefix: true # always add this
|
17
|
+
return stack_name
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create shared resources - one time
|
21
|
+
stack_name = declare_shared_s3_event_resources(bucket_name)
|
22
|
+
depends_on stack_name.underscore.to_sym, class_prefix: true # always add this
|
23
|
+
self.s3_events[bucket_name] = stack_name # tracks buckets already set up
|
24
|
+
end
|
25
|
+
|
26
|
+
def declare_shared_s3_event_resources(bucket_name)
|
27
|
+
s3_stack = Jets::Stack::S3Event.new(bucket_name)
|
28
|
+
s3_stack.build_stack
|
29
|
+
s3_stack.stack_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def s3_events
|
33
|
+
Jets::Job::Base.s3_events
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -25,6 +25,14 @@ module Jets::Job::Dsl
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def declare_sns_topic_policy(props={})
|
29
|
+
props ||= {} # options.delete(:topic_policy_properties) can be nil
|
30
|
+
r = Jets::Resource::Sns::TopicPolicy.new(props)
|
31
|
+
with_fresh_properties do
|
32
|
+
resource(r.definition) # add associated resource immediately
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
28
36
|
def declare_sns_subscription(props={})
|
29
37
|
r = Jets::Resource::Sns::Subscription.new(props)
|
30
38
|
with_fresh_properties do
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Jets::Job
|
2
|
+
module S3EventHelper
|
3
|
+
def s3_event_message
|
4
|
+
message = event["Records"][0]["Sns"]["Message"]
|
5
|
+
h = JSON.load(message)
|
6
|
+
ActiveSupport::HashWithIndifferentAccess.new(h)
|
7
|
+
end
|
8
|
+
|
9
|
+
def s3_object
|
10
|
+
s3_event_message["Records"][0]["s3"]["object"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/jets/lambda/dsl.rb
CHANGED
@@ -213,12 +213,22 @@ module Jets::Lambda::Dsl
|
|
213
213
|
@associated_resources = numbered_resources
|
214
214
|
end
|
215
215
|
|
216
|
+
# Examples:
|
217
|
+
#
|
218
|
+
# depends_on :custom
|
219
|
+
# depends_on :custom, :alert
|
220
|
+
# depends_on :custom, class_prefix: true
|
221
|
+
# depends_on :custom, :alert, class_prefix: true
|
222
|
+
#
|
216
223
|
def depends_on(*stacks)
|
217
224
|
if stacks == []
|
218
225
|
@depends_on
|
219
226
|
else
|
220
227
|
@depends_on ||= []
|
221
|
-
|
228
|
+
options = stacks.last.is_a?(Hash) ? stacks.pop : {}
|
229
|
+
stacks.each do |stack|
|
230
|
+
@depends_on << Jets::Stack::Depends::Item.new(stack, options)
|
231
|
+
end
|
222
232
|
end
|
223
233
|
end
|
224
234
|
|
data/lib/jets/mailer.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Jets
|
2
|
+
# Reference: https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/railtie.rb
|
3
|
+
class Mailer < ::Jets::Turbine
|
4
|
+
config.action_mailer = ActiveSupport::OrderedOptions.new
|
5
|
+
|
6
|
+
initializer "action_mailer.logger" do
|
7
|
+
ActiveSupport.on_load(:action_mailer) { self.logger ||= Jets.logger }
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "action_mailer.set_configs" do |app|
|
11
|
+
options = app.config.action_mailer
|
12
|
+
options.default_url_options ||= {}
|
13
|
+
options.default_url_options[:protocol] ||= "https"
|
14
|
+
options.show_previews = false if options.show_previews.nil?
|
15
|
+
options.preview_path ||= "#{Jets.root}/app/previews" if options.show_previews
|
16
|
+
options.view_paths ||= "#{Jets.root}/app/views"
|
17
|
+
|
18
|
+
# TODO: Dont think Jets sets asset_host the same way
|
19
|
+
# make sure readers methods get compiled
|
20
|
+
# options.asset_host ||= app.config.asset_host
|
21
|
+
# options.relative_url_root ||= app.config.relative_url_root
|
22
|
+
|
23
|
+
ActiveSupport.on_load(:action_mailer) do
|
24
|
+
include AbstractController::UrlFor
|
25
|
+
# TODO: figure out rest of the helpers
|
26
|
+
# extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
27
|
+
# include app.routes.mounted_helpers
|
28
|
+
|
29
|
+
register_interceptors(options.delete(:interceptors))
|
30
|
+
register_preview_interceptors(options.delete(:preview_interceptors))
|
31
|
+
register_observers(options.delete(:observers))
|
32
|
+
|
33
|
+
options.each { |k, v| send("#{k}=", v) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
after_initializer "action_mailer.routes" do |app|
|
38
|
+
if app.config.action_mailer.show_previews
|
39
|
+
app.routes.draw do
|
40
|
+
get "jets/mailers", to: "jets/mailers#index"
|
41
|
+
get "jets/mailers/*path", to: "jets/mailers#preview"
|
42
|
+
end
|
43
|
+
|
44
|
+
ActiveSupport.on_load :action_controller do
|
45
|
+
internal_views = File.expand_path("internal/app/views", File.dirname(__FILE__))
|
46
|
+
ActionController::Base.append_view_path(internal_views)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -29,24 +29,15 @@ module Jets::Resource::ChildStack
|
|
29
29
|
klass = current_app_class.constantize
|
30
30
|
return unless klass.depends_on
|
31
31
|
|
32
|
-
klass.depends_on
|
33
|
-
|
34
|
-
end
|
32
|
+
depends = Jets::Stack::Depends.new(klass.depends_on)
|
33
|
+
depends.stack_list
|
35
34
|
end
|
36
35
|
|
37
36
|
def depends_on_params
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
params[output] = "!GetAtt #{dependency_class}.Outputs.#{output}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
params
|
46
|
-
end
|
47
|
-
|
48
|
-
def dependency_outputs(dependency)
|
49
|
-
dependency.to_s.classify.constantize.output_keys
|
37
|
+
klass = current_app_class.constantize
|
38
|
+
return unless klass.depends_on
|
39
|
+
depends = Jets::Stack::Depends.new(klass.depends_on)
|
40
|
+
depends.params
|
50
41
|
end
|
51
42
|
|
52
43
|
def parameters
|
@@ -40,10 +40,12 @@ module Jets::Resource::ChildStack
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def common_parameters
|
43
|
-
{
|
43
|
+
parameters = {
|
44
44
|
IamRole: "!GetAtt IamRole.Arn",
|
45
45
|
S3Bucket: "!Ref S3Bucket",
|
46
46
|
}
|
47
|
+
parameters[:GemLayer] = "!Ref GemLayer" unless Jets.poly_only?
|
48
|
+
parameters
|
47
49
|
end
|
48
50
|
|
49
51
|
# Returns output keys associated with the stack. They are the resource logical ids.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Note the Lambda function timeout must be less than or equal to the sqs queue default timeout.
|
2
2
|
module Jets::Resource::Lambda
|
3
3
|
class EventSourceMapping < Jets::Resource::Base
|
4
|
-
def initialize(props)
|
4
|
+
def initialize(props={})
|
5
5
|
@props = props # associated_properties from dsl.rb
|
6
6
|
end
|
7
7
|
|
@@ -28,7 +28,7 @@ class Jets::Resource
|
|
28
28
|
|
29
29
|
def permission_logical_id
|
30
30
|
logical_id = "{namespace}_permission"
|
31
|
-
md = @associated_resource.logical_id.match(/(\d+)
|
31
|
+
md = @associated_resource.logical_id.match(/(\d+)$/)
|
32
32
|
counter = md[1] if md
|
33
33
|
[logical_id, counter].compact.join('').underscore
|
34
34
|
end
|
@@ -59,9 +59,17 @@ class Jets::Resource
|
|
59
59
|
# "AWS::ApiGateway::Method" => "apigateway.amazonaws.com"
|
60
60
|
def principal_map(type)
|
61
61
|
service = type.split('::')[1].downcase
|
62
|
+
service = special_principal_map(service)
|
62
63
|
"#{service}.amazonaws.com"
|
63
64
|
end
|
64
65
|
|
66
|
+
def special_principal_map(service)
|
67
|
+
# special map
|
68
|
+
# s3_event actually uses sns topic events to trigger a Lambda function
|
69
|
+
map = { "s3" => "sns" }
|
70
|
+
map[service] || service
|
71
|
+
end
|
72
|
+
|
65
73
|
# From AWS docs: https://amzn.to/2N0QXQL
|
66
74
|
# source_arn is "not supported by all event sources"
|
67
75
|
#
|
data/lib/jets/resource/s3.rb
CHANGED
@@ -1,17 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
{
|
5
|
-
s3_bucket: {
|
6
|
-
type: "AWS::S3::Bucket"
|
7
|
-
}
|
8
|
-
}
|
9
|
-
end
|
10
|
-
|
11
|
-
def outputs
|
12
|
-
{
|
13
|
-
"S3Bucket" => "!Ref S3Bucket",
|
14
|
-
}
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
1
|
+
module Jets::Resource::S3
|
2
|
+
autoload :Bucket, 'jets/resource/s3/bucket'
|
3
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Jets::Resource::S3
|
2
|
+
class Bucket < Jets::Resource::Base
|
3
|
+
attr_reader :bucket_logical_id
|
4
|
+
def initialize(props={})
|
5
|
+
@props = props # associated_properties from dsl.rb
|
6
|
+
@bucket_logical_id = props.delete(:logical_id) || "{namespace}_s3_bucket"
|
7
|
+
end
|
8
|
+
|
9
|
+
def definition
|
10
|
+
{
|
11
|
+
bucket_logical_id => {
|
12
|
+
type: "AWS::S3::Bucket",
|
13
|
+
properties: @props,
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def outputs
|
19
|
+
{
|
20
|
+
bucket_logical_id => "!Ref #{bucket_logical_id.to_s.camelize}",
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jets/resource/sns.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# CloudFormation SNS TopicPolicy docs: https://amzn.to/2SBMq9v
|
2
|
+
module Jets::Resource::Sns
|
3
|
+
class TopicPolicy < Jets::Resource::Base
|
4
|
+
def initialize(props={})
|
5
|
+
@props = props # associated_properties from dsl.rb
|
6
|
+
end
|
7
|
+
|
8
|
+
def definition
|
9
|
+
{
|
10
|
+
policy_logical_id => {
|
11
|
+
type: "AWS::SNS::TopicPolicy",
|
12
|
+
properties: merged_properties,
|
13
|
+
}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Do not name this method properties, that is a computed method of `Jets::Resource::Base`
|
18
|
+
def merged_properties
|
19
|
+
{
|
20
|
+
policy_document: {
|
21
|
+
version: "2012-10-17",
|
22
|
+
statement: {
|
23
|
+
effect: "Allow",
|
24
|
+
principal: { service: "s3.amazonaws.com"},
|
25
|
+
action: "sns:Publish",
|
26
|
+
resource: "*", # TODO: figure out good syntax to limit easily
|
27
|
+
# Condition:
|
28
|
+
# ArnLike:
|
29
|
+
# aws:SourceArn: arn:aws:s3:::aa-test-95872017
|
30
|
+
}
|
31
|
+
},
|
32
|
+
topics: ["!Ref {namespace}SnsTopic"],
|
33
|
+
}.deep_merge(@props)
|
34
|
+
end
|
35
|
+
|
36
|
+
def policy_logical_id
|
37
|
+
"{namespace}_sns_topic_policy"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/jets/stack.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
module Jets
|
2
2
|
class Stack
|
3
|
+
autoload :Builder, 'jets/stack/builder'
|
3
4
|
autoload :Definition, 'jets/stack/definition' # Registration and definitions
|
5
|
+
autoload :Depends, 'jets/stack/depends'
|
6
|
+
autoload :Function, 'jets/stack/function'
|
4
7
|
autoload :Main, 'jets/stack/main'
|
5
|
-
autoload :Parameter, 'jets/stack/parameter'
|
6
8
|
autoload :Output, 'jets/stack/output'
|
9
|
+
autoload :Parameter, 'jets/stack/parameter'
|
7
10
|
autoload :Resource, 'jets/stack/resource'
|
8
|
-
autoload :
|
9
|
-
autoload :Function, 'jets/stack/function'
|
11
|
+
autoload :S3Event, 'jets/stack/s3_event'
|
10
12
|
|
11
13
|
include Main::Dsl
|
12
14
|
include Parameter::Dsl
|
@@ -26,6 +28,20 @@ module Jets
|
|
26
28
|
self.subclasses << base if base.name
|
27
29
|
end
|
28
30
|
|
31
|
+
# klass = Jets::Stack.new_class("Bucket3")
|
32
|
+
def new_class(class_name, &block)
|
33
|
+
# https://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
|
34
|
+
# Defining the constant this way gets around: SyntaxError: dynamic constant assignment error
|
35
|
+
klass = Class.new(Jets::Stack) # First klass is an anonymous class. IE: class.name is nil
|
36
|
+
klass = Object.const_set(class_name, klass) # now klass is a named class
|
37
|
+
Jets::Stack.subclasses << klass # mimic inherited hook because
|
38
|
+
|
39
|
+
# Must run class_eval after adding to subclasses in order for the resource declarations in the
|
40
|
+
# so that the resources get registered to the right subclass.
|
41
|
+
klass.class_eval(&block)
|
42
|
+
klass # return klass
|
43
|
+
end
|
44
|
+
|
29
45
|
# Build it to figure out if we need to build the stack for the SharedBuilder
|
30
46
|
def build?
|
31
47
|
empty = template == {"Parameters"=>{"IamRole"=>{"Type"=>"String"}, "S3Bucket"=>{"Type"=>"String"}}}
|