Split EdgeCache::Bust into Fastly + Nginx-specific classes (#10505) [deploy]
* Split EdgeCache::Bust into Fastly + Nginx-specific classes * Update EdgeCache::Bust service specs * Move Nginx TODO comment into Nginx-specific file
This commit is contained in:
parent
e1b3844dfa
commit
6a697173bc
4 changed files with 96 additions and 83 deletions
|
|
@ -1,36 +1,27 @@
|
|||
module EdgeCache
|
||||
class Bust
|
||||
PROVIDERS = %w[fastly nginx].freeze
|
||||
|
||||
def self.call(*args)
|
||||
new(*args).call
|
||||
end
|
||||
|
||||
def initialize(path)
|
||||
# TODO: (Vaidehi Joshi) - Right now, we are checking that nginx is
|
||||
# available on every purge request/call to this bust service. If we are going
|
||||
# to bust multiple paths, we should be able to check that nginx is
|
||||
# available just once, and persist it on the class with @provider_available?.
|
||||
# Then, we could allow for an array of @paths = [] to be passed in,
|
||||
# and on single bust instance could bust multiple paths in order.
|
||||
|
||||
@path = path
|
||||
@provider = determine_provider
|
||||
@response = nil
|
||||
end
|
||||
|
||||
def call
|
||||
return unless PROVIDERS.include?(provider)
|
||||
return unless provider
|
||||
|
||||
bust_method = "bust_#{provider}_cache"
|
||||
if respond_to?(bust_method, true)
|
||||
@response = __send__(bust_method)
|
||||
provider_class = "#{self.class}::#{provider.capitalize}".constantize
|
||||
|
||||
if provider_class.respond_to?(:call)
|
||||
@response = provider_class.call(path)
|
||||
else
|
||||
# We theoretically should never hit this unless someone adds a provider
|
||||
# but doesn't add the implementation for it into the #call method.
|
||||
@response = nil
|
||||
Rails.logger.warn("EdgeCache::Bust was called with an invalid provider: #{provider}")
|
||||
DatadogStatsClient.increment("edgecache_bust.invalid_provider", tags: ["provider:#{provider}"])
|
||||
Rails.logger.warn("#{provider_class} cannot be used without a #call implementation!")
|
||||
DatadogStatsClient.increment("edgecache_bust.invalid_provider_class",
|
||||
tags: ["provider_class:#{provider_class}"])
|
||||
end
|
||||
|
||||
self
|
||||
|
|
@ -43,40 +34,11 @@ module EdgeCache
|
|||
def determine_provider
|
||||
if fastly_enabled?
|
||||
"fastly"
|
||||
elsif nginx_enabled? && nginx_available?
|
||||
elsif nginx_enabled?
|
||||
"nginx"
|
||||
end
|
||||
end
|
||||
|
||||
def bust_fastly_cache
|
||||
# TODO: (Alex Smith) - It would be "nice to have" the ability to use the
|
||||
# Fastly gem here instead of custom API calls.
|
||||
|
||||
# @forem/systems Fastly-enabled forems don't need "flexible" domains.
|
||||
HTTParty.post(
|
||||
"https://api.fastly.com/purge/https://#{URL.domain}#{path}",
|
||||
headers: {
|
||||
"Fastly-Key" => ApplicationConfig["FASTLY_API_KEY"]
|
||||
},
|
||||
)
|
||||
HTTParty.post(
|
||||
"https://api.fastly.com/purge/https://#{URL.domain}#{path}?i=i",
|
||||
headers: {
|
||||
"Fastly-Key" => ApplicationConfig["FASTLY_API_KEY"]
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def bust_nginx_cache
|
||||
uri = URI.parse("#{openresty_path}#{path}")
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response = http.request Net::HTTP::NginxPurge.new(uri.request_uri)
|
||||
|
||||
raise StandardError, "NginxPurge request failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
||||
|
||||
response.body
|
||||
end
|
||||
|
||||
def fastly_enabled?
|
||||
ApplicationConfig["FASTLY_API_KEY"].present? && ApplicationConfig["FASTLY_SERVICE_ID"].present?
|
||||
end
|
||||
|
|
@ -84,32 +46,5 @@ module EdgeCache
|
|||
def nginx_enabled?
|
||||
ApplicationConfig["OPENRESTY_PROTOCOL"].present? && ApplicationConfig["OPENRESTY_DOMAIN"].present?
|
||||
end
|
||||
|
||||
def openresty_path
|
||||
"#{ApplicationConfig['OPENRESTY_PROTOCOL']}#{ApplicationConfig['OPENRESTY_DOMAIN']}"
|
||||
end
|
||||
|
||||
def nginx_available?
|
||||
uri = URI.parse(openresty_path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response = http.get(uri.request_uri)
|
||||
|
||||
return true if response.is_a?(Net::HTTPSuccess)
|
||||
rescue StandardError
|
||||
# If we can't connect to Openresty, alert ourselves that
|
||||
# it is unavailable and return false.
|
||||
Rails.logger.error("Could not connect to Openresty via #{openresty_path}!")
|
||||
DatadogStatsClient.increment("edgecache_bust.service_unavailable", tags: ["path:#{openresty_path}"])
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates our own purge method for an HTTP request,
|
||||
# which is used by Nginx to bust a cache.
|
||||
# See Net::HTTPGenericRequest for attributes/methods.
|
||||
class Net::HTTP::NginxPurge < Net::HTTPRequest # rubocop:disable Style/ClassAndModuleChildren
|
||||
METHOD = "PURGE".freeze
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
|
|
|||
24
app/services/edge_cache/bust/fastly.rb
Normal file
24
app/services/edge_cache/bust/fastly.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
module EdgeCache
|
||||
class Bust
|
||||
class Fastly
|
||||
def self.call(path)
|
||||
# TODO: (Alex Smith) - It would be "nice to have" the ability to use the
|
||||
# Fastly gem here instead of custom API calls.
|
||||
|
||||
# @forem/systems Fastly-enabled forems don't need "flexible" domains.
|
||||
HTTParty.post(
|
||||
"https://api.fastly.com/purge/https://#{URL.domain}#{path}",
|
||||
headers: {
|
||||
"Fastly-Key" => ApplicationConfig["FASTLY_API_KEY"]
|
||||
},
|
||||
)
|
||||
HTTParty.post(
|
||||
"https://api.fastly.com/purge/https://#{URL.domain}#{path}?i=i",
|
||||
headers: {
|
||||
"Fastly-Key" => ApplicationConfig["FASTLY_API_KEY"]
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
50
app/services/edge_cache/bust/nginx.rb
Normal file
50
app/services/edge_cache/bust/nginx.rb
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
module EdgeCache
|
||||
class Bust
|
||||
class Nginx
|
||||
def self.call(path)
|
||||
return unless nginx_available?
|
||||
|
||||
uri = URI.parse("#{openresty_path}#{path}")
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response = http.request Net::HTTP::Purge.new(uri.request_uri)
|
||||
|
||||
raise StandardError, "Nginx Purge request failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
||||
|
||||
response.body
|
||||
end
|
||||
|
||||
def self.openresty_path
|
||||
"#{ApplicationConfig['OPENRESTY_PROTOCOL']}#{ApplicationConfig['OPENRESTY_DOMAIN']}"
|
||||
end
|
||||
|
||||
def self.nginx_available?
|
||||
# TODO: (Vaidehi Joshi) - Right now, we are checking that nginx is
|
||||
# available on every purge request/call to this bust service. If we are going
|
||||
# to bust multiple paths, we should be able to check that nginx is
|
||||
# available just once, and persist it on the class with @provider_available?.
|
||||
# Then, we could allow for an array of @paths = [] to be passed in,
|
||||
# and on single bust instance could bust multiple paths in order.
|
||||
uri = URI.parse(openresty_path)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response = http.get(uri.request_uri)
|
||||
|
||||
return true if response.is_a?(Net::HTTPSuccess)
|
||||
rescue StandardError
|
||||
# If we can't connect to Openresty, alert ourselves that
|
||||
# it is unavailable and return false.
|
||||
Rails.logger.error("Could not connect to Openresty via #{openresty_path}!")
|
||||
DatadogStatsClient.increment("edgecache_bust.service_unavailable", tags: ["path:#{openresty_path}"])
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates our own purge method for an HTTP request,
|
||||
# which is used by Nginx to bust a cache.
|
||||
# See Net::HTTPGenericRequest for attributes/methods.
|
||||
class Net::HTTP::Purge < Net::HTTPRequest # rubocop:disable Style/ClassAndModuleChildren
|
||||
METHOD = "PURGE".freeze
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
|
@ -5,6 +5,8 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
let(:path) { "/#{user.username}" }
|
||||
|
||||
describe "#bust_fastly_cache" do
|
||||
let(:fastly_provider_class) { EdgeCache::Bust::Fastly }
|
||||
|
||||
context "when fastly is not configured" do
|
||||
before do
|
||||
stub_fastly
|
||||
|
|
@ -14,11 +16,11 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
let(:cache_bust_service) { described_class.new(path) }
|
||||
|
||||
it "does not bust a fastly cache" do
|
||||
allow(cache_bust_service).to receive(:bust_fastly_cache)
|
||||
allow(fastly_provider_class).to receive(:call)
|
||||
|
||||
cache_bust_service.call
|
||||
expect(cache_bust_service.provider).to be(nil)
|
||||
expect(cache_bust_service).not_to have_received(:bust_fastly_cache)
|
||||
expect(fastly_provider_class).not_to have_received(:call)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -31,15 +33,15 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
let(:cache_bust_service) { described_class.new(path) }
|
||||
|
||||
it "can bust a fastly cache" do
|
||||
allow(cache_bust_service).to receive(:bust_fastly_cache)
|
||||
allow(fastly_provider_class).to receive(:call)
|
||||
|
||||
cache_bust_service.call
|
||||
expect(cache_bust_service.provider).to eq("fastly")
|
||||
expect(cache_bust_service).to have_received(:bust_fastly_cache)
|
||||
expect(fastly_provider_class).to have_received(:call)
|
||||
end
|
||||
|
||||
it "returns cache bust response" do
|
||||
allow(cache_bust_service).to receive(:bust_fastly_cache).and_return("success")
|
||||
allow(fastly_provider_class).to receive(:call).and_return("success")
|
||||
|
||||
cache_bust_service.call
|
||||
expect(cache_bust_service.response).to eq("success")
|
||||
|
|
@ -48,6 +50,8 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
end
|
||||
|
||||
describe "#bust_nginx_cache" do
|
||||
let(:nginx_provider_class) { EdgeCache::Bust::Nginx }
|
||||
|
||||
before do
|
||||
# Explicitly stub Fastly since we check if Fastly has
|
||||
# been configured before we try to use Nginx.
|
||||
|
|
@ -62,11 +66,11 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
let(:cache_bust_service) { described_class.new(path) }
|
||||
|
||||
it "does not bust an nginx cache" do
|
||||
allow(cache_bust_service).to receive(:bust_nginx_cache)
|
||||
allow(nginx_provider_class).to receive(:call)
|
||||
|
||||
cache_bust_service.call
|
||||
expect(cache_bust_service.provider).to eq(nil)
|
||||
expect(cache_bust_service).not_to have_received(:bust_nginx_cache)
|
||||
expect(nginx_provider_class).not_to have_received(:call)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -78,11 +82,11 @@ RSpec.describe EdgeCache::Bust, type: :service do
|
|||
let(:cache_bust_service) { described_class.new(path) }
|
||||
|
||||
it "can bust an nginx cache" do
|
||||
allow(cache_bust_service).to receive(:bust_nginx_cache)
|
||||
allow(nginx_provider_class).to receive(:call)
|
||||
|
||||
cache_bust_service.call
|
||||
expect(cache_bust_service.provider).to eq("nginx")
|
||||
expect(cache_bust_service).to have_received(:bust_nginx_cache)
|
||||
expect(nginx_provider_class).to have_received(:call)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue