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
@@ -0,0 +1,38 @@
1
+ class Jets::Stack
2
+ class Builder
3
+ extend Memoist
4
+
5
+ def initialize(stack)
6
+ @stack = stack
7
+ @template = {} # will build this structure up
8
+ end
9
+
10
+ def template
11
+ build_section(:parameters)
12
+ build_section(:resources)
13
+ build_section(:outputs)
14
+ Jets::Camelizer.transform(@template)
15
+ end
16
+ memoize :template
17
+
18
+ def build_section(section)
19
+ elements = build_elements(section)
20
+ @template[section] = elements if elements
21
+ end
22
+
23
+ def build_elements(section)
24
+ # s is a "section element". Examples:
25
+ #
26
+ # Jets::Stack::Parameter
27
+ # Jets::Stack::Resource
28
+ # Jets::Stack::Output
29
+ #
30
+ section_elements = @stack.send(section)
31
+ return unless section_elements
32
+
33
+ section_elements.inject({}) do |template_section, s|
34
+ template_section.merge(s.template)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ # Class that include Definition should implement:
2
+ #
3
+ # template - method should use @definition to build a CloudFormation template section
4
+ #
5
+ class Jets::Stack
6
+ module Definition
7
+ extend ActiveSupport::Concern
8
+
9
+ # Example of usage that leads here:
10
+ #
11
+ # Parameter.new(self, *definition).register
12
+ #
13
+ # Which is defined in parameter/dsl.rb
14
+ #
15
+ # Example subclass: ExampleStack < Jets::Stack
16
+ def initialize(subclass, *definition)
17
+ @subclass = subclass.to_s # important to use to_s, dont want the object as keys in @definitions
18
+ @definition = definition.flatten
19
+ end
20
+
21
+ def register
22
+ self.class.register(@subclass, *@definition)
23
+ end
24
+
25
+ def camelize(attributes)
26
+ Jets::Camelizer.transform(attributes)
27
+ end
28
+
29
+ class_methods do
30
+ def register(subclass, *definition)
31
+ @definitions ||= {}
32
+ @definitions[subclass.to_s] ||= []
33
+ # Create instance of the CloudFormation section class and register it. Examples:
34
+ # Stack::Parameter.new(definition)
35
+ # Stack::Resource.new(definition)
36
+ # Stack::Output.new(definition)
37
+ @definitions[subclass.to_s] << new(subclass, definition)
38
+ end
39
+
40
+ def definitions(subclass)
41
+ @definitions ||= {}
42
+ @definitions[subclass.to_s]
43
+ end
44
+
45
+ def all_definitions
46
+ @definitions
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ class Jets::Stack
2
+ class Function
3
+ extend Memoist
4
+
5
+ attr_reader :template
6
+ def initialize(template)
7
+ @template = template
8
+ end
9
+
10
+ def meth
11
+ attributes = @template.values.first
12
+ handler = attributes['Properties']['Handler']
13
+ handler.split('.').last
14
+ end
15
+
16
+ def lang
17
+ if source_file
18
+ # Detect language from file extension
19
+ ext = File.extname(source_file).sub(/^\./,'').to_sym
20
+ lang_map[ext]
21
+ else
22
+ puts "WARN: Unable to find a source file for function. Looked at: #{search_expression}".colorize(:yellow)
23
+ end
24
+ end
25
+
26
+ def lang_map
27
+ {
28
+ rb: :ruby,
29
+ py: :python,
30
+ js: :node,
31
+ }
32
+ end
33
+
34
+ def source_file
35
+ Dir.glob(search_expression).first
36
+ end
37
+ memoize :source_file
38
+
39
+ def search_expression
40
+ attributes = @template.values.first
41
+ handler = attributes['Properties']['Handler']
42
+ search_expression = handler.split('.')[0..-2].join('.') + '.*'
43
+ search_expression.sub('handlers/shared/', "#{Jets.root}app/shared/")
44
+ end
45
+
46
+ # Relative path
47
+ # app/shared/functions/kevin.py => handlers/shared/functions/kevin.py
48
+ def handler_dest
49
+ return unless source_file
50
+
51
+ dest = source_file.sub(%r{.*/app/}, "handlers/")
52
+ if lang == :ruby
53
+ filename = dest.split('.').first
54
+ filename + '.js' # change extension to .js because ruby uses a node shim
55
+ else
56
+ dest
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ class Jets::Stack
2
+ class Main
3
+ autoload :Dsl, "jets/stack/main/dsl"
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ class Jets::Stack
2
+ class Main
3
+ module Dsl
4
+ extend ActiveSupport::Concern
5
+ autoload :Base, 'jets/stack/main/extensions/base'
6
+ autoload :Cloudwatch, 'jets/stack/main/extensions/cloudwatch'
7
+ autoload :Lambda, 'jets/stack/main/extensions/lambda'
8
+ autoload :Sns, 'jets/stack/main/extensions/sns'
9
+ autoload :Sqs, 'jets/stack/main/extensions/sqs'
10
+
11
+ class_methods do
12
+ include Base
13
+ include Cloudwatch
14
+ include Lambda
15
+ include Sns
16
+ include Sqs
17
+ end
18
+
19
+ def self.included(base)
20
+ base_path = "#{Jets.root}/app/shared/extensions"
21
+ ActiveSupport::Dependencies.autoload_paths += [base_path]
22
+
23
+ Dir.glob("#{base_path}/**/*.rb").each do |path|
24
+ next unless File.file?(path)
25
+
26
+ class_name = path.sub("#{base_path}/", '').sub(/\.rb/,'').classify
27
+ klass = class_name.constantize # autoload
28
+ base.extend(klass)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Base
3
+ def ref(value)
4
+ "!Ref #{value.to_s.camelize}"
5
+ end
6
+
7
+ def logical_id(value)
8
+ value.to_s.camelize
9
+ end
10
+
11
+ def depends_on(*stacks)
12
+ if stacks == []
13
+ @depends_on
14
+ else
15
+ @depends_on ||= []
16
+ @depends_on += stacks
17
+ end
18
+ end
19
+
20
+ # Due to `if Jets::Stack.has_resources?` check early on in the bootstraping process
21
+ # The code has not been built at that point. So we use a placeholder and will replace
22
+ # the placeholder as part of the cfn template build process after the code has been built
23
+ # and the code_s3_key with md5 is available.
24
+ def code_s3_key
25
+ "code_s3_key_placeholder"
26
+ end
27
+
28
+ # resource(:hello,
29
+ # function_name: "hello",
30
+ # code: {
31
+ # s3_bucket: "!Ref S3Bucket",
32
+ # s3_key: code_s3_key
33
+ # },
34
+ # description: "Hello world",
35
+ # handler: handler_function("hello.lambda_handler"),
36
+ # memory_size: 128,
37
+ # role: "!Ref IamRole",
38
+ # runtime: "python3.6",
39
+ # timeout: 20,
40
+ # )
41
+ def handler(name)
42
+ "handlers/shared/functions/#{name}" # generated handler
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Cloudwatch
3
+ def cloudwatch_alarm(id, hash={})
4
+ if hash.key?(:depends_on)
5
+ attributes = hash # leave structure alone and add type only
6
+ attributes[:type] = "AWS::CloudWatch::Alarm"
7
+ else
8
+ # the attributes are properties
9
+ properties = hash
10
+ attributes = {
11
+ type: "AWS::CloudWatch::Alarm",
12
+ properties: properties,
13
+ }
14
+ end
15
+ resource(id, attributes)
16
+ output(id)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Lambda
3
+ # Example:
4
+ #
5
+ # function(:hello,
6
+ # handler: handler("hello.lambda_hander"),
7
+ # runtime: "python3.6"
8
+ # )
9
+ #
10
+ # Defaults to ruby. So:
11
+ #
12
+ # function(:hello)
13
+ #
14
+ # is the same as:
15
+ #
16
+ # function(:hello,
17
+ # handler: handler("hello.hande"),
18
+ # runtime: :ruby
19
+ # )
20
+ #
21
+ def function(id, props={})
22
+ # Required: code, handler, role, runtime Docs: https://amzn.to/2pdot7S
23
+ meth = id.to_s.underscore
24
+ defaults = {
25
+ function_name: "#{Jets.config.project_namespace}-#{id.to_s.underscore}",
26
+ code: {
27
+ s3_bucket: "!Ref S3Bucket",
28
+ s3_key: code_s3_key
29
+ },
30
+ role: "!Ref IamRole",
31
+ handler: "#{meth}.handle", # default ruby convention
32
+ runtime: :ruby,
33
+ timeout: Jets.config.function.timeout,
34
+ memory_size: Jets.config.function.memory_size,
35
+ }
36
+ props = defaults.merge(props)
37
+ props[:runtime] = "nodejs8.10" if props[:runtime].to_s == "ruby"
38
+ props[:handler] = handler(props[:handler])
39
+
40
+ resource(id, "AWS::Lambda::Function", props)
41
+ end
42
+ alias_method :ruby_function, :function
43
+ alias_method :lambda_function, :function
44
+
45
+ def python_function(id, props={})
46
+ meth = id.to_s.underscore
47
+ props[:handler] ||= "#{meth}.lambda_handler" # default python convention
48
+ props[:runtime] = "python3.6"
49
+ function(id, props)
50
+ end
51
+
52
+ def node_function(id, props={})
53
+ meth = id.to_s.underscore
54
+ props[:handler] ||= "#{meth}.handler" # default python convention
55
+ props[:runtime] = "node8.10"
56
+ function(id, props)
57
+ end
58
+
59
+ # Usage:
60
+ #
61
+ # permission(:my_permission, principal: "events.amazonaws.com")
62
+ #
63
+ def permission(id, props={})
64
+ defaults = { action: "lambda:InvokeFunction" }
65
+ props = defaults.merge(props)
66
+ resource(id, "AWS::Lambda::Permission", props)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Sns
3
+ def sns_topic(id, props={})
4
+ resource(id, "AWS::SNS::Topic", props)
5
+ output(id)
6
+ end
7
+
8
+ def sns_subscription(id, props={})
9
+ resource(id, "AWS::SNS::Subscription", props)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Jets::Stack::Main::Dsl
2
+ module Sqs
3
+ def sqs_queue(id, props={})
4
+ resource(id, "AWS::SNS::Topic", props)
5
+ output(id)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ # Implements:
2
+ #
3
+ # template - uses @definition to build a CloudFormation template section
4
+ #
5
+ class Jets::Stack
6
+ class Output
7
+ autoload :Dsl, "jets/stack/output/dsl"
8
+ autoload :Lookup, "jets/stack/output/lookup"
9
+
10
+ include Definition
11
+
12
+ def template
13
+ camelize(standarize(@definition))
14
+ end
15
+
16
+ # Value is the only required property: https://amzn.to/2xbhmk3
17
+ def standarize(definition)
18
+ first, second, _ = definition
19
+ if definition.size == 1 && first.is_a?(Hash) # long form
20
+ first # pass through
21
+ elsif definition.size == 2 && second.is_a?(Hash) # medium form
22
+ logical_id, properties = first, second
23
+ { logical_id => properties }
24
+ elsif definition.size == 2 && second.is_a?(String) # short form
25
+ logical_id = first
26
+ properties = second.is_a?(String) ? { value: second } : {}
27
+ { logical_id => properties }
28
+ elsif definition.size == 1
29
+ logical_id = first.to_s
30
+ properties = {value: "!Ref #{logical_id.camelize}"}
31
+ { logical_id => properties }
32
+ else # I dont know what form
33
+ raise "Invalid form provided. definition #{definition.inspect}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,19 @@
1
+ class Jets::Stack
2
+ class Output
3
+ module Dsl
4
+ extend ActiveSupport::Concern
5
+
6
+ def outputs
7
+ Output.definitions(self.class)
8
+ end
9
+
10
+ class_methods do
11
+ def output(*definition)
12
+ # self is subclass is the stack that inherits from Jets::Stack
13
+ # IE: ExampleStack < Jets::Stack
14
+ Output.new(self, *definition).register
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ class Jets::Stack::Output
2
+ class Lookup
3
+ include Jets::AwsServices
4
+
5
+ def initialize(stack_subclass)
6
+ @stack_subclass = stack_subclass
7
+ end
8
+
9
+ def output(logical_id)
10
+ child_stack_id = @stack_subclass.to_s.classify
11
+
12
+ stack_arn = shared_stack_arn(child_stack_id)
13
+ resp = cfn.describe_stacks(stack_name: stack_arn)
14
+ child = resp.stacks.first
15
+ return unless child
16
+
17
+ output_value(child, logical_id)
18
+ end
19
+
20
+ # Shared child stack arn
21
+ def shared_stack_arn(logical_id)
22
+ parent_stack = Jets.config.project_namespace
23
+ resp = cfn.describe_stacks(stack_name: parent_stack)
24
+ parent = resp.stacks.first
25
+ output_value(parent, logical_id)
26
+ end
27
+
28
+ def output_value(stack, key)
29
+ key = key.to_s.camelize
30
+ output = stack.outputs.find do |o|
31
+ o.output_key == key
32
+ end
33
+ output&.output_value
34
+ end
35
+ end
36
+ end