jets 2.3.19 → 3.0.4

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +0 -3
  3. data/.python-version +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +45 -2
  6. data/LICENSE.txt +1 -1
  7. data/README.md +9 -3
  8. data/backers.md +1 -0
  9. data/jets.gemspec +12 -11
  10. data/lib/jets.rb +9 -13
  11. data/lib/jets/application.rb +9 -2
  12. data/lib/jets/application/defaults.rb +17 -15
  13. data/lib/jets/autoloaders.rb +15 -1
  14. data/lib/jets/booter.rb +3 -3
  15. data/lib/jets/builders/code_builder.rb +16 -15
  16. data/lib/jets/builders/gem_replacer.rb +3 -4
  17. data/lib/jets/builders/lambda_layer.rb +4 -5
  18. data/lib/jets/builders/md5_zip.rb +1 -1
  19. data/lib/jets/builders/ruby_packager.rb +16 -18
  20. data/lib/jets/builders/tidy.rb +1 -2
  21. data/lib/jets/bundle.rb +6 -0
  22. data/lib/jets/cfn/builders/api_gateway_builder.rb +61 -7
  23. data/lib/jets/cfn/ship.rb +2 -1
  24. data/lib/jets/cli.rb +6 -1
  25. data/lib/jets/commands/base.rb +1 -1
  26. data/lib/jets/commands/call.rb +14 -1
  27. data/lib/jets/commands/clean.rb +1 -1
  28. data/lib/jets/commands/clean/base.rb +1 -1
  29. data/lib/jets/commands/configure.rb +51 -0
  30. data/lib/jets/commands/delete.rb +2 -2
  31. data/lib/jets/commands/gems.rb +2 -10
  32. data/lib/jets/commands/help/call.md +8 -0
  33. data/lib/jets/commands/help/gems/check.md +3 -5
  34. data/lib/jets/commands/main.rb +9 -1
  35. data/lib/jets/commands/new.rb +9 -1
  36. data/lib/jets/commands/sequence.rb +6 -0
  37. data/lib/jets/commands/templates/skeleton/Gemfile.tt +1 -1
  38. data/lib/jets/commands/templates/skeleton/config/application.rb.tt +1 -1
  39. data/lib/jets/commands/templates/skeleton/public/index.html.tt +1 -1
  40. data/lib/jets/commands/url.rb +1 -0
  41. data/lib/jets/controller/base.rb +14 -4
  42. data/lib/jets/controller/middleware/main.rb +2 -1
  43. data/lib/jets/controller/params.rb +26 -4
  44. data/lib/jets/controller/rack/env.rb +18 -1
  45. data/lib/jets/controller/rendering.rb +5 -2
  46. data/lib/jets/controller/rendering/rack_renderer.rb +7 -1
  47. data/lib/jets/core.rb +12 -4
  48. data/lib/jets/dotenv/ssm.rb +18 -4
  49. data/lib/jets/generator.rb +2 -3
  50. data/lib/jets/internal/app/controllers/jets/public_controller.rb +2 -2
  51. data/lib/jets/internal/app/functions/jets/base_path.rb +10 -149
  52. data/lib/jets/internal/app/functions/jets/base_path_mapping.rb +81 -0
  53. data/lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb +14 -24
  54. data/lib/jets/overrides/rails/migration_checker.rb +6 -2
  55. data/lib/jets/resource/api_gateway/base_path/function.rb +6 -1
  56. data/lib/jets/resource/api_gateway/deployment.rb +2 -0
  57. data/lib/jets/resource/api_gateway/rest_api/logical_id.rb +34 -0
  58. data/lib/jets/resource/api_gateway/rest_api/logical_id/message.rb +49 -0
  59. data/lib/jets/resource/child_stack/api_deployment.rb +2 -0
  60. data/lib/jets/resource/lambda/function.rb +1 -1
  61. data/lib/jets/router/dsl.rb +7 -1
  62. data/lib/jets/router/method_creator/code.rb +1 -1
  63. data/lib/jets/router/scope.rb +7 -3
  64. data/lib/jets/spec_helpers/controllers.rb +11 -3
  65. data/lib/jets/spec_helpers/controllers/request.rb +23 -10
  66. data/lib/jets/stack/main/dsl/lambda.rb +1 -1
  67. data/lib/jets/turbo.rb +1 -0
  68. data/lib/jets/version.rb +1 -1
  69. metadata +51 -58
  70. data/vendor/cfn-status/CHANGELOG.md +0 -14
  71. data/vendor/cfn-status/Gemfile +0 -4
  72. data/vendor/cfn-status/LICENSE.txt +0 -21
  73. data/vendor/cfn-status/README.md +0 -56
  74. data/vendor/cfn-status/Rakefile +0 -6
  75. data/vendor/cfn-status/bin/console +0 -14
  76. data/vendor/cfn-status/bin/setup +0 -8
  77. data/vendor/cfn-status/cfn-status.gemspec +0 -30
  78. data/vendor/cfn-status/lib/cfn-status.rb +0 -1
  79. data/vendor/cfn-status/lib/cfn_status.rb +0 -245
  80. data/vendor/cfn-status/lib/cfn_status/aws_service.rb +0 -51
  81. data/vendor/cfn-status/lib/cfn_status/version.rb +0 -3
  82. data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-1.json +0 -1103
  83. data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-2.json +0 -1104
  84. data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-3.json +0 -1103
  85. data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-1.json +0 -1103
  86. data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-2.json +0 -1104
  87. data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-3.json +0 -1103
  88. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-complete.json +0 -1080
  89. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-in-progress.json +0 -1080
  90. data/vendor/cfn-status/spec/fixtures/cfn/stack-events-update-rollback-complete.json +0 -1086
  91. data/vendor/cfn-status/spec/lib/cfn_status_spec.rb +0 -153
  92. data/vendor/cfn-status/spec/spec_helper.rb +0 -14
@@ -1,5 +1,5 @@
1
1
  require "rack/mime"
2
- require "mimemagic"
2
+ require "mini_mime"
3
3
 
4
4
  class Jets::PublicController < Jets::Controller::Base
5
5
  layout false
@@ -12,7 +12,7 @@ class Jets::PublicController < Jets::Controller::Base
12
12
 
13
13
  if File.exist?(catchall_path)
14
14
  content_type = Rack::Mime.mime_type(File.extname(catchall_path))
15
- binary = !MimeMagic.by_path(catchall_path).text?
15
+ binary = !MiniMime.lookup_by_filename(catchall_path).content_type.include?("text")
16
16
 
17
17
  # For binary support to work, the client also has to send the right Accept header.
18
18
  # And the media type has been to added to api gateway.
@@ -1,157 +1,18 @@
1
- require 'aws-sdk-apigateway'
2
- require 'aws-sdk-cloudformation'
1
+ require 'bundler/setup'
2
+ require 'cfn_response'
3
+ require 'jets/internal/app/functions/jets/base_path_mapping'
3
4
 
4
5
  STAGE_NAME = "<%= @stage_name %>"
5
6
 
6
7
  def lambda_handler(event:, context:)
7
- puts("event['RequestType'] #{event['RequestType']}")
8
- puts("event: #{JSON.dump(event)}")
9
- puts("context: #{JSON.dump(context)}")
10
- puts("context.log_stream_name #{context.log_stream_name.inspect}")
11
-
12
- mimic = event['ResourceProperties']['Mimic']
13
- physical_id = event['ResourceProperties']['PhysicalId'] || "PhysicalId"
14
-
15
- puts "mimic: #{mimic}"
16
- puts "physical_id: #{physical_id}"
17
-
18
- if event['RequestType'] == 'Delete'
19
- if mimic == 'FAILED'
20
- send_response(event, context, "FAILED")
21
- else
22
- mapping = BasePathMapping.new(event)
8
+ cfn = CfnResponse.new(event, context)
9
+ cfn.response do
10
+ mapping = BasePathMapping.new(event, STAGE_NAME)
11
+ case event['RequestType']
12
+ when "Create", "Update"
13
+ mapping.update
14
+ when "Delete"
23
15
  mapping.delete(true) if mapping.should_delete?
24
- send_response(event, context, "SUCCESS")
25
16
  end
26
- return # early return
27
- end
28
-
29
- mapping = BasePathMapping.new(event)
30
- mapping.update
31
-
32
- response_status = mimic == "FAILED" ? "FAILED" : "SUCCESS"
33
- response_data = { "Hello" => "World" }
34
-
35
- send_response(event, context, response_status, response_data, physical_id)
36
-
37
- # We rescue all exceptions and send an message to CloudFormation so we dont have to
38
- # wait for over an hour for the stack operation to timeout and rollback.
39
- rescue Exception => e
40
- puts e.message
41
- puts e.backtrace
42
- sleep 10 # provide delete to make sure that the log gets sent to CloudWatch
43
- send_response(event, context, "FAILED")
44
- end
45
-
46
- def send_response(event, context, response_status, response_data={}, physical_id="PhysicalId")
47
- response_body = JSON.dump(
48
- Status: response_status,
49
- Reason: "See the details in CloudWatch Log Stream: #{context.log_stream_name.inspect}",
50
- PhysicalResourceId: physical_id,
51
- StackId: event['StackId'],
52
- RequestId: event['RequestId'],
53
- LogicalResourceId: event['LogicalResourceId'],
54
- Data: response_data
55
- )
56
-
57
- puts "RESPONSE BODY:\n"
58
- puts response_body
59
-
60
- url = event['ResponseURL']
61
- uri = URI(url)
62
- http = Net::HTTP.new(uri.host, uri.port)
63
- http.open_timeout = http.read_timeout = 30
64
- http.use_ssl = true if uri.scheme == 'https'
65
-
66
-
67
- # must used url to include the AWSAccessKeyId and Signature
68
- req = Net::HTTP::Put.new(url) # url includes query string and uri.path does not, must used url t
69
- req.body = response_body
70
- req.content_length = response_body.bytesize
71
-
72
- # set headers
73
- req['content-type'] = ''
74
- req['content-length'] = response_body.bytesize
75
-
76
- res = http.request(req)
77
- puts "status code: #{res.code}"
78
- puts "headers: #{res.each_header.to_h.inspect}"
79
- puts "body: #{res.body}"
80
- end
81
-
82
-
83
- class BasePathMapping
84
- def initialize(event)
85
- @event = event
86
- @rest_api_id = get_rest_api_id
87
- @domain_name = get_domain_name
88
- @base_path = ''
89
- end
90
-
91
- def update
92
- # Cannot use update_base_path_mapping to update the base_mapping because it doesnt
93
- # allow us to change the rest_api_id. So we delete and create.
94
- delete(true)
95
- create
96
- end
97
-
98
- # Dont delete the newly created base path mapping unless this is an operation
99
- # where we're fully deleting the stack
100
- def should_delete?
101
- deleting_parent?
102
- end
103
-
104
- def delete(fail_silently=false)
105
- apigateway.delete_base_path_mapping(
106
- domain_name: @domain_name, # required
107
- base_path: '(none)',
108
- )
109
- # https://github.com/tongueroo/jets/issues/255
110
- # Used to return: Aws::APIGateway::Errors::NotFoundException
111
- # Now returns: Aws::APIGateway::Errors::InternalFailure
112
- # So we'll use a more generic error
113
- rescue Aws::APIGateway::Errors::ServiceError => e
114
- raise(e) unless fail_silently
115
- end
116
-
117
- def create
118
- apigateway.create_base_path_mapping(
119
- domain_name: @domain_name, # required
120
- base_path: @base_path,
121
- rest_api_id: @rest_api_id, # required
122
- stage: STAGE_NAME,
123
- )
124
- end
125
-
126
- def get_domain_name
127
- param = deployment_stack[:parameters].find { |p| p.parameter_key == 'DomainName' }
128
- param.parameter_value
129
- end
130
-
131
- def deployment_stack
132
- @deployment_stack ||= cfn.describe_stacks(stack_name: @event['StackId']).stacks.first
133
- end
134
-
135
- def get_rest_api_id
136
- param = deployment_stack[:parameters].find { |p| p.parameter_key == 'RestApi' }
137
- param.parameter_value
138
- end
139
-
140
- def deleting_parent?
141
- stack = cfn.describe_stacks(stack_name: parent_stack_name).stacks.first
142
- stack.stack_status == 'DELETE_IN_PROGRESS'
143
- end
144
-
145
- def parent_stack_name
146
- deployment_stack[:root_id]
147
- end
148
-
149
- private
150
- def apigateway
151
- @apigateway ||= Aws::APIGateway::Client.new
152
- end
153
-
154
- def cfn
155
- @cfn ||= Aws::CloudFormation::Client.new
156
17
  end
157
18
  end
@@ -0,0 +1,81 @@
1
+ require 'aws-sdk-apigateway'
2
+ require 'aws-sdk-cloudformation'
3
+
4
+ class BasePathMapping
5
+ def initialize(event, stage_name)
6
+ @event, @stage_name = event, stage_name
7
+ end
8
+
9
+ # Cannot use update_base_path_mapping to update the base_mapping because it doesnt
10
+ # allow us to change the rest_api_id. So we delete and create.
11
+ def update
12
+ delete(true)
13
+ create
14
+ end
15
+
16
+ # Dont delete the newly created base path mapping unless this is an operation
17
+ # where we're fully deleting the stack
18
+ def should_delete?
19
+ deleting_parent?
20
+ end
21
+
22
+ def delete(fail_silently=false)
23
+ apigateway.delete_base_path_mapping(
24
+ domain_name: domain_name, # required
25
+ base_path: base_path.empty? ? '(none)' : base_path,
26
+ )
27
+ # https://github.com/tongueroo/jets/issues/255
28
+ # Used to return: Aws::APIGateway::Errors::NotFoundException
29
+ # Now returns: Aws::APIGateway::Errors::InternalFailure
30
+ # So we'll use a more generic error
31
+ rescue Aws::APIGateway::Errors::ServiceError => e
32
+ raise(e) unless fail_silently
33
+ end
34
+
35
+ def create
36
+ apigateway.create_base_path_mapping(
37
+ domain_name: domain_name, # required
38
+ base_path: base_path,
39
+ rest_api_id: rest_api_id, # required
40
+ stage: @stage_name,
41
+ )
42
+ end
43
+
44
+ def deployment_stack
45
+ @deployment_stack ||= cfn.describe_stacks(stack_name: @event['StackId']).stacks.first
46
+ end
47
+
48
+ def rest_api_id
49
+ @rest_api_id ||= parameter_value('RestApi')
50
+ end
51
+
52
+ def domain_name
53
+ @domain_name ||= parameter_value('DomainName')
54
+ end
55
+
56
+ def base_path
57
+ @base_path ||= parameter_value('BasePath') || ''
58
+ end
59
+
60
+ def parameter_value(parameter_key)
61
+ param = deployment_stack[:parameters].find { |p| p.parameter_key == parameter_key }
62
+ param&.parameter_value # possible for this to be nil when removing the config: IE: config.domain.name = nil
63
+ end
64
+
65
+ def deleting_parent?
66
+ stack = cfn.describe_stacks(stack_name: parent_stack_name).stacks.first
67
+ stack.stack_status == 'DELETE_IN_PROGRESS'
68
+ end
69
+
70
+ def parent_stack_name
71
+ deployment_stack[:root_id]
72
+ end
73
+
74
+ def apigateway
75
+ @apigateway ||= Aws::APIGateway::Client.new
76
+ end
77
+
78
+ def cfn
79
+ @cfn ||= Aws::CloudFormation::Client.new
80
+ end
81
+ end
@@ -1,33 +1,23 @@
1
- require "active_support/all"
2
- require "aws-sdk-s3"
3
- require "cfnresponse"
4
- include Cfnresponse
1
+ require 'bundler/setup'
2
+ require 'active_support/core_ext/hash'
3
+ require 'cfn_response'
5
4
 
6
5
  def lambda_handler(event:, context:)
7
- # Print out debugging info immediately just in case
8
- puts "event: #{json_pretty(event)}"
9
- puts "context: #{json_pretty(context)}"
10
-
11
- if %w[Create Update].include?(event['RequestType'])
12
- properties = event["ResourceProperties"].dup
13
- # After deleting ServiceToken, the rest of the values is the bucket configuration properties.
14
- properties.delete("ServiceToken")
15
- configurator = BucketConfigurator.new
16
- configurator.put(properties)
6
+ cfn = CfnResponse.new(event, context)
7
+ cfn.response do
8
+ case event['RequestType']
9
+ when "Create", "Update"
10
+ properties = event["ResourceProperties"].dup
11
+ # After deleting ServiceToken, the rest of the values are the bucket configuration properties.
12
+ properties.delete("ServiceToken")
13
+ configurator = BucketConfigurator.new
14
+ configurator.put(properties)
15
+ end
17
16
  end
18
-
19
- send_response(event, context, "SUCCESS")
20
-
21
- # We rescue all exceptions and send an message to CloudFormation so we dont have to
22
- # wait for over an hour for the stack operation to timeout and rollback.
23
- rescue Exception => e
24
- puts e.message
25
- puts e.backtrace
26
- sleep 10 # a little time for logs to be sent to CloudWatch
27
- send_response(event, context, "FAILED")
28
17
  end
29
18
 
30
19
  ########################################################
20
+ require "aws-sdk-s3"
31
21
 
32
22
  class BucketConfigurator
33
23
  def put(props={})
@@ -1,11 +1,15 @@
1
1
  module ActiveRecord
2
2
  module MigrationChecker
3
3
  def prepare_test_db
4
- current_config = ::ActiveRecord::Base.connection_config
4
+ current_config = ::ActiveRecord::Base.connection_db_config
5
5
  all_configs = ::ActiveRecord::Base.configurations.configs_for(env_name: Jets.env)
6
6
 
7
7
  needs_update = !all_configs.all? do |db_config|
8
- ::ActiveRecord::Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ::ActiveRecord::Base.schema_format, nil, Jets.env, db_config.spec_name)
8
+ ::ActiveRecord::Tasks::DatabaseTasks.schema_up_to_date?(
9
+ db_config.configuration_hash,
10
+ ::ActiveRecord::Base.schema_format,
11
+ nil
12
+ )
9
13
  end
10
14
 
11
15
  if needs_update
@@ -14,15 +14,20 @@ module Jets::Resource::ApiGateway::BasePath
14
14
  },
15
15
  role: "!GetAtt BasePathRole.Arn",
16
16
  handler: handler,
17
- runtime: "ruby2.5",
17
+ runtime: Jets.ruby_runtime,
18
18
  timeout: 60,
19
19
  memory_size: 1536,
20
20
  environment: env_properties[:environment],
21
+ layers: layers,
21
22
  }
22
23
  }
23
24
  }
24
25
  end
25
26
 
27
+ def layers
28
+ ["!Ref GemLayer"]
29
+ end
30
+
26
31
  def function_name
27
32
  method = "jets-base-path"
28
33
  # need to add the deployment timestamp because or else function name collides between deploys
@@ -15,11 +15,13 @@ module Jets::Resource::ApiGateway
15
15
 
16
16
  def parameters
17
17
  p = {
18
+ "GemLayer" => "GemLayer",
18
19
  "IamRole" => "IamRole",
19
20
  "RestApi" => "RestApi",
20
21
  "S3Bucket" => "S3Bucket",
21
22
  }
22
23
  p[:DomainName] = "DomainName" if Jets.custom_domain?
24
+ p[:BasePath] = "BasePath" unless Jets.config.domain.base_path.nil?
23
25
  p
24
26
  end
25
27
 
@@ -8,12 +8,46 @@ class Jets::Resource::ApiGateway::RestApi
8
8
  return default unless stack_exists?(parent_stack_name) && api_gateway_exists?
9
9
 
10
10
  if changed?
11
+ auto_replace_prompt
11
12
  new_id
12
13
  else
13
14
  current
14
15
  end
15
16
  end
16
17
 
18
+ def auto_replace_prompt
19
+ return if ENV['JETS_API_AUTO_REPLACE']
20
+ return unless ARGV[0] == "deploy"
21
+ case Jets.config.api.auto_replace
22
+ when nil
23
+ puts message.routes_changed
24
+ puts message.custom_domain
25
+ print "Would you like to continue the deployment? (y/N) "
26
+ answer = get_answer
27
+ exit 1 unless answer =~ /^y/
28
+ when false
29
+ puts message.routes_changed
30
+ puts message.auto_replace_disabled
31
+ exit 1
32
+ end
33
+ end
34
+
35
+ def message
36
+ Message.new
37
+ end
38
+ memoize :message
39
+
40
+ TIMEOUT_PERIOD = 120
41
+ def get_answer
42
+ Timeout::timeout(TIMEOUT_PERIOD) do
43
+ $stdin.gets
44
+ end
45
+ rescue Timeout::Error => e
46
+ puts "#{e.class}: #{e.message}".color(:red)
47
+ puts "Deployment timeout after #{TIMEOUT_PERIOD}s. Waited too long answer. Exiting."
48
+ exit 1
49
+ end
50
+
17
51
  def changed?
18
52
  change_detection = ChangeDetection.new
19
53
  change_detection.changed?
@@ -0,0 +1,49 @@
1
+ class Jets::Resource::ApiGateway::RestApi::LogicalId
2
+ class Message
3
+ def routes_changed
4
+ <<~EOL
5
+ Routes Change Detection: Jets has detected that a new brand API Gateway is required to be deployed.
6
+ IMPORTANT: This will result in the API Gateway endpoint changing.
7
+ EOL
8
+ end
9
+
10
+ def custom_domain
11
+ api = Jets::Resource::ApiGateway::DomainName.new
12
+ domain_name = api.domain_name
13
+ if domain_name
14
+ <<~EOL
15
+ It looks like you have already set up a custom domain.
16
+ The domain name: #{domain_name}
17
+
18
+ So you should be good to go as the custom domain will be updated with the new API Gateway endpoint.
19
+ To avoid this prompt in the future, you can configure:
20
+
21
+ config/application.rb
22
+
23
+ config.api.auto_replace = true
24
+
25
+ More info: custom domain docs: https://rubyonjets.com/docs/routing/custom-domain/
26
+ EOL
27
+ else
28
+ "Please set up a custom domain https://rubyonjets.com/docs/routing/custom-domain/"
29
+ end
30
+ end
31
+
32
+ def auto_replace_disabled
33
+ <<~EOL
34
+ It looks like `config.api.auto_replace = false`. IE:
35
+
36
+ config/application.rb
37
+
38
+ config.api.auto_replace = false
39
+
40
+ The deploy will not continue. See:
41
+
42
+ * https://rubyonjets.com/docs/app-config/reference/
43
+ * https://rubyonjets.com/docs/routing/custom-domain/
44
+
45
+ EOL
46
+ end
47
+ end
48
+ end
49
+