mirror of
https://github.com/puppetlabs/vmpooler-dns-gcp.git
synced 2026-01-26 02:58:42 -05:00
Rename for consistency
Squash working
This commit is contained in:
parent
e3a8455ead
commit
8c73d160d9
3 changed files with 144 additions and 149 deletions
139
lib/vmpooler/dns/gcp.rb
Normal file
139
lib/vmpooler/dns/gcp.rb
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'googleauth'
|
||||||
|
require 'google/cloud/dns'
|
||||||
|
require 'vmpooler/dns/base'
|
||||||
|
|
||||||
|
module Vmpooler
|
||||||
|
class PoolManager
|
||||||
|
class Dns
|
||||||
|
# This class represent a DNS plugin to CRUD resources in Google Cloud DNS.
|
||||||
|
class Gcp < Vmpooler::PoolManager::Dns::Base
|
||||||
|
# The connection_pool method is normally used only for testing
|
||||||
|
attr_reader :connection_pool
|
||||||
|
|
||||||
|
def initialize(config, logger, metrics, redis_connection_pool, name, options)
|
||||||
|
super(config, logger, metrics, redis_connection_pool, name, options)
|
||||||
|
|
||||||
|
task_limit = global_config[:config].nil? || global_config[:config]['task_limit'].nil? ? 10 : global_config[:config]['task_limit'].to_i
|
||||||
|
|
||||||
|
default_connpool_size = [provided_pools.count, task_limit, 2].max
|
||||||
|
connpool_timeout = 60
|
||||||
|
logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{default_connpool_size} with timeout #{connpool_timeout}")
|
||||||
|
@connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new(
|
||||||
|
metrics: metrics,
|
||||||
|
connpool_type: 'dns_connection_pool',
|
||||||
|
connpool_provider: name,
|
||||||
|
size: default_connpool_size,
|
||||||
|
timeout: connpool_timeout
|
||||||
|
) do
|
||||||
|
logger.log('d', "[#{name}] Connection Pool - Creating a connection object")
|
||||||
|
# Need to wrap the GCP connection object in another object. The generic connection pooler will preserve
|
||||||
|
# the object reference for the connection, which means it cannot "reconnect" by creating an entirely new connection
|
||||||
|
# object. Instead by wrapping it in a Hash, the Hash object reference itself never changes but the content of the
|
||||||
|
# Hash can change, and is preserved across invocations.
|
||||||
|
new_conn = connect_to_gcp
|
||||||
|
{ connection: new_conn }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
'gcp'
|
||||||
|
end
|
||||||
|
|
||||||
|
# main configuration options
|
||||||
|
def project
|
||||||
|
dns_config['project']
|
||||||
|
end
|
||||||
|
|
||||||
|
def zone_name
|
||||||
|
dns_config['zone_name']
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_or_replace_record(hostname)
|
||||||
|
retries = 0
|
||||||
|
ip = get_ip(hostname)
|
||||||
|
if ip.nil?
|
||||||
|
debug_logger("An IP Address was not recorded for #{hostname}")
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
change = connection.zone(zone_name).add(hostname, 'A', 60, ip)
|
||||||
|
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address added") if change
|
||||||
|
rescue Google::Cloud::AlreadyExistsError => _e
|
||||||
|
# the error is Google::Cloud::AlreadyExistsError: alreadyExists: The resource 'entity.change.additions[0]' named 'instance-8.test.vmpooler.net. (A)' already exists
|
||||||
|
# the error is Google::Cloud::AlreadyExistsError: alreadyExists: The resource 'entity.change.additions[0]' named 'instance-8.test.vmpooler.net. (A)' already exists
|
||||||
|
change = connection.zone(zone_name).replace(hostname, 'A', 60, ip)
|
||||||
|
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address previously existed and was replaced") if change
|
||||||
|
rescue Google::Cloud::FailedPreconditionError => e
|
||||||
|
debug_logger("DNS create failed, retrying error: #{e}")
|
||||||
|
sleep 5
|
||||||
|
retry if (retries += 1) < 30
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_record(hostname)
|
||||||
|
retries = 0
|
||||||
|
begin
|
||||||
|
connection.zone(zone_name).remove(hostname, 'A')
|
||||||
|
rescue Google::Cloud::FailedPreconditionError => e
|
||||||
|
# this error was experienced intermittently, will retry to see if it can complete successfully
|
||||||
|
# the error is Google::Cloud::FailedPreconditionError: conditionNotMet: Precondition not met for 'entity.change.deletions[1]'
|
||||||
|
debug_logger("GCP DNS delete_record failed, retrying error: #{e}")
|
||||||
|
sleep 5
|
||||||
|
retry if (retries += 1) < 30
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def connection
|
||||||
|
@connection_pool.with_metrics do |pool_object|
|
||||||
|
return ensured_gcp_connection(pool_object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensured_gcp_connection(connection_pool_object)
|
||||||
|
connection_pool_object[:connection] = connect_to_gcp unless gcp_connection_ok?(connection_pool_object[:connection])
|
||||||
|
connection_pool_object[:connection]
|
||||||
|
end
|
||||||
|
|
||||||
|
def gcp_connection_ok?(connection)
|
||||||
|
_result = connection.id
|
||||||
|
true
|
||||||
|
rescue StandardError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def connect_to_gcp
|
||||||
|
max_tries = global_config[:config]['max_tries'] || 3
|
||||||
|
retry_factor = global_config[:config]['retry_factor'] || 10
|
||||||
|
try = 1
|
||||||
|
begin
|
||||||
|
Google::Cloud::Dns.configure do |config|
|
||||||
|
config.project_id = project
|
||||||
|
end
|
||||||
|
|
||||||
|
dns = Google::Cloud::Dns.new
|
||||||
|
|
||||||
|
metrics.increment('connect.open')
|
||||||
|
dns
|
||||||
|
rescue StandardError => e
|
||||||
|
metrics.increment('connect.fail')
|
||||||
|
raise e if try >= max_tries
|
||||||
|
|
||||||
|
sleep(try * retry_factor)
|
||||||
|
try += 1
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# used in local dev environment, set DEBUG_FLAG=true
|
||||||
|
# this way the upstream vmpooler manager does not get polluted with logs
|
||||||
|
def debug_logger(message, send_to_upstream: false)
|
||||||
|
# the default logger is simple and does not enforce debug levels (the first argument)
|
||||||
|
puts message if ENV['DEBUG_FLAG']
|
||||||
|
logger.log('[g]', message) if send_to_upstream
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'googleauth'
|
|
||||||
require 'google/cloud/dns'
|
|
||||||
require 'vmpooler/dns/base'
|
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class PoolManager
|
|
||||||
class Dns
|
|
||||||
class Gcp
|
|
||||||
# This class represent a DNS plugin to CRUD resources in google clouddns.
|
|
||||||
class Clouddns < Vmpooler::PoolManager::Dns::Base
|
|
||||||
# The connection_pool method is normally used only for testing
|
|
||||||
attr_reader :connection_pool
|
|
||||||
|
|
||||||
def initialize(config, logger, metrics, redis_connection_pool, name, options)
|
|
||||||
super(config, logger, metrics, redis_connection_pool, name, options)
|
|
||||||
|
|
||||||
task_limit = global_config[:config].nil? || global_config[:config]['task_limit'].nil? ? 10 : global_config[:config]['task_limit'].to_i
|
|
||||||
|
|
||||||
default_connpool_size = [provided_pools.count, task_limit, 2].max
|
|
||||||
# connpool_size = provider_config['connection_pool_size'].nil? ? default_connpool_size : provider_config['connection_pool_size'].to_i
|
|
||||||
connpool_timeout = 60
|
|
||||||
# logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{connpool_size} with timeout #{connpool_timeout}")
|
|
||||||
logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{default_connpool_size} with timeout #{connpool_timeout}")
|
|
||||||
@connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new(
|
|
||||||
metrics: metrics,
|
|
||||||
connpool_type: 'dns_connection_pool',
|
|
||||||
connpool_provider: name,
|
|
||||||
size: default_connpool_size,
|
|
||||||
# size: connpool_size,
|
|
||||||
timeout: connpool_timeout
|
|
||||||
) do
|
|
||||||
logger.log('d', "[#{name}] Connection Pool - Creating a connection object")
|
|
||||||
# Need to wrap the vSphere connection object in another object. The generic connection pooler will preserve
|
|
||||||
# the object reference for the connection, which means it cannot "reconnect" by creating an entirely new connection
|
|
||||||
# object. Instead by wrapping it in a Hash, the Hash object reference itself never changes but the content of the
|
|
||||||
# Hash can change, and is preserved across invocations.
|
|
||||||
new_conn = connect_to_gcp
|
|
||||||
{ connection: new_conn }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def name
|
|
||||||
'gcp-clouddns'
|
|
||||||
end
|
|
||||||
|
|
||||||
# main configuration options
|
|
||||||
def project
|
|
||||||
dns_config['project']
|
|
||||||
end
|
|
||||||
|
|
||||||
def zone_name
|
|
||||||
dns_config['zone_name']
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_or_replace_record(hostname)
|
|
||||||
retries = 0
|
|
||||||
ip = get_ip(hostname)
|
|
||||||
if ip.nil?
|
|
||||||
debug_logger("An IP Address was not recorded for #{hostname}")
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
change = connection.zone(zone_name).add(hostname, 'A', 60, ip)
|
|
||||||
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address added") if change
|
|
||||||
rescue Google::Cloud::AlreadyExistsError => _e
|
|
||||||
# the error is Google::Cloud::AlreadyExistsError: alreadyExists: The resource 'entity.change.additions[0]' named 'instance-8.test.vmpooler.net. (A)' already exists
|
|
||||||
# the error is Google::Cloud::AlreadyExistsError: alreadyExists: The resource 'entity.change.additions[0]' named 'instance-8.test.vmpooler.net. (A)' already exists
|
|
||||||
change = connection.zone(zone_name).replace(hostname, 'A', 60, ip)
|
|
||||||
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address previously existed and was replaced") if change
|
|
||||||
rescue Google::Cloud::FailedPreconditionError => e
|
|
||||||
debug_logger("DNS create failed, retrying error: #{e}")
|
|
||||||
sleep 5
|
|
||||||
retry if (retries += 1) < 30
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_record(hostname)
|
|
||||||
retries = 0
|
|
||||||
begin
|
|
||||||
connection.zone(zone_name).remove(hostname, 'A')
|
|
||||||
rescue Google::Cloud::FailedPreconditionError => e
|
|
||||||
# this error was experienced intermittently, will retry to see if it can complete successfully
|
|
||||||
# the error is Google::Cloud::FailedPreconditionError: conditionNotMet: Precondition not met for 'entity.change.deletions[1]'
|
|
||||||
debug_logger("GCP DNS delete_record failed, retrying error: #{e}")
|
|
||||||
sleep 5
|
|
||||||
retry if (retries += 1) < 30
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def connection
|
|
||||||
@connection_pool.with_metrics do |pool_object|
|
|
||||||
return ensured_gcp_connection(pool_object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensured_gcp_connection(connection_pool_object)
|
|
||||||
connection_pool_object[:connection] = connect_to_gcp unless gcp_connection_ok?(connection_pool_object[:connection])
|
|
||||||
connection_pool_object[:connection]
|
|
||||||
end
|
|
||||||
|
|
||||||
def gcp_connection_ok?(connection)
|
|
||||||
_result = connection.id
|
|
||||||
true
|
|
||||||
rescue StandardError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def connect_to_gcp
|
|
||||||
max_tries = global_config[:config]['max_tries'] || 3
|
|
||||||
retry_factor = global_config[:config]['retry_factor'] || 10
|
|
||||||
try = 1
|
|
||||||
begin
|
|
||||||
Google::Cloud::Dns.configure do |config|
|
|
||||||
config.project_id = project
|
|
||||||
end
|
|
||||||
|
|
||||||
dns = Google::Cloud::Dns.new
|
|
||||||
|
|
||||||
metrics.increment('connect.open')
|
|
||||||
dns
|
|
||||||
rescue StandardError => e
|
|
||||||
metrics.increment('connect.fail')
|
|
||||||
raise e if try >= max_tries
|
|
||||||
|
|
||||||
sleep(try * retry_factor)
|
|
||||||
try += 1
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# used in local dev environment, set DEBUG_FLAG=true
|
|
||||||
# this way the upstream vmpooler manager does not get polluted with logs
|
|
||||||
def debug_logger(message, send_to_upstream: false)
|
|
||||||
# the default logger is simple and does not enforce debug levels (the first argument)
|
|
||||||
puts message if ENV['DEBUG_FLAG']
|
|
||||||
logger.log('[g]', message) if send_to_upstream
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'mock_redis'
|
require 'mock_redis'
|
||||||
require 'vmpooler/dns/gcp/clouddns'
|
require 'vmpooler/dns/gcp'
|
||||||
|
|
||||||
describe 'Vmpooler::PoolManager::Dns::Gcp' do
|
describe 'Vmpooler::PoolManager::Dns::Gcp' do
|
||||||
let(:logger) { MockLogger.new }
|
let(:logger) { MockLogger.new }
|
||||||
|
|
@ -16,7 +16,7 @@ describe 'Vmpooler::PoolManager::Dns::Gcp' do
|
||||||
retry_factor: 10
|
retry_factor: 10
|
||||||
:dns_configs:
|
:dns_configs:
|
||||||
:#{name}:
|
:#{name}:
|
||||||
dns_class: gcp-clouddns
|
dns_class: gcp
|
||||||
project: vmpooler-example
|
project: vmpooler-example
|
||||||
domain: vmpooler.example.com
|
domain: vmpooler.example.com
|
||||||
zone_name: vmpooler-example-com
|
zone_name: vmpooler-example-com
|
||||||
|
|
@ -50,11 +50,11 @@ EOT
|
||||||
) { MockRedis.new }
|
) { MockRedis.new }
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { Vmpooler::PoolManager::Dns::Gcp::Clouddns.new(config, logger, metrics, redis_connection_pool, name, options) }
|
subject { Vmpooler::PoolManager::Dns::Gcp.new(config, logger, metrics, redis_connection_pool, name, options) }
|
||||||
|
|
||||||
describe '#name' do
|
describe '#name' do
|
||||||
it 'should be gcp-clouddns' do
|
it 'should be gcp' do
|
||||||
expect(subject.name).to eq('gcp-clouddns')
|
expect(subject.name).to eq('gcp')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue