jets 1.7.2 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"}}}
|