mirror of
https://github.com/puppetlabs/vmpooler-provider-gce.git
synced 2026-01-26 03:18:41 -05:00
Merge pull request #21 from puppetlabs/vmpooler-dns-gcp
(RE-15124) Decouple DNS Record Management into DNS Plugins
This commit is contained in:
commit
bee66557ea
9 changed files with 85 additions and 137 deletions
25
Gemfile.lock
25
Gemfile.lock
|
|
@ -3,9 +3,8 @@ PATH
|
|||
specs:
|
||||
vmpooler-provider-gce (0.5.0)
|
||||
google-apis-compute_v1 (~> 0.14)
|
||||
google-cloud-dns (~> 0.35.1)
|
||||
googleauth (>= 0.16.2, < 1.3.0)
|
||||
vmpooler (~> 2.3, >= 1.3.0)
|
||||
vmpooler (~> 3.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
|
|
@ -39,19 +38,8 @@ GEM
|
|||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-dns_v1 (0.31.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-dns (0.35.1)
|
||||
google-apis-dns_v1 (~> 0.1)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
zonefile (~> 1.04)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
googleauth (1.2.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
|
|
@ -158,9 +146,9 @@ GEM
|
|||
rubocop-ast (>= 1.0.1)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-ast (1.27.0)
|
||||
rubocop-ast (1.26.0)
|
||||
parser (>= 3.2.1.0)
|
||||
ruby-progressbar (1.12.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
|
|
@ -184,12 +172,12 @@ GEM
|
|||
ffi
|
||||
statsd-ruby (1.5.0)
|
||||
thor (1.2.1)
|
||||
thrift (0.18.1)
|
||||
thrift (0.18.0)
|
||||
tilt (2.1.0)
|
||||
trailblazer-option (0.1.2)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (1.8.0)
|
||||
vmpooler (2.4.0)
|
||||
vmpooler (3.0.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
connection_pool (~> 2.2)
|
||||
deep_merge (~> 1.2)
|
||||
|
|
@ -214,7 +202,6 @@ GEM
|
|||
yarjuf (2.0.0)
|
||||
builder
|
||||
rspec (~> 3)
|
||||
zonefile (1.06)
|
||||
|
||||
PLATFORMS
|
||||
universal-java-1.8
|
||||
|
|
@ -233,4 +220,4 @@ DEPENDENCIES
|
|||
yarjuf (>= 2.0)
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.5
|
||||
2.4.7
|
||||
|
|
|
|||
37
README.md
37
README.md
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- [vmpooler-provider-gce](#vmpooler-provider-gce)
|
||||
- [Usage](#usage)
|
||||
- [Migrating to v1](#migrating-to-v1)
|
||||
- [DNS](#dns)
|
||||
- [Labels](#labels)
|
||||
- [Pre-requisite](#pre-requisite)
|
||||
|
|
@ -23,6 +24,42 @@ GCE authorization is handled via a service account (or personal account) private
|
|||
|
||||
1. GOOGLE_APPLICATION_CREDENTIALS environment variable eg GOOGLE_APPLICATION_CREDENTIALS=/my/home/directory/my_account_key.json
|
||||
|
||||
### Migrating to v1
|
||||
|
||||
Starting with the v1.x release, management of DNS records has been extracted from this compute provider and implemented as DNS plugins, similar to compute providers. This means each pool configuration should be pointing to a configuration object in `:dns_config` to determine it's method of record management.
|
||||
|
||||
For those using DNS management via this provider, the DNS related options should be moved under `:dns_configs:<INSERT_YOUR_OWN_SYMBOL>` with the value for `dns_class`.
|
||||
|
||||
For example, the following keys in a v0.x GCE provider config:
|
||||
|
||||
```yaml
|
||||
:providers:
|
||||
:gce:
|
||||
domain: vmpooler.example.com
|
||||
dns_zone_resource_name: vmpooler-example-com
|
||||
```
|
||||
|
||||
Would be moved to:
|
||||
|
||||
```yaml
|
||||
:dns_configs:
|
||||
:example:
|
||||
dns_class: gcp-clouddns
|
||||
project: jake-vmpooler-dev
|
||||
domain: vmpooler.example.com
|
||||
zone_name: vmpooler-example-com
|
||||
```
|
||||
|
||||
Then any pools that should have records created via the dns config above should now reference the named dns config in the `dns_plugin` key:
|
||||
|
||||
```yaml
|
||||
:pools:
|
||||
- name: 'debian-11-x86_64'
|
||||
dns_plugin: 'example'
|
||||
```
|
||||
|
||||
For complete examples on how to use the GCP DNS plugin see [vmpooler-dns-gcp](https://github.com/puppetlabs/vmpooler-dns-gcp).
|
||||
|
||||
### DNS
|
||||
DNS is integrated via Google's CloudDNS service. To enable, a CloudDNS zone name must be provided in the config (see the example yaml file dns_zone_resource_name)
|
||||
|
||||
|
|
|
|||
7
install-gemfile-lock
Executable file
7
install-gemfile-lock
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# The container tag should closely match what is used in `docker/Dockerfile` in vmpooler-deployment
|
||||
docker run -it --rm \
|
||||
-v $(pwd):/app \
|
||||
jruby:9.4.1.0-jdk11 \
|
||||
/bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make git netbase && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"'
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'googleauth'
|
||||
require 'google/cloud/dns'
|
||||
|
||||
module Vmpooler
|
||||
class PoolManager
|
||||
# This class interacts with GCP Cloud DNS to create or delete records.
|
||||
class CloudDns
|
||||
def initialize(project, dns_zone_resource_name)
|
||||
@dns = Google::Cloud::Dns.new(project_id: project)
|
||||
@dns_zone_resource_name = dns_zone_resource_name
|
||||
end
|
||||
|
||||
def dns_create_or_replace(created_instance)
|
||||
dns_zone = @dns.zone(@dns_zone_resource_name) if @dns_zone_resource_name
|
||||
return unless dns_zone && created_instance && created_instance['name'] && created_instance['ip']
|
||||
|
||||
retries = 0
|
||||
name = created_instance['name']
|
||||
begin
|
||||
change = dns_zone.add(name, 'A', 60, [created_instance['ip']])
|
||||
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address added") if change
|
||||
rescue Google::Cloud::AlreadyExistsError => _e
|
||||
# DNS setup is done only for new instances, so in the rare case where a DNS record already exists (it is stale) and we replace it.
|
||||
# the error is Google::Cloud::AlreadyExistsError: alreadyExists: The resource 'entity.change.additions[0]' named 'instance-8.test.vmpooler.net. (A)' already exists
|
||||
change = dns_zone.replace(name, 'A', 60, [created_instance['ip']])
|
||||
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address previously existed and was replaced") if change
|
||||
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[0]'
|
||||
debug_logger("DNS create failed, retrying error: #{e}")
|
||||
sleep 5
|
||||
retry if (retries += 1) < 30
|
||||
end
|
||||
end
|
||||
|
||||
def dns_teardown(created_instance)
|
||||
dns_zone = @dns.zone(@dns_zone_resource_name) if @dns_zone_resource_name
|
||||
return unless dns_zone && created_instance
|
||||
|
||||
retries = 0
|
||||
name = created_instance['name']
|
||||
change = dns_zone.remove(name, 'A')
|
||||
debug_logger("#{change.id} - #{change.started_at} - #{change.status} DNS address removed") if change
|
||||
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("DNS teardown failed, retrying error: #{e}")
|
||||
sleep 5
|
||||
retry if (retries += 1) < 30
|
||||
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
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'googleauth'
|
||||
require 'google/apis/compute_v1'
|
||||
require 'vmpooler/cloud_dns'
|
||||
require 'bigdecimal'
|
||||
require 'bigdecimal/util'
|
||||
require 'google/apis/compute_v1'
|
||||
require 'googleauth'
|
||||
require 'vmpooler/dns/base'
|
||||
require 'vmpooler/providers/base'
|
||||
|
||||
module Vmpooler
|
||||
|
|
@ -82,12 +82,9 @@ module Vmpooler
|
|||
return provider_config['machine_type'] if provider_config['machine_type']
|
||||
end
|
||||
|
||||
def domain
|
||||
provider_config['domain']
|
||||
end
|
||||
|
||||
def dns_zone_resource_name
|
||||
provider_config['dns_zone_resource_name']
|
||||
def domain(pool_name)
|
||||
dns_plugin_name = pool_config(pool_name)['dns_plugin']
|
||||
dns_config(dns_plugin_name)
|
||||
end
|
||||
|
||||
# Base methods that are implemented:
|
||||
|
|
@ -191,7 +188,7 @@ module Vmpooler
|
|||
boot: true,
|
||||
initialize_params: init_params
|
||||
)
|
||||
append_domain = domain || global_config[:config]['domain']
|
||||
append_domain = domain(pool_name)
|
||||
fqdn = "#{new_vmname}.#{append_domain}" if append_domain
|
||||
|
||||
# Assume all pool config is valid i.e. not missing
|
||||
|
|
@ -208,9 +205,12 @@ module Vmpooler
|
|||
debug_logger('trigger insert_instance')
|
||||
result = connection.insert_instance(project, zone(pool_name), client)
|
||||
wait_for_operation(project, pool_name, result)
|
||||
created_instance = get_vm(pool_name, new_vmname)
|
||||
dns_setup(created_instance)
|
||||
created_instance
|
||||
get_vm(pool_name, new_vmname)
|
||||
end
|
||||
|
||||
def get_vm_ip_address(vm_name, pool_name)
|
||||
vm_object = get_vm(pool_name, vm_name)
|
||||
vm_object['ip']
|
||||
end
|
||||
|
||||
# create_disk creates an additional disk for an existing VM. It will name the new
|
||||
|
|
@ -424,10 +424,8 @@ module Vmpooler
|
|||
|
||||
unless deleted
|
||||
debug_logger("trigger delete_instance #{vm_name}")
|
||||
vm_hash = get_vm(pool_name, vm_name)
|
||||
result = connection.delete_instance(project, zone(pool_name), vm_name)
|
||||
wait_for_operation(project, pool_name, result, 10)
|
||||
dns_teardown(vm_hash)
|
||||
end
|
||||
|
||||
# list and delete any leftover disk, for instance if they were detached from the instance
|
||||
|
|
@ -462,10 +460,12 @@ module Vmpooler
|
|||
true
|
||||
end
|
||||
|
||||
def vm_ready?(_pool_name, vm_name)
|
||||
def vm_ready?(pool_name, vm_name)
|
||||
debug_logger('vm_ready?')
|
||||
begin
|
||||
# TODO: we could use a healthcheck resource attached to instance
|
||||
open_socket(vm_name, domain || global_config[:config]['domain'])
|
||||
domain = domain(pool_name)
|
||||
open_socket(vm_name, domain)
|
||||
rescue StandardError => _e
|
||||
return false
|
||||
end
|
||||
|
|
@ -497,9 +497,6 @@ module Vmpooler
|
|||
|
||||
debug_logger("trigger async delete_instance #{vm.name}")
|
||||
result = connection.delete_instance(project, zone, vm.name)
|
||||
vm_pool = vm.labels&.key?('pool') ? vm.labels['pool'] : nil
|
||||
existing_vm = generate_vm_hash(vm, vm_pool)
|
||||
dns_teardown(existing_vm)
|
||||
result_list << result
|
||||
end
|
||||
# now check they are done
|
||||
|
|
@ -560,16 +557,6 @@ module Vmpooler
|
|||
|
||||
# END BASE METHODS
|
||||
|
||||
def dns_setup(created_instance)
|
||||
dns = Vmpooler::PoolManager::CloudDns.new(project, dns_zone_resource_name)
|
||||
dns.dns_create_or_replace(created_instance)
|
||||
end
|
||||
|
||||
def dns_teardown(created_instance)
|
||||
dns = Vmpooler::PoolManager::CloudDns.new(project, dns_zone_resource_name)
|
||||
dns.dns_teardown(created_instance)
|
||||
end
|
||||
|
||||
def should_be_ignored(item, allowlist)
|
||||
return false if allowlist.nil?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
MockDNS = Struct.new(
|
||||
# https://rubydoc.info/gems/google-cloud-dns/0.35.1/Google/Cloud/Dns
|
||||
:change, :credentials, :project, :record, :zone,
|
||||
keyword_init: true
|
||||
) do
|
||||
def zone(zone)
|
||||
self.zone = zone
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,6 @@ require 'vmpooler'
|
|||
require 'redis'
|
||||
require 'vmpooler/metrics'
|
||||
require 'computeservice_helper'
|
||||
require 'dnsservice_helper'
|
||||
|
||||
def project_root_dir
|
||||
File.dirname(File.dirname(__FILE__))
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ describe 'Vmpooler::PoolManager::Provider::Gce' do
|
|||
:config:
|
||||
max_tries: 3
|
||||
retry_factor: 10
|
||||
:dns_configs:
|
||||
:gcp-clouddns:
|
||||
project: vmpooler-test
|
||||
domain: vmpooler.example.com
|
||||
dns_zone_resource_name: vmpooler-example-com
|
||||
:providers:
|
||||
:gce:
|
||||
connection_pool_timeout: 1
|
||||
|
|
@ -32,6 +37,7 @@ describe 'Vmpooler::PoolManager::Provider::Gce' do
|
|||
timeout: 10
|
||||
ready_ttl: 1440
|
||||
provider: 'gce'
|
||||
dns_config: 'gcp-clouddns'
|
||||
machine_type: 'zones/#{zone}/machineTypes/e2-micro'
|
||||
EOT
|
||||
)
|
||||
|
|
@ -51,8 +57,6 @@ EOT
|
|||
|
||||
subject { Vmpooler::PoolManager::Provider::Gce.new(config, logger, metrics, redis_connection_pool, 'gce', provider_options) }
|
||||
|
||||
before(:each) { allow(subject).to receive(:dns).and_return(MockDNS.new()) }
|
||||
|
||||
describe '#name' do
|
||||
it 'should be gce' do
|
||||
expect(subject.name).to eq('gce')
|
||||
|
|
@ -61,7 +65,6 @@ EOT
|
|||
|
||||
describe '#manual tests live' do
|
||||
context 'in itsysops' do
|
||||
before(:each) { allow(subject).to receive(:dns).and_call_original }
|
||||
let(:vmname) { "instance-31" }
|
||||
let(:project) { 'vmpooler-test' }
|
||||
let(:config) { YAML.load(<<~EOT
|
||||
|
|
@ -69,14 +72,17 @@ EOT
|
|||
:config:
|
||||
max_tries: 3
|
||||
retry_factor: 10
|
||||
:dns_configs:
|
||||
:gcp-clouddns:
|
||||
project: vmpooler-test
|
||||
domain: vmpooler.example.com
|
||||
dns_zone_resource_name: vmpooler-example-com
|
||||
:providers:
|
||||
:gce:
|
||||
connection_pool_timeout: 1
|
||||
project: '#{project}'
|
||||
zone: '#{zone}'
|
||||
network_name: 'projects/itsysopsnetworking/global/networks/shared1'
|
||||
dns_zone_resource_name: 'vmpooler-test-puppet-net'
|
||||
domain: 'vmpooler-test.puppet.net'
|
||||
:pools:
|
||||
- name: '#{poolname}'
|
||||
alias: [ 'mockpool' ]
|
||||
|
|
@ -85,6 +91,7 @@ EOT
|
|||
timeout: 10
|
||||
ready_ttl: 1440
|
||||
provider: 'gce'
|
||||
dns_config: 'gcp-clouddns'
|
||||
subnetwork_name: 'projects/itsysopsnetworking/regions/us-west1/subnetworks/vmpooler-test'
|
||||
machine_type: 'zones/#{zone}/machineTypes/e2-micro'
|
||||
disk_type: 'pd-ssd'
|
||||
|
|
@ -92,10 +99,6 @@ EOT
|
|||
) }
|
||||
skip 'gets a vm' do
|
||||
result = subject.create_vm(poolname, vmname)
|
||||
#result = subject.destroy_vm(poolname, vmname)
|
||||
# subject.get_vm(poolname, vmname)
|
||||
subject.dns_teardown({'name' => vmname})
|
||||
# subject.dns_setup({'name' => vmname, 'ip' => '1.2.3.5'})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -267,7 +270,6 @@ EOT
|
|||
result = MockResult.new
|
||||
result.status = 'DONE'
|
||||
allow(connection).to receive(:insert_instance).and_return(result)
|
||||
allow(subject).to receive(:dns_setup).and_return(true)
|
||||
end
|
||||
|
||||
it 'should return a hash' do
|
||||
|
|
@ -314,7 +316,6 @@ EOT
|
|||
result.status = 'DONE'
|
||||
allow(subject).to receive(:wait_for_operation).and_return(result)
|
||||
allow(connection).to receive(:delete_instance).and_return(result)
|
||||
allow(subject).to receive(:dns_teardown).and_return(true)
|
||||
end
|
||||
|
||||
it 'should return true' do
|
||||
|
|
@ -349,7 +350,11 @@ EOT
|
|||
end
|
||||
|
||||
describe '#vm_ready?' do
|
||||
let(:domain) { nil }
|
||||
let(:domain) { 'vmpooler.example.com' }
|
||||
before(:each) do
|
||||
allow(subject).to receive(:domain).and_return('vmpooler.example.com')
|
||||
end
|
||||
|
||||
context 'When a VM is ready' do
|
||||
before(:each) do
|
||||
expect(subject).to receive(:open_socket).with(vmname, domain)
|
||||
|
|
@ -586,7 +591,6 @@ EOT
|
|||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:connect_to_gce).and_return(connection)
|
||||
allow(subject).to receive(:dns_teardown).and_return(true)
|
||||
end
|
||||
|
||||
context 'with empty allowlist' do
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ Gem::Specification.new do |s|
|
|||
s.require_paths = ["lib"]
|
||||
s.add_dependency "google-apis-compute_v1", "~> 0.14"
|
||||
s.add_dependency "googleauth", ">= 0.16.2", "< 1.3.0"
|
||||
s.add_dependency "google-cloud-dns", "~> 0.35.1"
|
||||
s.add_dependency 'vmpooler', '>= 1.3.0', '~> 2.3'
|
||||
s.add_dependency 'vmpooler', '~> 3.0'
|
||||
|
||||
# Testing dependencies
|
||||
s.add_development_dependency 'climate_control', '>= 0.2.0'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue