jets 0.5.8 → 0.6.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +2 -3
  4. data/jets.gemspec +1 -4
  5. data/lib/jets.rb +3 -2
  6. data/lib/jets/application.rb +18 -1
  7. data/lib/jets/aws_info.rb +36 -0
  8. data/lib/jets/aws_services.rb +5 -0
  9. data/lib/jets/builders/code_builder.rb +0 -1
  10. data/lib/jets/cfn/template_builders.rb +5 -2
  11. data/lib/jets/cfn/template_builders/api_gateway_builder.rb +2 -3
  12. data/lib/jets/cfn/template_builders/base_child_builder.rb +16 -0
  13. data/lib/jets/cfn/template_builders/function_properties/base_builder.rb +23 -1
  14. data/lib/jets/cfn/template_builders/iam_policy.rb +6 -0
  15. data/lib/jets/cfn/template_builders/iam_policy/application_policy.rb +18 -0
  16. data/lib/jets/cfn/template_builders/iam_policy/base_policy.rb +57 -0
  17. data/lib/jets/cfn/template_builders/iam_policy/class_policy.rb +20 -0
  18. data/lib/jets/cfn/template_builders/iam_policy/function_policy.rb +21 -0
  19. data/lib/jets/cfn/template_builders/interface.rb +1 -1
  20. data/lib/jets/cfn/template_builders/parent_builder.rb +4 -6
  21. data/lib/jets/cfn/template_builders/templates/minimal-stack.yml +0 -36
  22. data/lib/jets/cfn/template_mappers.rb +2 -0
  23. data/lib/jets/cfn/template_mappers/iam_policy.rb +6 -0
  24. data/lib/jets/cfn/template_mappers/iam_policy/application_policy_mapper.rb +28 -0
  25. data/lib/jets/cfn/template_mappers/iam_policy/base_policy_mapper.rb +44 -0
  26. data/lib/jets/cfn/template_mappers/iam_policy/class_policy_mapper.rb +32 -0
  27. data/lib/jets/cfn/template_mappers/iam_policy/function_policy_mapper.rb +32 -0
  28. data/lib/jets/cfn/template_mappers/lambda_function_mapper.rb +3 -1
  29. data/lib/jets/commands/build.rb +2 -2
  30. data/lib/jets/commands/call.rb +1 -1
  31. data/lib/jets/core.rb +5 -1
  32. data/lib/jets/internal/app/controllers/jets/public_controller.rb +1 -2
  33. data/lib/jets/internal/app/jobs/jets/preheat_job.rb +14 -0
  34. data/lib/jets/lambda/dsl.rb +20 -1
  35. data/lib/jets/lambda/task.rb +2 -1
  36. data/lib/jets/version.rb +1 -1
  37. metadata +13 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea1e1a42bbfe0e0008e3ba21c5f35f208783533aad98e84c43e851bcf9ef93d7
4
- data.tar.gz: e08138d929d12d24259b712c134fa5beaa321f680ef1eb0d24f4c6661031fd65
3
+ metadata.gz: b156a3904d427d2d2604ad4026e069c6efe7a724c6c3937c9bd0a8cc28ecdda2
4
+ data.tar.gz: 4fb811e051ea4cf10dd5032a2b127a0b92b686d3e9818640fe8e10f2cc6b34a4
5
5
  SHA512:
6
- metadata.gz: 820b153d9dc5851fa71a16dea4907c4ca910f3b3216b6f558a138b82e45220fd571d0b254c81eaba824f860f22befb59b15ec7e84e0f855dd8e8421097a1876d
7
- data.tar.gz: '082993a2d007f0f700a43a4464901c059dbcb3ca0e5723520800785b826ef7f6120257f036c0b6d38a85326a13e594046e74254d506182b37ba9fb8c28aaaacb'
6
+ metadata.gz: c137bfe10a6a4c07af49d524a95374ca26c124b5367054015e709adaa200c748b2187b4275e69495cff5a0cef60c70e52e6abb8be81bd0a524953c96d7043b6a
7
+ data.tar.gz: e823f819bc813e3e78b99ca76c38aae10ee644178b73ad4728bc5bdea70818bfc544af116020e89c472d2cd5ac99fee79244e69fea2aacb26644a37e3fae9888
data/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [0.6.0]
7
+ - fine grain iam policy abilities: pull request #13 from tongueroo/iam-policy
8
+ - changed quite a few logical ids in the CloudFormation templates
9
+
6
10
  ## [0.5.8]
7
11
  - fix config.prewarm defaults
8
12
 
data/Gemfile CHANGED
@@ -4,9 +4,8 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  # required here for specs
7
- # TODO: Would like to only required this in the project's Gemfile
8
- # right now need both because of jets/application.rb and
9
- # jets/webpacker/middleware_setup.rb
7
+ # TODO: Only require webpacker in Gemfile of project if possible.
8
+ # Need both because of jets/application.rb and jets/webpacker/middleware_setup.rb
10
9
  group :development, :test do
11
10
  gem "webpacker", git: "https://github.com/tongueroo/webpacker.git", branch: "jets"
12
11
  gem "rspec_junit_formatter"
data/jets.gemspec CHANGED
@@ -31,8 +31,6 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency "actionpack"
32
32
  spec.add_dependency "activerecord"
33
33
  spec.add_dependency "railties" # ActiveRecord database_tasks.rb require this
34
- # TODO: only load the database adapters that the app uses, so generate this
35
- # in the app's Gemfile
36
34
  spec.add_dependency "dotenv"
37
35
 
38
36
  spec.add_dependency "recursive-open-struct"
@@ -45,8 +43,7 @@ Gem::Specification.new do |spec|
45
43
  spec.add_dependency "text-table"
46
44
  spec.add_dependency "rack"
47
45
  spec.add_dependency "json"
48
- # there are development dependencies because we want to lazy load them
49
- # in the app. but we want to have them so we can run specs.
46
+ # TODO: only load the database adapters that app's Gemfile instead of jets.gemspec
50
47
  spec.add_dependency "pg", "=0.21"
51
48
 
52
49
  spec.add_dependency "gems" # lambdagem dependency
data/lib/jets.rb CHANGED
@@ -5,7 +5,7 @@ require "active_support/core_ext/string"
5
5
  require "active_support/ordered_hash"
6
6
  require "colorize"
7
7
  require "fileutils"
8
- require "pp" # TODO: remove pp after debugging
8
+ require "pp" # TODO: Remove pp after debugging
9
9
  require "memoist"
10
10
 
11
11
  module Jets
@@ -15,6 +15,7 @@ module Jets
15
15
  autoload :CLI, "jets/cli"
16
16
  autoload :Commands, "jets/commands"
17
17
 
18
+ autoload :AwsInfo, "jets/aws_info"
18
19
  autoload :AwsServices, "jets/aws_services"
19
20
  autoload :Builders, 'jets/builders'
20
21
  autoload :Call, "jets/call"
@@ -57,7 +58,7 @@ if File.exist?("#{Jets.root}config/dynamodb.yml")
57
58
  require "dynomite"
58
59
  end
59
60
 
60
- # https://makandracards.com/makandra/42521-detecting-if-a-ruby-gem-is-loaded
61
+ # Thanks: https://makandracards.com/makandra/42521-detecting-if-a-ruby-gem-is-loaded
61
62
  # TODO: move require "pg" into loader class and abstract to support more gems
62
63
  if File.exist?("#{Jets.root}config/database.yml")
63
64
  require "active_record"
@@ -82,9 +82,21 @@ class Jets::Application
82
82
  # IE: With env_extra: project-dev-1
83
83
  # Without env_extra: project-dev
84
84
  config.short_env = ENV_MAP[Jets.env.to_sym] || Jets.env
85
- config.project_namespace = [config.project_name, config.short_env, config.env_extra].compact.join('-')
86
85
  # table_namespace does not have the env_extra, more common case desired.
87
86
  config.table_namespace = [config.project_name, config.short_env].compact.join('-')
87
+
88
+ project_namespace = [config.project_name, config.short_env, config.env_extra].compact.join('-')
89
+ config.project_namespace = project_namespace
90
+
91
+ # config.iam_policy = ["logs2:*"]
92
+ # Must set defaul t iam_policy here instead of `def config` because we need access to
93
+ # the project_namespace and if we call it from `def config` we get an infinit loop
94
+ config.iam_policy = [{
95
+ sid: "Statement1",
96
+ action: ["logs:*"],
97
+ effect: "Allow",
98
+ resource: "arn:aws:logs:#{Jets.aws.region}:#{Jets.aws.account}:log-group:#{project_namespace}-*",
99
+ }]
88
100
  end
89
101
 
90
102
  # It is pretty easy to attempt to set environment variables without
@@ -124,4 +136,9 @@ class Jets::Application
124
136
  require routes_file if File.exist?(routes_file)
125
137
  end
126
138
 
139
+ def aws
140
+ Jets::AwsInfo.new
141
+ end
142
+ memoize :aws
143
+
127
144
  end
@@ -0,0 +1,36 @@
1
+
2
+ module Jets
3
+ class AwsInfo
4
+ extend Memoist
5
+ include AwsServices
6
+
7
+ def region
8
+ return 'us-east-1' if ENV['TEST']
9
+
10
+ region = nil
11
+
12
+ # First try to get it from the ~/.aws/config
13
+ region = `aws configure get region`.strip rescue nil
14
+ return region if region
15
+
16
+ # Second try the metadata endpoint, should be available on AWS Lambda environment
17
+ # https://stackoverflow.com/questions/4249488/find-region-from-within-an-ec2-instance
18
+ begin
19
+ az = `curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`
20
+ region = az.strip.chop # remove last char
21
+ rescue
22
+ end
23
+ return region if region
24
+
25
+ ENV['JETS_AWS_REGION'] || 'us-east-1' # default if all else fails
26
+ end
27
+ memoize :region
28
+
29
+ # aws sts get-caller-identity
30
+ def account
31
+ return '123456789' if ENV['TEST']
32
+ sts.get_caller_identity.account
33
+ end
34
+ memoize :account
35
+ end
36
+ end
@@ -1,6 +1,7 @@
1
1
  require "aws-sdk-s3"
2
2
  require "aws-sdk-cloudformation"
3
3
  require "aws-sdk-lambda"
4
+ require "aws-sdk-sts"
4
5
 
5
6
  module Jets::AwsServices
6
7
  def s3
@@ -19,6 +20,10 @@ module Jets::AwsServices
19
20
  @lambda ||= Aws::Lambda::Client.new
20
21
  end
21
22
 
23
+ def sts
24
+ @sts ||= Aws::STS::Client.new
25
+ end
26
+
22
27
  def stack_exists?(stack_name)
23
28
  return false if ENV['TEST']
24
29
 
@@ -359,7 +359,6 @@ EOL
359
359
  md = spec.source.to_s.match(/@(\w+)\)/)
360
360
  git_sha = md[1]
361
361
  end
362
- puts "git_shas #{git_shas.inspect}"
363
362
 
364
363
  # IE: /tmp/jets/demo/cache/bundled/gems/ruby/2.5.0/bundler/gems/webpacker-a8c46614c675
365
364
  Dir.glob("#{cache_area}/bundled/gems/ruby/2.5.0/bundler/gems/*").each do |path|
@@ -2,6 +2,7 @@ require 'active_support/core_ext/hash'
2
2
  require 'yaml'
3
3
 
4
4
  class Jets::Cfn
5
+ # TODO: Refactor builder classes. They all work slightly differently.
5
6
  class TemplateBuilders
6
7
  autoload :Interface, "jets/cfn/template_builders/interface"
7
8
  autoload :ParentBuilder, "jets/cfn/template_builders/parent_builder"
@@ -15,7 +16,9 @@ class Jets::Cfn
15
16
 
16
17
  autoload :ApiGatewayBuilder, "jets/cfn/template_builders/api_gateway_builder"
17
18
  autoload :ApiGatewayDeploymentBuilder, "jets/cfn/template_builders/api_gateway_deployment_builder"
18
- # separate beast:
19
- autoload :FunctionProperties, "jets/cfn/template_builders/function_properties"
19
+
20
+ # separate beasts:
21
+ autoload :FunctionProperties, "jets/cfn/template_builders/function_properties" # sort of a builder
22
+ autoload :IamPolicy, "jets/cfn/template_builders/iam_policy" # resource only
20
23
  end
21
24
  end
@@ -43,9 +43,8 @@ class Jets::Cfn::TemplateBuilders
43
43
  # Adds route related Resources and Outputs
44
44
  def add_gateway_routes
45
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.
46
+ # TODO: Support more routes. Right now outputing all routes in 1 template will hit the 60 routes limit.
47
+ # Will have to either output them as a joined string or break this up to multiple tempaltes.
49
48
  # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
50
49
  # Outputs: Maximum number of outputs that you can declare in your AWS CloudFormation template. 60 outputs
51
50
  # Output name: Maximum size of an output name. 255 characters.
@@ -20,8 +20,10 @@ class Jets::Cfn::TemplateBuilders
20
20
  end
21
21
 
22
22
  def add_functions
23
+ add_class_iam_policy if @app_klass.class_iam_policy
23
24
  @app_klass.tasks.each do |task|
24
25
  add_function(task)
26
+ add_iam_policy(task) if task.iam_policy
25
27
  end
26
28
  end
27
29
 
@@ -34,5 +36,19 @@ class Jets::Cfn::TemplateBuilders
34
36
  logical_id = builder.map.logical_id
35
37
  add_resource(logical_id, "AWS::Lambda::Function", builder.properties)
36
38
  end
39
+
40
+ def add_class_iam_policy
41
+ map = Jets::Cfn::TemplateMappers::IamPolicy::ClassPolicyMapper.new(@app_klass)
42
+ logical_id = map.logical_id
43
+ properties = map.properties
44
+ add_resource(logical_id, "AWS::IAM::Role", properties)
45
+ end
46
+
47
+ def add_iam_policy(task)
48
+ map = Jets::Cfn::TemplateMappers::IamPolicy::FunctionPolicyMapper.new(task)
49
+ logical_id = map.logical_id
50
+ properties = map.properties
51
+ add_resource(logical_id, "AWS::IAM::Role", properties)
52
+ end
37
53
  end
38
54
  end
@@ -79,10 +79,19 @@ module Jets::Cfn::TemplateBuilders::FunctionProperties
79
79
  # class_timeout 22
80
80
  # ...
81
81
  # end
82
+ #
83
+ # Also handles iam policy override at the class level. Example:
84
+ #
85
+ # class_iam_policy("logs:*")
86
+ #
82
87
  def class_properties
83
88
  # klass is PostsController, HardJob, GameRule, Hello or HelloFunction
84
89
  klass = Jets::Klass.from_task(@task)
85
90
  class_properties = klass.class_properties
91
+ if klass.class_iam_policy
92
+ map = Jets::Cfn::TemplateMappers::IamPolicy::ClassPolicyMapper.new(klass)
93
+ class_properties[:Role] = "!GetAtt #{map.logical_id}.Arn"
94
+ end
86
95
  Pascalize.pascalize(class_properties.deep_stringify_keys)
87
96
  end
88
97
 
@@ -93,8 +102,21 @@ module Jets::Cfn::TemplateBuilders::FunctionProperties
93
102
  # def index
94
103
  # ...
95
104
  # end
105
+ #
106
+ # Also handles iam policy override at the function level. Example:
107
+ #
108
+ # iam_policy("ec2:*")
109
+ # def new
110
+ # render json: params.merge(action: "new")
111
+ # end
112
+ #
96
113
  def function_properties
97
- Pascalize.pascalize(@task.properties.deep_stringify_keys)
114
+ properties = @task.properties
115
+ if @task.iam_policy
116
+ map = Jets::Cfn::TemplateMappers::IamPolicy::FunctionPolicyMapper.new(@task)
117
+ properties[:Role] = "!GetAtt #{map.logical_id}.Arn"
118
+ end
119
+ Pascalize.pascalize(properties.deep_stringify_keys)
98
120
  end
99
121
 
100
122
  def env_file_properties
@@ -0,0 +1,6 @@
1
+ module Jets::Cfn::TemplateBuilders::IamPolicy
2
+ autoload :ApplicationPolicy, "jets/cfn/template_builders/iam_policy/application_policy"
3
+ autoload :BasePolicy, "jets/cfn/template_builders/iam_policy/base_policy"
4
+ autoload :ClassPolicy, "jets/cfn/template_builders/iam_policy/class_policy"
5
+ autoload :FunctionPolicy, "jets/cfn/template_builders/iam_policy/function_policy"
6
+ end
@@ -0,0 +1,18 @@
1
+ # Implements:
2
+ # initialize
3
+ # policy_name
4
+ #
5
+ module Jets::Cfn::TemplateBuilders::IamPolicy
6
+ class ApplicationPolicy < BasePolicy
7
+ def initialize
8
+ setup
9
+ @definitions = Jets.config.iam_policy || [] # config.iam_policy contains definitions
10
+ end
11
+
12
+ # Example: PostsControllerPolicy or SleepJobPolicy
13
+ # Note: There is no "method" in the name
14
+ def policy_name
15
+ "ApplicationPolicy"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ # Classes that inherit this Base class should implement:
2
+ #
3
+ # initialize - should call setup in it
4
+ # policy_name
5
+ #
6
+ module Jets::Cfn::TemplateBuilders::IamPolicy
7
+ class BasePolicy
8
+ extend Memoist
9
+
10
+ attr_reader :definitions
11
+ # Not using initialize because method signature is different
12
+ def setup
13
+ # empty starting policy that will be changed
14
+ @policy = {
15
+ "Version" => "2012-10-17",
16
+ "Statement" => []
17
+ }
18
+ # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_sid.html
19
+ @sid = 0 # counter
20
+ end
21
+
22
+ def policy_document
23
+ definitions.map { |definition| standardize(definition) }
24
+ # Thanks: https://www.mnishiguchi.com/2017/11/29/rails-hash-camelize-and-underscore-keys/
25
+ @policy.deep_transform_keys! { |key| key.to_s.camelize }
26
+ end
27
+ memoize :policy_document # only process policy_document once
28
+
29
+ def standardize(definition)
30
+ @sid += 1
31
+ case definition
32
+ when String
33
+ @policy["Statement"] << {
34
+ sid: "Stmt#{@sid}",
35
+ action: [definition],
36
+ effect: "Allow",
37
+ resource: "*",
38
+ }
39
+ when Hash
40
+ definition = definition.stringify_keys
41
+ if definition.key?("Version") # special case where we replace the policy entirely
42
+ @policy = definition
43
+ else
44
+ @policy["Statement"] << definition
45
+ end
46
+ end
47
+ end
48
+
49
+ # Need to underscore and then classify again for this case:
50
+ # Jets::PreheatJob_policy => JetsPreheatJobPolicy
51
+ # Or else you we get this:
52
+ # Jets::PreheatJob_policy => JetsPreheatjobPolicy
53
+ def classify_name(text)
54
+ text.gsub('::','_').underscore.classify
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ # Implements:
2
+ # initialize
3
+ # policy_name
4
+ #
5
+ module Jets::Cfn::TemplateBuilders::IamPolicy
6
+ class ClassPolicy < BasePolicy
7
+ def initialize(app_class)
8
+ setup
9
+ @app_class = app_class
10
+ # IE: @app_class: PostsController, HardJob, Hello, HelloFunction
11
+ @definitions = app_class.class_iam_policy || [] # class_iam_policy contains definitions
12
+ end
13
+
14
+ # Example: PostsControllerPolicy or SleepJobPolicy
15
+ # Note: There is no "method" in the name
16
+ def policy_name
17
+ classify_name("#{@app_class}_policy")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # Implements:
2
+ # initialize
3
+ # policy_name
4
+ #
5
+ module Jets::Cfn::TemplateBuilders::IamPolicy
6
+ class FunctionPolicy < BasePolicy
7
+ def initialize(task)
8
+ setup
9
+ @task = task
10
+ @app_class = task.class_name.to_s
11
+ # IE: @app_class: PostsController, HardJob, Hello, HelloFunction
12
+
13
+ @definitions = task.iam_policy || [] # iam_policy contains definitions
14
+ end
15
+
16
+ # Example: PostsControllerIndexPolicy or SleepJobPerformPolicy
17
+ def policy_name
18
+ classify_name("#{@app_class}_#{@task.meth}_policy")
19
+ end
20
+ end
21
+ end
@@ -43,7 +43,7 @@ class Jets::Cfn::TemplateBuilders
43
43
  results.join("\n") + "\n"
44
44
  end
45
45
 
46
- # add_resource handles an options Hash with both only Rroperties
46
+ # add_resource handles an options Hash with both only Properties
47
47
  # and also one with a nested Properties.
48
48
 
49
49
  # Example:
@@ -32,9 +32,11 @@ class Jets::Cfn::TemplateBuilders
32
32
  }
33
33
  rendered_result = Jets::Erb.result(path, variables)
34
34
  minimal_template = YAML.load(rendered_result)
35
-
36
- # minimal_template = YAML.load(IO.read(path))
37
35
  @template.deep_merge!(minimal_template)
36
+
37
+ # Add application-wide IAM policy from Jets.config.iam_role
38
+ map = Jets::Cfn::TemplateMappers::IamPolicy::ApplicationPolicyMapper.new
39
+ add_resource(map.logical_id, "AWS::IAM::Role", map.properties)
38
40
  end
39
41
 
40
42
  def add_child_resources
@@ -48,7 +50,6 @@ class Jets::Cfn::TemplateBuilders
48
50
  #
49
51
  # map = Jets::Cfn::TemplateMappers::ControllerMapper.new(path, s3_bucket)
50
52
  # map = Jets::Cfn::TemplateMappers::JobMapper.new(path, s3_bucket)
51
- # map = Jets::Cfn::TemplateMappers::FunctionMapper.new(path, s3_bucket)
52
53
  #
53
54
  mapper_class_name = File.basename(path, '.yml').split('_').last
54
55
  mapper_class_name = mapper_class_name.classify
@@ -68,9 +69,6 @@ class Jets::Cfn::TemplateBuilders
68
69
  end
69
70
  end
70
71
 
71
- # Each shared stacks has different logic.
72
- # Handle in ugly case statement until we see the common patterns between them.
73
- # TODO: clean up the add_shared_stack logical after we figure out the common interface pattern
74
72
  def add_api_gateway
75
73
  path = "#{Jets.config.project_namespace}-api-gateway.yml"
76
74
  map = Jets::Cfn::TemplateMappers::ApiGatewayMapper.new(path, @options[:s3_bucket])
@@ -2,42 +2,6 @@
2
2
  Resources:
3
3
  S3Bucket:
4
4
  Type: AWS::S3::Bucket
5
- IamRole:
6
- Type: AWS::IAM::Role
7
- Properties:
8
- AssumeRolePolicyDocument:
9
- Version: '2012-10-17'
10
- Statement:
11
- - Effect: Allow
12
- Principal:
13
- Service:
14
- - lambda.amazonaws.com
15
- Action:
16
- - sts:AssumeRole
17
- Policies:
18
- - PolicyName: <%= @policy_name %>
19
- PolicyDocument:
20
- Version: '2012-10-17'
21
- Statement:
22
- - Effect: Allow
23
- Action:
24
- - logs:*
25
- Resource: '*'
26
- # TODO: Make IAM permissions more fine grain for mimimal stack
27
- # - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/quiz-dev-hello:*:*
28
- # - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/quiz-dev-quiz:*:*
29
- # TODO: Allow hook so user can configure IAM themselves and support managed polices
30
- - Effect: Allow
31
- Action:
32
- - dynamodb:*
33
- Resource: "*"
34
- # required for PreheatJob which calls the other lambda functions repeatedly
35
- - Effect: Allow
36
- Action:
37
- - lambda:*
38
- Resource: "*"
39
- Path: "/"
40
- RoleName: <%= @role_name %>
41
5
  Outputs:
42
6
  S3Bucket:
43
7
  Value: "!Ref S3Bucket" # not a valid cfn notation, surrounding double quotes is handled by post processing.
@@ -19,5 +19,7 @@ class Jets::Cfn
19
19
 
20
20
  autoload :EventsRuleMapper, "jets/cfn/template_mappers/events_rule_mapper"
21
21
  autoload :ConfigRuleMapper, "jets/cfn/template_mappers/config_rule_mapper"
22
+
23
+ autoload :IamPolicy, "jets/cfn/template_mappers/iam_policy"
22
24
  end
23
25
  end
@@ -0,0 +1,6 @@
1
+ module Jets::Cfn::TemplateMappers::IamPolicy
2
+ autoload :ApplicationPolicyMapper, "jets/cfn/template_mappers/iam_policy/application_policy_mapper"
3
+ autoload :BasePolicyMapper, "jets/cfn/template_mappers/iam_policy/base_policy_mapper"
4
+ autoload :ClassPolicyMapper, "jets/cfn/template_mappers/iam_policy/class_policy_mapper"
5
+ autoload :FunctionPolicyMapper, "jets/cfn/template_mappers/iam_policy/function_policy_mapper"
6
+ end
@@ -0,0 +1,28 @@
1
+ # Implements:
2
+ #
3
+ # initialize
4
+ # iam_policy
5
+ # logical_id
6
+ # role_name
7
+ #
8
+ module Jets::Cfn::TemplateMappers::IamPolicy
9
+ class ApplicationPolicyMapper < BasePolicyMapper
10
+ def initialize; end # does nothing
11
+
12
+ def iam_policy
13
+ Jets::Cfn::TemplateBuilders::IamPolicy::ApplicationPolicy.new
14
+ end
15
+ memoize :iam_policy
16
+
17
+ # Example: PostsControllerLambdaFunction
18
+ # Note there are is no "Show" action in the name
19
+ def logical_id
20
+ "IamRole" # very simple logical ideal for the application-wide logical id
21
+ end
22
+
23
+ # There should be namespace in the role_name.
24
+ def role_name
25
+ "#{namespace}_application_iam_role".underscore.dasherize
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ # Class that inherits this base class should implement:
2
+ #
3
+ # initialize
4
+ # iam_policy
5
+ # logical_id
6
+ # role_name
7
+ #
8
+ module Jets::Cfn::TemplateMappers::IamPolicy
9
+ class BasePolicyMapper
10
+ extend Memoist
11
+
12
+ def properties
13
+ properties = {
14
+ AssumeRolePolicyDocument: {
15
+ Version: "2012-10-17",
16
+ Statement: [{
17
+ Effect: "Allow",
18
+ Principal: {Service: ["lambda.amazonaws.com"]},
19
+ Action: ["sts:AssumeRole"]}
20
+ ]},
21
+ Path: "/"
22
+ }
23
+ properties[:Policies] = [
24
+ PolicyName: iam_policy.policy_name,
25
+ PolicyDocument: iam_policy.policy_document,
26
+ ]
27
+ properties[:RoleName] = role_name
28
+ properties.deep_stringify_keys!
29
+ properties
30
+ end
31
+
32
+ def namespace
33
+ Jets.config.project_namespace.underscore
34
+ end
35
+
36
+ # Need to underscore and then classify again for this case:
37
+ # Jets::PreheatJob_policy => JetsPreheatJobPolicy
38
+ # Or else you we get this:
39
+ # Jets::PreheatJob_policy => JetsPreheatjobPolicy
40
+ def classify_name(text)
41
+ text.gsub('::','_').underscore.classify
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # Implements:
2
+ #
3
+ # initialize
4
+ # iam_policy
5
+ # logical_id
6
+ # role_name
7
+ #
8
+ module Jets::Cfn::TemplateMappers::IamPolicy
9
+ class ClassPolicyMapper < BasePolicyMapper
10
+ def initialize(app_class)
11
+ @app_class = app_class
12
+ # IE: @app_class: PostsController, HardJob, Hello, HelloFunction
13
+ end
14
+
15
+ def iam_policy
16
+ Jets::Cfn::TemplateBuilders::IamPolicy::ClassPolicy.new(@app_class)
17
+ end
18
+ memoize :iam_policy
19
+
20
+ # Example: PostsControllerLambdaFunction
21
+ # Note there are is no "Show" action in the name
22
+ # There should be no namespace in the logical_id.
23
+ def logical_id
24
+ classify_name("#{@app_class}_iam_role")
25
+ end
26
+
27
+ # There should be namespace in the role_name.
28
+ def role_name
29
+ classify_name("#{namespace}_#{@app_class}_iam_role").underscore.dasherize
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # Implements:
2
+ #
3
+ # initialize
4
+ # iam_policy
5
+ # logical_id
6
+ # role_name
7
+ #
8
+ module Jets::Cfn::TemplateMappers::IamPolicy
9
+ class FunctionPolicyMapper < BasePolicyMapper
10
+ def initialize(task)
11
+ @task = task
12
+ @app_class = task.class_name.to_s
13
+ # IE: @app_class: PostsController, HardJob, Hello, HelloFunction
14
+ end
15
+
16
+ def iam_policy
17
+ Jets::Cfn::TemplateBuilders::IamPolicy::FunctionPolicy.new(@task)
18
+ end
19
+ memoize :iam_policy
20
+
21
+ # Example: PostsControllerShowLambdaFunction
22
+ # There should be no namespace in the logical_id.
23
+ def logical_id
24
+ classify_name("#{@app_class}_#{@task.meth}_iam_role")
25
+ end
26
+
27
+ # There should be namespace in the role_name.
28
+ def role_name
29
+ classify_name("#{namespace}_#{@app_class}_#{@task.meth}_iam_role").underscore.dasherize
30
+ end
31
+ end
32
+ end
@@ -13,7 +13,9 @@ class Jets::Cfn::TemplateMappers
13
13
 
14
14
  def environment
15
15
  env = Jets.config.environment ? Jets.config.environment.to_h : {}
16
- env.deep_merge(JETS_ENV: Jets.env.to_s)
16
+ jets_env_options = {JETS_ENV: Jets.env.to_s}
17
+ jets_env_options[:JETS_ENV_EXTRA] = Jets.config.env_extra if Jets.config.env_extra
18
+ env.deep_merge(jets_env_options)
17
19
  end
18
20
 
19
21
  # Example: PostsControllerIndex or SleepJobPerform
@@ -41,7 +41,7 @@ module Jets::Commands
41
41
 
42
42
  def build_all_templates
43
43
  clean_templates
44
- # TODO: move this build.rb logic to cfn/builder.rb
44
+ # TODO: Maybe move this tbuild.rb template related logic to cfn/builder.rb
45
45
  ## CloudFormation templates
46
46
  puts "Building Lambda functions as CloudFormation templates."
47
47
  # 1. Shared templates - child templates needs them
@@ -107,7 +107,7 @@ module Jets::Commands
107
107
  # Crucial that the Dir.pwd is in the tmp_app_root because for
108
108
  # because Jets.boot set ups autoload_paths and this is how project
109
109
  # classes are loaded.
110
- # TODO: rework code so this is not the case.
110
+ # TODO: rework code so that Dir.pwd does not have to be in tmp_app_root for build to work.
111
111
  def self.app_files
112
112
  paths = []
113
113
  expression = "#{Jets.root}app/**/**/*.rb"
@@ -146,9 +146,9 @@ class Jets::Commands::Call
146
146
  puts "Pro tip: The Lambda Console Link to the #{function_name} function has been added to your clipboard." unless @options[:mute]
147
147
  end
148
148
 
149
+ # TODO: Hook client_context up and maek sure it works. Think I've figure out how to sign client_context below.
149
150
  # Client context must be a valid Base64-encoded JSON object
150
151
  # Example: http://docs.aws.amazon.com/mobileanalytics/latest/ug/PutEvents.html
151
- # TODO: figure out how to sign client_context
152
152
  def client_context
153
153
  context = {
154
154
  "client" => {
data/lib/jets/core.rb CHANGED
@@ -23,6 +23,10 @@ module Jets::Core
23
23
  application.config
24
24
  end
25
25
 
26
+ def aws
27
+ application.aws
28
+ end
29
+
26
30
  # Load all application base classes and project classes
27
31
  def boot
28
32
  Jets::Booter.boot!
@@ -77,7 +81,7 @@ module Jets::Core
77
81
  def version
78
82
  Jets::VERSION
79
83
  end
80
-
84
+
81
85
  def eager_load!
82
86
  Dir.glob("#{Jets.root}app/**/*.rb").select do |path|
83
87
  next if !File.file?(path) or path =~ %r{/javascript/} or path =~ %r{/views/}
@@ -2,8 +2,7 @@ require "rack/mime"
2
2
 
3
3
  # Works for utf8 text files.
4
4
  # TODO: Add support to public_controller for binary data like images.
5
- # Tricky because API Gateway is not respecting the Accept header in the
6
- # same way as browsers.
5
+ # Tricky because API Gateway is not respecting the Accept header the same way as browsers.
7
6
  class Jets::PublicController < Jets::Controller::Base
8
7
  layout false
9
8
  internal true
@@ -7,6 +7,20 @@ class Jets::PreheatJob < ApplicationJob
7
7
 
8
8
  class_timeout 30
9
9
  class_memory 1024
10
+ class_iam_policy(
11
+ {
12
+ sid: "Statement1",
13
+ action: ["logs:*"],
14
+ effect: "Allow",
15
+ resource: "arn:aws:logs:#{Jets.aws.region}:#{Jets.aws.account}:log-group:#{Jets.config.project_namespace}-*",
16
+ },
17
+ {
18
+ sid: "Statement2",
19
+ action: ["lambda:InvokeFunction", "lambda:InvokeAsync"],
20
+ effect: "Allow",
21
+ resource: "arn:aws:lambda:#{Jets.aws.region}:#{Jets.aws.account}:function:#{Jets.config.project_namespace}-*",
22
+ }
23
+ )
10
24
 
11
25
  unless Jets::Commands::Build.poly_only?
12
26
  torching ? rate(PREWARM_RATE) : disable(true)
@@ -76,6 +76,24 @@ module Jets::Lambda::Dsl
76
76
  end
77
77
  alias_method :props, :properties
78
78
 
79
+ # definitions: one more many definitions
80
+ def class_iam_policy(*definitions)
81
+ if definitions.empty?
82
+ @class_iam_policy
83
+ else
84
+ @class_iam_policy = definitions.flatten
85
+ end
86
+ end
87
+
88
+ # definitions: one more many definitions
89
+ def iam_policy(*definitions)
90
+ if definitions.empty?
91
+ @iam_policy
92
+ else
93
+ @iam_policy = definitions.flatten
94
+ end
95
+ end
96
+
79
97
  # meth is a Symbol
80
98
  def method_added(meth)
81
99
  return if %w[initialize method_missing].include?(meth.to_s)
@@ -89,7 +107,7 @@ module Jets::Lambda::Dsl
89
107
  # We adjust the class name when we build the functions later in
90
108
  # FunctionContstructor#adjust_tasks.
91
109
  all_tasks[meth] = Jets::Lambda::Task.new(self.name, meth,
92
- properties: @properties, lang: lang)
110
+ properties: @properties, iam_policy: @iam_policy, lang: lang)
93
111
 
94
112
  # Done storing options, clear out for the next added method.
95
113
  clear_properties
@@ -106,6 +124,7 @@ module Jets::Lambda::Dsl
106
124
 
107
125
  def clear_properties
108
126
  @properties = nil
127
+ @iam_policy = nil
109
128
  end
110
129
 
111
130
  # Returns the all tasks for this class with their method names as keys.
@@ -1,12 +1,13 @@
1
1
  class Jets::Lambda::Task
2
2
  attr_accessor :class_name, :type
3
- attr_reader :meth, :properties, :lang
3
+ attr_reader :meth, :properties, :iam_policy, :lang
4
4
  def initialize(class_name, meth, options={})
5
5
  @class_name = class_name.to_s # use at EventsRuleMapper#full_task_name
6
6
  @meth = meth
7
7
  @options = options
8
8
  @type = options[:type] || get_type # controller, job, or function
9
9
  @properties = options[:properties] || {}
10
+ @iam_policy = options[:iam_policy]
10
11
  @lang = options[:lang] || :ruby
11
12
  end
12
13
 
data/lib/jets/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jets
2
- VERSION = "0.5.8"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jets
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.6.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: 2018-08-17 00:00:00.000000000 Z
11
+ date: 2018-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -400,6 +400,7 @@ files:
400
400
  - lib/jets.rb
401
401
  - lib/jets/application.rb
402
402
  - lib/jets/application/middleware.rb
403
+ - lib/jets/aws_info.rb
403
404
  - lib/jets/aws_services.rb
404
405
  - lib/jets/booter.rb
405
406
  - lib/jets/builders.rb
@@ -423,6 +424,11 @@ files:
423
424
  - lib/jets/cfn/template_builders/function_properties/node_builder.rb
424
425
  - lib/jets/cfn/template_builders/function_properties/python_builder.rb
425
426
  - lib/jets/cfn/template_builders/function_properties/ruby_builder.rb
427
+ - lib/jets/cfn/template_builders/iam_policy.rb
428
+ - lib/jets/cfn/template_builders/iam_policy/application_policy.rb
429
+ - lib/jets/cfn/template_builders/iam_policy/base_policy.rb
430
+ - lib/jets/cfn/template_builders/iam_policy/class_policy.rb
431
+ - lib/jets/cfn/template_builders/iam_policy/function_policy.rb
426
432
  - lib/jets/cfn/template_builders/interface.rb
427
433
  - lib/jets/cfn/template_builders/job_builder.rb
428
434
  - lib/jets/cfn/template_builders/parent_builder.rb
@@ -438,6 +444,11 @@ files:
438
444
  - lib/jets/cfn/template_mappers/function_mapper.rb
439
445
  - lib/jets/cfn/template_mappers/gateway_method_mapper.rb
440
446
  - lib/jets/cfn/template_mappers/gateway_resource_mapper.rb
447
+ - lib/jets/cfn/template_mappers/iam_policy.rb
448
+ - lib/jets/cfn/template_mappers/iam_policy/application_policy_mapper.rb
449
+ - lib/jets/cfn/template_mappers/iam_policy/base_policy_mapper.rb
450
+ - lib/jets/cfn/template_mappers/iam_policy/class_policy_mapper.rb
451
+ - lib/jets/cfn/template_mappers/iam_policy/function_policy_mapper.rb
441
452
  - lib/jets/cfn/template_mappers/job_mapper.rb
442
453
  - lib/jets/cfn/template_mappers/lambda_function_mapper.rb
443
454
  - lib/jets/cfn/template_mappers/rule_mapper.rb