jets 0.5.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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