Class: BasePathMapping

Inherits:
Object
  • Object
show all
Defined in:
lib/jets/internal/app/functions/jets/base_path_mapping.rb

Instance Method Summary collapse

Constructor Details

#initialize(event, stage_name) ⇒ BasePathMapping

Returns a new instance of BasePathMapping.



5
6
7
8
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 5

def initialize(event, stage_name)
  @event, @stage_name = event, stage_name
  aws_config_update!
end

Instance Method Details

#apigatewayObject



146
147
148
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 146

def apigateway
  @apigateway ||= Aws::APIGateway::Client.new(aws_options)
end

#aws_config_update!Object

Override the AWS retry settings. The aws-sdk-core has expondential backup with this formula:

2 ** c.retries * c.config.retry_base_delay

So the max delay will be 2 ** 7 * 0.6 = 76.8s

Useful links:

https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-core/lib/aws-sdk-core/plugins/retry_errors.rb
https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html


21
22
23
24
25
26
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 21

def aws_config_update!
  Aws.config.update(
    retry_limit: 7, # default: 3
    retry_base_delay: 0.6, # default: 0.3
  )
end

#aws_optionsObject



154
155
156
157
158
159
160
161
162
163
164
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 154

def aws_options
  options = {
    retry_limit: 7, # default: 3
    retry_base_delay: 0.6, # default: 0.3
  }
  options.merge!(
    log_level: :debug,
    logger: Logger.new($stdout),
  ) if ENV['JETS_DEBUG_AWS_SDK']
  options
end

#base_pathObject



128
129
130
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 128

def base_path
  @base_path ||= parameter_value('BasePath') || ''
end

#cfnObject



150
151
152
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 150

def cfn
  @cfn ||= Aws::CloudFormation::Client.new(aws_options)
end

#createObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 78

def create
  puts "BasePathMapping create"
  options = {
    domain_name: domain_name, # required
    base_path: base_path,
    rest_api_id: rest_api_id, # required
    stage: @stage_name,
  }
  puts "BasePathMapping create options #{options.inspect}"
  apigateway.create_base_path_mapping(options)
  puts "BasePathMapping create complete"
rescue Aws::APIGateway::Errors::ServiceError => e
  puts "ERROR: #{e.class}: #{e.message}"
  puts "BasePathMapping create failed"
  if e.message.include?("Invalid domain name identifier specified")
    puts <<~EOL
      This super edge case error seems to happen when the cloudformation stack does a rollback
      because the BasePathMapping custom resource fails. This has happened with a strange combination of
      ruby 2.7 and the timeout gem not being picked up in the AWS Lambda runtime environment
      Specifically, when jets deploy was used with a rubygems install that is out-of-date.
      See: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996

      The new base path mapping is not created correctly and the old base path mapping is not properly deleted.
      The old ghost base mapping interferes with the new base path mapping.
      The workaround solution seems to require removing all the config.domain settings and deploying again. Example:

      config/application.rb

          config.domain.cert_arn = "arn:aws:acm:us-west-2:111111111111:certificate/EXAMPLE1-a3de-4fe7-b72e-4cc153c5303e"
          config.domain.hosted_zone_name = "example.com"

      Comment out those settings, deploy, then uncomment and deploy again.
      If there's a better workaround, please let us know.
    EOL
  end
  raise(e)
end

#current_base_path_mappingObject



49
50
51
52
53
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 49

def current_base_path_mapping
  resp = apigateway.get_base_path_mapping(base_path: "(none)", domain_name: domain_name)
rescue Aws::APIGateway::Errors::NotFoundException
  return nil
end

#delete(fail_silently = false) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 61

def delete(fail_silently=false)
  puts "BasePathMapping delete"
  options = {
    domain_name: domain_name, # required
    base_path: base_path.empty? ? '(none)' : base_path,
  }
  puts "BasePathMapping delete options #{options.inspect}"
  apigateway.delete_base_path_mapping(options)
  puts "BasePathMapping delete complete"
# https://github.com/tongueroo/jets/issues/255
# Used to return: Aws::APIGateway::Errors::NotFoundException
# Now returns: Aws::APIGateway::Errors::InternalFailure
# So we'll use a more generic error
rescue Aws::APIGateway::Errors::ServiceError => e
  raise(e) unless fail_silently
end

#deleting_parent?Boolean

Returns:

  • (Boolean)


137
138
139
140
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 137

def deleting_parent?
  stack = cfn.describe_stacks(stack_name: parent_stack_name).stacks.first
  stack.stack_status == 'DELETE_IN_PROGRESS'
end

#deployment_stackObject



116
117
118
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 116

def deployment_stack
  @deployment_stack ||= cfn.describe_stacks(stack_name: @event['StackId']).stacks.first
end

#domain_nameObject



124
125
126
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 124

def domain_name
  @domain_name ||= parameter_value('DomainName')
end

#parameter_value(parameter_key) ⇒ Object



132
133
134
135
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 132

def parameter_value(parameter_key)
  param = deployment_stack[:parameters].find { |p| p.parameter_key == parameter_key }
  param&.parameter_value # possible for this to be nil when removing the config: IE: config.domain.name = nil
end

#parent_stack_nameObject



142
143
144
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 142

def parent_stack_name
  deployment_stack[:root_id]
end

#rest_api_changed?Boolean

Returns:

  • (Boolean)


42
43
44
45
46
47
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 42

def rest_api_changed?
  puts "BasePathMapping checking if rest_api_id changed"
  mapping = current_base_path_mapping
  return true unless mapping
  mapping.rest_api_id != rest_api_id
end

#rest_api_idObject



120
121
122
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 120

def rest_api_id
  @rest_api_id ||= parameter_value('RestApi')
end

#should_delete?Boolean

Dont delete the newly created base path mapping unless this is an operation where we’re fully deleting the stack

Returns:

  • (Boolean)


57
58
59
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 57

def should_delete?
  deleting_parent?
end

#updateObject

Cannot use update_base_path_mapping to update the base_mapping because it doesnt allow us to change the rest_api_id. So we delete and create.



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/jets/internal/app/functions/jets/base_path_mapping.rb', line 30

def update
  puts "BasePathMapping update"
  if rest_api_changed?
    delete(true)
    create
  else
    puts "BasePathMapping update: rest_api_id #{rest_api_id} did not change. Skipping."
  end

  puts "BasePathMapping update complete"
end