Class: Vmpooler::PoolManager::Dns::Gcp

Inherits:
Base
  • Object
show all
Defined in:
lib/vmpooler/dns/gcp.rb

Overview

This class represent a DNS plugin to CRUD resources in Google Cloud DNS.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, logger, metrics, redis_connection_pool, name, options) ⇒ Gcp

Returns a new instance of Gcp.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/vmpooler/dns/gcp.rb', line 15

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

Instance Attribute Details

#connection_poolObject (readonly)

The connection_pool method is normally used only for testing



13
14
15
# File 'lib/vmpooler/dns/gcp.rb', line 13

def connection_pool
  @connection_pool
end

Instance Method Details

#connect_to_gcpObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/vmpooler/dns/gcp.rb', line 106

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

#connectionObject



88
89
90
91
92
# File 'lib/vmpooler/dns/gcp.rb', line 88

def connection
  @connection_pool.with_metrics do |pool_object|
    return ensured_gcp_connection(pool_object)
  end
end

#create_or_replace_record(hostname) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/vmpooler/dns/gcp.rb', line 53

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

#debug_logger(message, send_to_upstream: false) ⇒ Object

used in local dev environment, set DEBUG_FLAG=true this way the upstream vmpooler manager does not get polluted with logs



131
132
133
134
135
# File 'lib/vmpooler/dns/gcp.rb', line 131

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

#delete_record(hostname) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/vmpooler/dns/gcp.rb', line 75

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

#ensured_gcp_connection(connection_pool_object) ⇒ Object



94
95
96
97
# File 'lib/vmpooler/dns/gcp.rb', line 94

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

#gcp_connection_ok?(connection) ⇒ Boolean

Returns:

  • (Boolean)


99
100
101
102
103
104
# File 'lib/vmpooler/dns/gcp.rb', line 99

def gcp_connection_ok?(connection)
  _result = connection.id
  true
rescue StandardError
  false
end

#nameObject



40
41
42
# File 'lib/vmpooler/dns/gcp.rb', line 40

def name
  'gcp'
end

#projectObject

main configuration options



45
46
47
# File 'lib/vmpooler/dns/gcp.rb', line 45

def project
  dns_config['project']
end

#zone_nameObject



49
50
51
# File 'lib/vmpooler/dns/gcp.rb', line 49

def zone_name
  dns_config['zone_name']
end