jets 3.2.1 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fedba281ee9d6ac31a44184b49bbc8fe15d309a3f4d58142cfa25c4ddebc153
4
- data.tar.gz: e73ee950202ea1786c881b43abc95c03bc4c3e7ef44b04265997eca2f4b9211b
3
+ metadata.gz: 12689dd61b554f0b5a049ee578cf14566593788a3a2ebb7353ea26a5f7bd803c
4
+ data.tar.gz: c54ebb36356ac71a8e9d211eb9187e761eba9aca2c650abd50e55ffdbf14f6d3
5
5
  SHA512:
6
- metadata.gz: 57070368542b8a767fbf8553f0c627b36432e691ae07f1f41488a0d8af830bffd1ed84a71633fe971df6b45bcf79ff8862f2d4513d22e4a1981b633f36cbb8bd
7
- data.tar.gz: 32ba83eb4d4e87a8c92b5d7ec8d573ae2b36ff558fedbae7bb33f1ebe5dbcb36baf2f64b89b2a05d2e56e52bcd2145964a0770f7a56efdb48d1a925498c7f177
6
+ metadata.gz: 9834c8acaa032e002820e7ba7855f62f318160942287e2c1d23b31f4746d62cbb92253c97097bc94ca6d463044b95d7ef97f5030c6c8105682155ec6d568941d
7
+ data.tar.gz: 707f58aae8c4b8730db0a6e160830eea8a45fc840300a1f1e2a79ffb656118774446e24f1b91722e974bd0f5126a1b755c68eaea6147a8403d2aad76cfc4e697
data/CHANGELOG.md CHANGED
@@ -3,6 +3,9 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## [3.2.2] - 2023-05-19
7
+ - [#640](https://github.com/boltops-tools/jets/pull/640) Base path mapping CloudFormation custom resource and lambda function hardening
8
+
6
9
  ## [3.2.1] - 2023-05-03
7
10
  - [#637](https://github.com/boltops-tools/jets/pull/637) Readme: Replace "splat out" with "had" a baby
8
11
  - [#638](https://github.com/boltops-tools/jets/pull/638) feature: update an array of reserved variables in aws lambda
@@ -1,13 +1,49 @@
1
- require 'bundler/setup'
1
+ begin
2
+ require 'bundler/setup'
3
+ # When require bundler/setup fails, AWS Lambda won't be able to load base_path_mapping
4
+ # So we'll require base_path_mapping within begin/rescue block so that it does not also
5
+ # fail the entire lambda function.
6
+ require 'jets/internal/app/functions/jets/base_path_mapping'
7
+ rescue Exception => e
8
+ # Note: rescue LoadError is not enough in AWS Lambda environment
9
+ # Actual exceptions:
10
+ # require bundler/setup: Ruby exception "Bundler::GemNotFound"
11
+ # require base_path_mappnig: AWS Lambda reported error "errorType": "Init<LoadError>"
12
+ # Will use a generic rescue Exception though in case error changes in the future.
13
+ puts "WARN: #{e.class} #{e.message}"
14
+ puts <<~EOL
15
+ Could not require bundler/setup.
16
+ This can happen for weird timeout missing error. Example:
17
+
18
+ Could not find timeout-0.3.2 in locally installed gems
19
+
20
+ Happens when the gem command is out-of-date on old versions of ruby 2.7.
21
+ See: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
22
+ EOL
23
+ end
2
24
  require 'cfn_response'
3
- require 'jets/internal/app/functions/jets/base_path_mapping'
4
25
 
5
26
  STAGE_NAME = "<%= @stage_name %>"
6
27
 
7
28
  def lambda_handler(event:, context:)
8
29
  cfn = CfnResponse.new(event, context)
9
30
  cfn.response do
10
- mapping = BasePathMapping.new(event, STAGE_NAME)
31
+ # Super edge case: mapping is nil when require bundler/setup fails
32
+ begin
33
+ mapping = BasePathMapping.new(event, STAGE_NAME)
34
+ rescue NameError => e
35
+ puts "ERROR: #{e.class} #{e.message}"
36
+ puts error_message
37
+ end
38
+
39
+ # This is the "second pass" of CloudFormation when it tries to delete the BasePathMapping during rollback
40
+ if mapping.nil? && event['RequestType'] == "Delete"
41
+ cfn.success # so that CloudFormation can continue the delete process from a rollback
42
+ delay
43
+ return
44
+ end
45
+
46
+ # Normal behavior when mapping is not nil when bundler/setup loads successfully
11
47
  case event['RequestType']
12
48
  when "Create", "Update"
13
49
  mapping.update
@@ -16,3 +52,57 @@ def lambda_handler(event:, context:)
16
52
  end
17
53
  end
18
54
  end
55
+
56
+ def delay
57
+ puts "Delaying 60 seconds to allow user some time to see lambda function logs."
58
+ 60.times do
59
+ puts Time.now
60
+ sleep 1
61
+ end
62
+ end
63
+
64
+ def error_message
65
+ <<~EOL
66
+ This is ultimately the result of require bundler/setup failing to load.
67
+ On the CloudFormation first pass, the BasePathMapping fails to CREATE.
68
+ CloudFormation does a rollback and tries to delete the BasePathMapping.
69
+
70
+ Jets will send a success response to CloudFormation so it can continue and delete
71
+ BasePathMapping on the rollback. Otherwise, CloudFormation ends up in the terminal
72
+ UPDATE_FAILED state and the CloudFormation console provides 3 options:
73
+
74
+ 1) retry 2) update 3) rollback.
75
+
76
+ The only option that seems to work is rollback to get it out of UPDATE_FAILED to
77
+ UPDATE_ROLLBACK_COMPLETE. But then, if we `jets deploy` again without fixing the
78
+ require bundler/setup issue, we'll end back up in the UPDATE_FAILED state.
79
+
80
+ Will handle this error so we can continue the stack because we do not want it to fail
81
+ and never be able to send the CfnResponse. Then we have to wait hours for a CloudFormation timeout.
82
+ Sometimes deleting the APP-dev-ApiDeployment20230518230443-EXAMPLEY8YQP0 stack
83
+ allows the cloudformation stacks to continue, but usually, it'll only just slightly speed up the rollback.
84
+
85
+ Some examples of actual rollback times:
86
+
87
+ When left alone, the rollback takes about 2.5 hours.
88
+
89
+ 2023-05-19 05:39:25 User Initiated
90
+ 2023-05-19 08:01:48 UPDATE_ROLLBACK_COMPLETE
91
+
92
+ When deleting the APP-dev-ApiDeployment20230518230443-EXAMPLEY8YQP0 stack, it takes about 1.5 hours.
93
+
94
+ 2023-05-19 06:25:41 User Initiated
95
+ 2023-05-19 07:47:03 UPDATE_ROLLBACK_COMPLETE
96
+
97
+ Rescuing and handling the error here allows the CloudFormation stack to continue and finish the rollback process.
98
+ It takes the rollback time down to about 3 minutes. Example:
99
+
100
+ 2023-05-19 16:34:19 User Initiated
101
+ 2023-05-19 16:37:34 UPDATE_ROLLBACK_COMPLETE
102
+
103
+ Note: The first cloudformation CREATE pass sends FAILED Status to CloudFormation,
104
+ and the second cloudformation DELETE pass sends SUCCESS Status to CloudFormation.
105
+
106
+ Related: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
107
+ EOL
108
+ end
@@ -28,8 +28,28 @@ class BasePathMapping
28
28
  # Cannot use update_base_path_mapping to update the base_mapping because it doesnt
29
29
  # allow us to change the rest_api_id. So we delete and create.
30
30
  def update
31
- delete(true)
32
- create
31
+ puts "BasePathMapping update"
32
+ if rest_api_changed?
33
+ delete(true)
34
+ create
35
+ else
36
+ puts "BasePathMapping update: rest_api_id #{rest_api_id} did not change. Skipping."
37
+ end
38
+
39
+ puts "BasePathMapping update complete"
40
+ end
41
+
42
+ def rest_api_changed?
43
+ puts "BasePathMapping checking if rest_api_id changed"
44
+ mapping = current_base_path_mapping
45
+ return true unless mapping
46
+ mapping.rest_api_id != rest_api_id
47
+ end
48
+
49
+ def current_base_path_mapping
50
+ resp = apigateway.get_base_path_mapping(base_path: "(none)", domain_name: domain_name)
51
+ rescue Aws::APIGateway::Errors::NotFoundException
52
+ return nil
33
53
  end
34
54
 
35
55
  # Dont delete the newly created base path mapping unless this is an operation
@@ -39,10 +59,14 @@ class BasePathMapping
39
59
  end
40
60
 
41
61
  def delete(fail_silently=false)
42
- apigateway.delete_base_path_mapping(
62
+ puts "BasePathMapping delete"
63
+ options = {
43
64
  domain_name: domain_name, # required
44
65
  base_path: base_path.empty? ? '(none)' : base_path,
45
- )
66
+ }
67
+ puts "BasePathMapping delete options #{options.inspect}"
68
+ apigateway.delete_base_path_mapping(options)
69
+ puts "BasePathMapping delete complete"
46
70
  # https://github.com/tongueroo/jets/issues/255
47
71
  # Used to return: Aws::APIGateway::Errors::NotFoundException
48
72
  # Now returns: Aws::APIGateway::Errors::InternalFailure
@@ -52,12 +76,41 @@ class BasePathMapping
52
76
  end
53
77
 
54
78
  def create
55
- apigateway.create_base_path_mapping(
79
+ puts "BasePathMapping create"
80
+ options = {
56
81
  domain_name: domain_name, # required
57
82
  base_path: base_path,
58
83
  rest_api_id: rest_api_id, # required
59
84
  stage: @stage_name,
60
- )
85
+ }
86
+ puts "BasePathMapping create options #{options.inspect}"
87
+ apigateway.create_base_path_mapping(options)
88
+ puts "BasePathMapping create complete"
89
+ rescue Aws::APIGateway::Errors::ServiceError => e
90
+ puts "ERROR: #{e.class}: #{e.message}"
91
+ puts "BasePathMapping create failed"
92
+ if e.message.include?("Invalid domain name identifier specified")
93
+ puts <<~EOL
94
+ This super edge case error seems to happen when the cloudformation stack does a rollback
95
+ because the BasePathMapping custom resource fails. This has happened with a strange combination of
96
+ ruby 2.7 and the timeout gem not being picked up in the AWS Lambda runtime environment
97
+ Specifically, when jets deploy was used with a rubygems install that is out-of-date.
98
+ See: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
99
+
100
+ The new base path mapping is not created correctly and the old base path mapping is not properly deleted.
101
+ The old ghost base mapping interferes with the new base path mapping.
102
+ The workaround solution seems to require removing all the config.domain settings and deploying again. Example:
103
+
104
+ config/application.rb
105
+
106
+ config.domain.cert_arn = "arn:aws:acm:us-west-2:111111111111:certificate/EXAMPLE1-a3de-4fe7-b72e-4cc153c5303e"
107
+ config.domain.hosted_zone_name = "example.com"
108
+
109
+ Comment out those settings, deploy, then uncomment and deploy again.
110
+ If there's a better workaround, please let us know.
111
+ EOL
112
+ end
113
+ raise(e)
61
114
  end
62
115
 
63
116
  def deployment_stack
@@ -110,3 +163,9 @@ class BasePathMapping
110
163
  options
111
164
  end
112
165
  end
166
+
167
+ if __FILE__ == $0
168
+ event = JSON.load(File.read(ARGV[0]))
169
+ context = nil # stub out
170
+ BasePathMapping.new(event, context).update
171
+ end
data/lib/jets/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jets
2
- VERSION = "3.2.1"
2
+ VERSION = "3.2.2"
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: 3.2.1
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-03 00:00:00.000000000 Z
11
+ date: 2023-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionmailer
@@ -1103,7 +1103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1103
1103
  - !ruby/object:Gem::Version
1104
1104
  version: '0'
1105
1105
  requirements: []
1106
- rubygems_version: 3.4.6
1106
+ rubygems_version: 3.4.13
1107
1107
  signing_key:
1108
1108
  specification_version: 4
1109
1109
  summary: Ruby Serverless Framework