jets 0.8.18 → 0.9.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +0 -3
  3. data/CHANGELOG.md +20 -1
  4. data/Gemfile.lock +6 -2
  5. data/README/prerelease.md +6 -0
  6. data/README/testing.md +41 -0
  7. data/Rakefile +9 -1
  8. data/jets.gemspec +1 -0
  9. data/lib/jets.rb +17 -18
  10. data/lib/jets/application.rb +26 -3
  11. data/lib/jets/aws_services.rb +26 -59
  12. data/lib/jets/aws_services/stack_status.rb +52 -0
  13. data/lib/jets/builders.rb +3 -2
  14. data/lib/jets/builders/handler_generator.rb +38 -2
  15. data/lib/jets/builders/shared_deducer.rb +32 -0
  16. data/lib/jets/cfn/builders.rb +3 -1
  17. data/lib/jets/cfn/builders/api_deployment_builder.rb +1 -1
  18. data/lib/jets/cfn/builders/api_gateway_builder.rb +1 -1
  19. data/lib/jets/cfn/builders/base_child_builder.rb +37 -7
  20. data/lib/jets/cfn/builders/controller_builder.rb +6 -1
  21. data/lib/jets/cfn/builders/function_builder.rb +5 -0
  22. data/lib/jets/cfn/builders/interface.rb +5 -6
  23. data/lib/jets/cfn/builders/job_builder.rb +5 -0
  24. data/lib/jets/cfn/builders/parent_builder.rb +17 -16
  25. data/lib/jets/cfn/builders/rule_builder.rb +6 -1
  26. data/lib/jets/cfn/builders/shared_builder.rb +14 -0
  27. data/lib/jets/commands.rb +9 -8
  28. data/lib/jets/commands/build.rb +36 -14
  29. data/lib/jets/commands/console.rb +1 -0
  30. data/lib/jets/commands/help/runner.md +17 -0
  31. data/lib/jets/commands/main.rb +7 -0
  32. data/lib/jets/commands/new.rb +39 -19
  33. data/lib/jets/commands/runner.rb +18 -0
  34. data/lib/jets/commands/sequence.rb +27 -1
  35. data/lib/jets/commands/templates/skeleton/.rspec +3 -0
  36. data/lib/jets/commands/templates/skeleton/Gemfile.tt +0 -1
  37. data/lib/jets/commands/templates/skeleton/app/jobs/application_job.rb +2 -0
  38. data/lib/jets/commands/templates/skeleton/config/application.rb.tt +2 -1
  39. data/lib/jets/commands/templates/skeleton/config/routes.rb +5 -1
  40. data/lib/jets/commands/templates/skeleton/spec/spec_helper.rb.tt +5 -4
  41. data/lib/jets/core.rb +8 -6
  42. data/lib/jets/default/application.rb +1 -0
  43. data/lib/jets/generator.rb +1 -1
  44. data/lib/jets/inflections.rb +23 -0
  45. data/lib/jets/job/dsl.rb +69 -47
  46. data/lib/jets/klass.rb +6 -1
  47. data/lib/jets/lambda/dsl.rb +102 -34
  48. data/lib/jets/lambda/function_constructor.rb +2 -2
  49. data/lib/jets/lambda/task.rb +10 -5
  50. data/lib/jets/naming.rb +13 -2
  51. data/lib/jets/processors/deducer.rb +13 -2
  52. data/lib/jets/processors/main_processor.rb +1 -1
  53. data/lib/jets/rails_overrides.rb +1 -1
  54. data/lib/jets/resource.rb +20 -5
  55. data/lib/jets/resource/api_gateway/deployment.rb +0 -1
  56. data/lib/jets/resource/associated.rb +26 -0
  57. data/lib/jets/resource/base.rb +12 -0
  58. data/lib/jets/resource/child_stack.rb +2 -0
  59. data/lib/jets/resource/child_stack/api_deployment.rb +9 -15
  60. data/lib/jets/resource/child_stack/api_gateway.rb +8 -8
  61. data/lib/jets/resource/child_stack/app_class.rb +41 -16
  62. data/lib/jets/resource/child_stack/base.rb +24 -0
  63. data/lib/jets/resource/child_stack/shared.rb +90 -0
  64. data/lib/jets/resource/config.rb +4 -0
  65. data/lib/jets/resource/config/config_rule.rb +66 -0
  66. data/lib/jets/resource/config/managed_rule.rb +15 -0
  67. data/lib/jets/resource/events.rb +3 -0
  68. data/lib/jets/resource/events/rule.rb +31 -0
  69. data/lib/jets/resource/iam/application_role.rb +2 -2
  70. data/lib/jets/resource/iam/base_role_definition.rb +4 -2
  71. data/lib/jets/resource/iam/class_role.rb +50 -2
  72. data/lib/jets/resource/iam/function_role.rb +28 -0
  73. data/lib/jets/resource/iam/policy_document.rb +0 -4
  74. data/lib/jets/resource/permission.rb +12 -6
  75. data/lib/jets/resource/replacer.rb +4 -0
  76. data/lib/jets/resource/sns.rb +3 -0
  77. data/lib/jets/resource/standardizer.rb +42 -0
  78. data/lib/jets/router.rb +9 -1
  79. data/lib/jets/rule/dsl.rb +51 -78
  80. data/lib/jets/stack.rb +105 -0
  81. data/lib/jets/stack/builder.rb +38 -0
  82. data/lib/jets/stack/definition.rb +50 -0
  83. data/lib/jets/stack/function.rb +60 -0
  84. data/lib/jets/stack/main.rb +5 -0
  85. data/lib/jets/stack/main/dsl.rb +33 -0
  86. data/lib/jets/stack/main/extensions/base.rb +45 -0
  87. data/lib/jets/stack/main/extensions/cloudwatch.rb +19 -0
  88. data/lib/jets/stack/main/extensions/lambda.rb +69 -0
  89. data/lib/jets/stack/main/extensions/sns.rb +12 -0
  90. data/lib/jets/stack/main/extensions/sqs.rb +8 -0
  91. data/lib/jets/stack/output.rb +38 -0
  92. data/lib/jets/stack/output/dsl.rb +19 -0
  93. data/lib/jets/stack/output/lookup.rb +36 -0
  94. data/lib/jets/stack/parameter.rb +38 -0
  95. data/lib/jets/stack/parameter/dsl.rb +42 -0
  96. data/lib/jets/stack/resource.rb +30 -0
  97. data/lib/jets/stack/resource/dsl.rb +19 -0
  98. data/lib/jets/version.rb +1 -1
  99. metadata +53 -4
  100. data/support/clean +0 -3
  101. data/support/console +0 -3
@@ -22,5 +22,33 @@ module Jets::Resource::Iam
22
22
  namespace: "#{@task.class_name.gsub('::','')}#{@task.meth.to_s.camelize}", # camelized because can be used as value
23
23
  }
24
24
  end
25
+
26
+ def policy_document
27
+ if inherit?
28
+ @policy_definitions += class_role.policy_definitions + application_role.policy_definitions
29
+ end
30
+ super
31
+ end
32
+
33
+ def managed_policy_arns
34
+ if inherit?
35
+ @managed_policy_definitions += class_role.managed_policy_definitions + application_role.managed_policy_definitions
36
+ end
37
+ super
38
+ end
39
+
40
+ def inherit?
41
+ !@policy_definitions.empty? || !@managed_policy_definitions.empty?
42
+ end
43
+
44
+ def class_role
45
+ Jets::Resource::Iam::ClassRole.new(@task.class_name.constantize)
46
+ end
47
+ memoize :class_role
48
+
49
+ def application_role
50
+ Jets::Resource::Iam::ApplicationRole.new
51
+ end
52
+ memoize :application_role
25
53
  end
26
54
  end
@@ -10,8 +10,6 @@ module Jets::Resource::Iam
10
10
  version: "2012-10-17",
11
11
  statement: []
12
12
  }
13
- # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_sid.html
14
- @sid = 0 # counter
15
13
  end
16
14
 
17
15
  def policy_document
@@ -21,13 +19,11 @@ module Jets::Resource::Iam
21
19
  memoize :policy_document # only process policy_document once
22
20
 
23
21
  def standardize(definition)
24
- @sid += 1
25
22
  case definition
26
23
  when String
27
24
  # Expands simple string from: logs => logs:*
28
25
  definition = "#{definition}:*" unless definition.include?(':')
29
26
  @policy[:statement] << {
30
- sid: "Stmt#{@sid}",
31
27
  action: [definition],
32
28
  effect: "Allow",
33
29
  resource: "*",
@@ -6,17 +6,24 @@ class Jets::Resource
6
6
  end
7
7
 
8
8
  def definition
9
- {
10
- permission_logical_id => {
9
+ logical_id = permission_logical_id
10
+
11
+ definition = {
12
+ logical_id => {
11
13
  type: "AWS::Lambda::Permission",
12
14
  properties: {
13
15
  function_name: "!GetAtt {namespace}LambdaFunction.Arn",
14
16
  action: "lambda:InvokeFunction",
15
- principal: principal,
16
- source_arn: source_arn,
17
+ principal: principal
17
18
  }
18
19
  }
19
20
  }
21
+
22
+ # From AWS docs: https://amzn.to/2N0QXQL
23
+ # source_arn is "not supported by all event sources"
24
+ definition[logical_id][:properties][:source_arn] = source_arn if source_arn
25
+
26
+ definition
20
27
  end
21
28
 
22
29
  def permission_logical_id
@@ -32,8 +39,7 @@ class Jets::Resource
32
39
  end
33
40
 
34
41
  def source_arn
35
- default_arn = "!GetAtt #{@associated_resource.logical_id}.Arn"
36
- Replacer.source_arn_map(@associated_resource.type) || default_arn
42
+ Replacer.source_arn_map(@associated_resource.type)
37
43
  end
38
44
  end
39
45
  end
@@ -62,6 +62,10 @@ class Jets::Resource
62
62
  "#{service}.amazonaws.com"
63
63
  end
64
64
 
65
+ # From AWS docs: https://amzn.to/2N0QXQL
66
+ # source_arn is "not supported by all event sources"
67
+ #
68
+ # When it is not available the resource definition should add it.
65
69
  def source_arn_map(type)
66
70
  map = {
67
71
  "AWS::ApiGateway::Method" => "!Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*",
@@ -0,0 +1,3 @@
1
+ module Jets::Resource::Sns
2
+ autoload :Topic, 'jets/resource/sns/topic'
3
+ end
@@ -0,0 +1,42 @@
1
+ class Jets::Resource
2
+ class Standardizer
3
+ attr_reader :definition
4
+ def initialize(*definition)
5
+ @definition = definition.flatten
6
+ end
7
+
8
+ def template
9
+ camelize(standarize(@definition))
10
+ end
11
+
12
+ def camelize(attributes)
13
+ Jets::Camelizer.transform(attributes)
14
+ end
15
+
16
+ def standarize(definition)
17
+ first, second, third, _ = definition
18
+ if definition.size == 1 && first.is_a?(Hash) # long form
19
+ first # pass through
20
+ elsif definition.size == 2 && second.is_a?(Hash) # medium form
21
+ logical_id, attributes = first, second
22
+ attributes.delete(:properties) if attributes[:properties].nil? || attributes[:properties].empty?
23
+ { logical_id => attributes }
24
+ elsif definition.size == 2 && second.is_a?(String) # short form
25
+ logical_id, type = first, second
26
+ { logical_id => {
27
+ type: type
28
+ }}
29
+ elsif definition.size == 3 && (second.is_a?(String) || second.is_a?(NilClass))# short form
30
+ logical_id, type, properties = first, second, third
31
+ template = { logical_id => {
32
+ type: type
33
+ }}
34
+ attributes = template.values.first
35
+ attributes[:properties] = properties unless properties.empty?
36
+ template
37
+ else # Dont understand this form
38
+ raise "Invalid form provided. definition #{definition.inspect}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -30,7 +30,15 @@ module Jets
30
30
  end
31
31
 
32
32
  def api_mode?
33
- Jets.config.api_generator
33
+ if Jets.config.api_generator
34
+ puts <<~EOL.colorize(:yellow)
35
+ DEPRECATED: Jets.config.api_generator
36
+ Instead, please update your config/application.rb to use:
37
+ Jets.config.api_mode
38
+ EOL
39
+ end
40
+ api_mode = Jets.config.api_mode || Jets.config.api_generator
41
+ api_mode
34
42
  end
35
43
 
36
44
  def create_route(options)
@@ -3,12 +3,22 @@
3
3
  # So the Jets::Rule::Dsl overrides some of the Jets::Lambda::Functions behavior.
4
4
  #
5
5
  # Implements:
6
- # default_associated_resource: must return @resources
6
+ #
7
+ # default_associated_resource_definition
8
+ #
7
9
  module Jets::Rule::Dsl
8
10
  extend ActiveSupport::Concern
9
11
 
10
12
  included do
11
13
  class << self
14
+ def rule_namespace(value=nil)
15
+ if value.nil?
16
+ @rule_namespace # getter
17
+ else
18
+ @rule_namespace = value # # setter
19
+ end
20
+ end
21
+
12
22
  # Allows for different types of values. Examples:
13
23
  #
14
24
  # String: scope "AWS::EC2::SecurityGroup"
@@ -23,102 +33,65 @@ module Jets::Rule::Dsl
23
33
  else # default to hash
24
34
  value
25
35
  end
26
- update_properties(scope: scope)
36
+ associated_properties(scope: scope)
27
37
  end
28
38
 
29
- def config_rule_name(value)
30
- update_properties(config_rule_name: value)
31
- end
32
-
33
- def description(value)
34
- update_properties(description: value)
35
- end
39
+ # Convenience method that set properties. List based on https://amzn.to/2oSph1P
40
+ # Not all properties are included because some properties are not meant to be set
41
+ # directly. For example, function_name is a calculated setting by Jets.
42
+ ASSOCIATED_PROPERTIES = %W[
43
+ config_rule_name
44
+ description
45
+ input_parameters
46
+ maximum_execution_frequency
47
+ ]
48
+ define_associated_properties(ASSOCIATED_PROPERTIES)
36
49
  alias_method :desc, :description
37
50
 
38
- def input_parameters(value)
39
- update_properties(input_parameters: value)
51
+ def default_associated_resource_definition(meth)
52
+ config_rule_definition(meth)
40
53
  end
41
54
 
42
- def maximum_execution_frequency(value)
43
- update_properties(maximum_execution_frequency: value)
55
+ def config_rule_definition(meth)
56
+ resource = Jets::Resource::Config::ConfigRule.new(self, meth, associated_properties)
57
+ resource.definition # returns a definition to be added by associated_resources
44
58
  end
45
59
 
46
- def source(value)
47
- update_properties(source: value)
48
- end
49
-
50
- def default_associated_resource
51
- config_rule
52
- end
53
-
54
- def config_rule(props={})
55
- default_props = {
56
- config_rule_name: "{config_rule_name}",
57
- source: {
58
- owner: "CUSTOM_LAMBDA",
59
- source_identifier: "{namespace}LambdaFunction.Arn",
60
- source_details: [
61
- {
62
- event_source: "aws.config",
63
- message_type: "ConfigurationItemChangeNotification"
64
- },
65
- {
66
- event_source: "aws.config",
67
- message_type: "OversizedConfigurationItemChangeNotification"
68
- }
69
- ]
70
- }
71
- }
72
- properties = default_props.deep_merge(props)
73
-
74
- resource("{namespace}ConfigRule" => {
75
- type: "AWS::Config::ConfigRule",
76
- properties: properties
77
- })
78
- @resources # must return @resoures for update_properties
79
- end
80
-
81
- def managed_rule(name, props={})
60
+ def managed_rule(name)
82
61
  name = name.to_s
62
+ managed_rule = Jets::Resource::Config::ManagedRule.new(self, name, associated_properties)
63
+ resource(managed_rule.definition) # Sets @associated_resources
83
64
 
84
- # Similar logic in Replacer::ConfigRule#config_rule_name
85
- name_without_rule = self.name.underscore.gsub(/_rule$/,'')
86
- config_rule_name = "#{name_without_rule}_#{name}".dasherize
87
- source_identifier = name.upcase
88
-
89
- default_props = {
90
- config_rule_name: config_rule_name,
91
- source: {
92
- owner: "AWS",
93
- source_identifier: source_identifier,
94
- }
95
- }
96
- properties = default_props.deep_merge(props)
97
- # The key is to use update_properties to update the current resource and maintain
98
- # the added properties from the convenience methods like scope and description.
99
- # At the same time, we do not register the task to all_tasks to avoid creating a Lambda function.
65
+ # The key to not register the task to all_tasks to avoid creating a Lambda function.
100
66
  # Instead we store it in all_managed_rules.
101
- update_properties(properties)
102
- definition = @resources.first
103
-
104
- register_managed_rule(name, definition)
67
+ register_managed_rule(name, managed_rule.definition)
105
68
  end
106
69
 
107
70
  # Creates a task but registers it to all_managed_rules instead of all_tasks
108
71
  # because we do not want Lambda functions to be created.
109
72
  def register_managed_rule(name, definition)
110
- # A task object is needed to build {namespace} for later replacing.
111
- task = Jets::Lambda::Task.new(self.name, name, resources: @resources)
112
73
 
113
- # TODO: figure out better way for specific replacements for different classes
114
- name_without_rule = self.name.underscore.gsub(/_rule$/,'')
115
- config_rule_name = "#{name_without_rule}_#{name}".dasherize
116
- replacements = task.replacements.merge(config_rule_name: config_rule_name)
117
-
118
- all_managed_rules[name] = { definition: definition, replacements: replacements }
74
+ # Mimic task to grab base_replacements, namely namespace.
75
+ # Do not actually use the task to create a Lambda function for managed rules.
76
+ # Only using the task for base_replacements.
77
+ resources = [definition]
78
+ meth = name
79
+ task = Jets::Lambda::Task.new(self.name, meth,
80
+ resources: resources,
81
+ replacements: replacements(meth))
82
+ all_managed_rules[name] = { definition: definition, replacements: task.replacements }
119
83
  clear_properties
120
84
  end
121
85
 
86
+ # Override lambda/dsl.rb to add config_rule_name also
87
+ def replacements(meth)
88
+ name_without_rule = self.name.underscore.gsub(/_rule$/,'')
89
+ config_rule_name = "#{name_without_rule}_#{meth}".dasherize
90
+ {
91
+ config_rule_name: config_rule_name
92
+ }
93
+ end
94
+
122
95
  # AWS managed rules are not actual Lambda functions and require their own storage.
123
96
  def all_managed_rules
124
97
  @all_managed_rules ||= ActiveSupport::OrderedHash.new
@@ -128,7 +101,7 @@ module Jets::Rule::Dsl
128
101
  all_managed_rules.values
129
102
  end
130
103
 
131
- # Override Lambda::Dsl.build? to account of possible managed_rules
104
+ # Override Lambda::Dsl.build? to account for possible managed_rules
132
105
  def build?
133
106
  !tasks.empty? || !managed_rules.empty?
134
107
  end
@@ -0,0 +1,105 @@
1
+ require 'active_support/concern'
2
+
3
+ module Jets
4
+ class Stack
5
+ autoload :Definition, 'jets/stack/definition' # Registration and definitions
6
+ autoload :Main, 'jets/stack/main'
7
+ autoload :Parameter, 'jets/stack/parameter'
8
+ autoload :Output, 'jets/stack/output'
9
+ autoload :Resource, 'jets/stack/resource'
10
+ autoload :Builder, 'jets/stack/builder'
11
+ autoload :Function, 'jets/stack/function'
12
+
13
+ include Main::Dsl
14
+ include Parameter::Dsl
15
+ include Output::Dsl
16
+ include Resource::Dsl
17
+
18
+ class << self
19
+ extend Memoist
20
+
21
+ # Track all command subclasses.
22
+ def subclasses
23
+ @subclasses ||= []
24
+ end
25
+
26
+ def inherited(base)
27
+ super
28
+ self.subclasses << base if base.name
29
+ end
30
+
31
+ # Build it to figure out if we need to build the stack for the SharedBuilder
32
+ def build?
33
+ empty = template == {"Parameters"=>{"IamRole"=>{"Type"=>"String"}, "S3Bucket"=>{"Type"=>"String"}}}
34
+ !empty
35
+ end
36
+
37
+ def functions
38
+ stack = new
39
+ # All the & because resources might be nil
40
+ templates = stack.resources&.map(&:template)&.select do |t|
41
+ attributes = t.values.first
42
+ attributes['Type'] == 'AWS::Lambda::Function'
43
+ end
44
+ templates ||= []
45
+ templates.map { |t| Function.new(t) }
46
+ end
47
+
48
+ def template
49
+ # Pretty funny looking, creating an instance of stack to be passed to the Builder.
50
+ # Another way of looking at it:
51
+ #
52
+ # stack = new # MyStack.new
53
+ # builder = Jets::Stack::Builder.new(stack)
54
+ #
55
+ builder = Jets::Stack::Builder.new(new)
56
+ builder.template
57
+ end
58
+ memoize :template
59
+
60
+ def has_resources?
61
+ !subclasses.empty?
62
+ end
63
+
64
+ # Usage:
65
+ #
66
+ # Jets::Stack.has_resources?
67
+ #
68
+ def has_resources?
69
+ # need to eager load the app/shared resources in order to check if shared resources have been registered
70
+ eager_load_shared_resources!
71
+ !!subclasses.detect do |subclass|
72
+ subclass.build?
73
+ end
74
+ end
75
+
76
+ def eager_load_shared_resources!
77
+ ActiveSupport::Dependencies.autoload_paths += ["#{Jets.root}app/shared/resources"]
78
+ Dir.glob("#{Jets.root}app/shared/resources/*.rb").select do |path|
79
+ next if !File.file?(path) or path =~ %r{/javascript/} or path =~ %r{/views/}
80
+
81
+ class_name = path
82
+ .sub(/\.rb$/,'') # remove .rb
83
+ .sub(Jets.root.to_s,'') # remove ./
84
+ .sub(%r{app/shared/resources/},'') # remove app/shared/resources/
85
+ .classify
86
+ class_name.constantize # use constantize instead of require so dont have to worry about order.
87
+ end
88
+ end
89
+
90
+ def lookup(logical_id)
91
+ looker.output(logical_id)
92
+ end
93
+
94
+ def looker
95
+ Jets::Stack::Output::Lookup.new(self)
96
+ end
97
+ memoize :looker
98
+
99
+ def output_keys
100
+ outputs = new.outputs || []
101
+ outputs.map(&:template).map {|o| o.keys.first}
102
+ end
103
+ end
104
+ end
105
+ end