It hasn't been necessary to use create_with_id since
ebc79622df
, in December 2024.
I have plans to introduce:
```ruby
def create_with_id(id, values)
obj = new(values)
obj.id = id
obj.save_changes
end
```
This will make it easier to use the same id when creating
multiple objects. The first step is removing the existing
uses of create_with_id.
169 lines
4.7 KiB
Ruby
169 lines
4.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Prog::DnsZone::SetupDnsServerVm < Prog::Base
|
|
subject_is :vm, :sshable
|
|
|
|
def self.assemble(dns_server_id, name: nil, vm_size: "standard-2", storage_size_gib: 30, location_id: Location::HETZNER_FSN1_ID)
|
|
unless (dns_server = DnsServer[dns_server_id])
|
|
fail "No existing Dns Server"
|
|
end
|
|
|
|
unless Project[Config.dns_service_project_id]
|
|
fail "No existing Project"
|
|
end
|
|
|
|
unless Location[location_id]
|
|
fail "No existing Location"
|
|
end
|
|
|
|
# The .assemble function is meant to be run by an operator manually. If/when we want to make this more programmatic
|
|
# we should move this check to a pre-validation label of the prog.
|
|
fail "Existing DNS Server VMs are not in sync, try again later" unless vms_in_sync?(dns_server.vms)
|
|
|
|
name ||= "#{dns_server.ubid}-#{SecureRandom.alphanumeric(8).downcase}"
|
|
|
|
DB.transaction do
|
|
vm_st = Prog::Vm::Nexus.assemble_with_sshable(
|
|
Config.dns_service_project_id,
|
|
sshable_unix_user: "ubi",
|
|
location_id:,
|
|
name: name,
|
|
size: vm_size,
|
|
storage_volumes: [
|
|
{encrypted: true, size_gib: storage_size_gib}
|
|
],
|
|
boot_image: "ubuntu-jammy",
|
|
enable_ip4: true
|
|
)
|
|
|
|
Strand.create(prog: "DnsZone::SetupDnsServerVm", label: "start", stack: [{subject_id: vm_st.id, dns_server_id: dns_server_id}])
|
|
end
|
|
end
|
|
|
|
def self.vms_in_sync?(vms)
|
|
return true if vms.nil? || vms.empty?
|
|
|
|
outputs = vms.map do |vm|
|
|
lines = vm.sshable.cmd("sudo -u knot knotc", stdin: "zone-read --").split("\n")
|
|
|
|
lines.map do |line|
|
|
parts = line.split
|
|
# Serial number for the SOA record can vary, and it's normal so exclude that
|
|
if parts[3] == "SOA"
|
|
parts.delete_at(6)
|
|
parts.join(" ")
|
|
else
|
|
line
|
|
end
|
|
end
|
|
end
|
|
|
|
outputs.map(&:to_set).uniq.count == 1
|
|
end
|
|
|
|
def ds
|
|
@ds ||= DnsServer[frame["dns_server_id"]]
|
|
end
|
|
|
|
label def start
|
|
nap 5 unless vm.strand.label == "wait"
|
|
register_deadline(nil, 15 * 60)
|
|
hop_prepare
|
|
end
|
|
|
|
label def prepare
|
|
sshable.cmd <<~SH
|
|
sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf
|
|
sudo sed -i ':a;N;$!ba;s/127.0.0.1 localhost\\n\\n#/127.0.0.1 localhost\\n127.0.0.1 #{vm.inhost_name}\\n\\n#/' /etc/hosts
|
|
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
|
|
sudo apt-get update
|
|
sudo apt-get -y install apt-transport-https ca-certificates wget
|
|
sudo wget -O /usr/share/keyrings/cznic-labs-pkg.gpg https://pkg.labs.nic.cz/gpg
|
|
echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/knot-dns jammy main" | sudo tee /etc/apt/sources.list.d/cznic-labs-knot-dns.list
|
|
sudo apt-get update
|
|
sudo systemctl reboot
|
|
SH
|
|
|
|
hop_setup_knot
|
|
end
|
|
|
|
label def setup_knot
|
|
nap 5 unless sshable.available?
|
|
|
|
sshable.cmd <<~SH
|
|
sudo apt-get -y install knot
|
|
echo "KNOTD_ARGS="-C /var/lib/knot/confdb"" | sudo tee -a /etc/default/knot
|
|
SH
|
|
|
|
knot_config = <<-CONF
|
|
server:
|
|
rundir: "/run/knot"
|
|
user: "knot:knot"
|
|
listen: [ "0.0.0.0@53", "::@53" ]
|
|
|
|
log:
|
|
- target: "syslog"
|
|
any: "info"
|
|
|
|
database:
|
|
storage: "/var/lib/knot"
|
|
|
|
acl:
|
|
- id: "allow_dynamic_updates"
|
|
address: "127.0.0.1/32"
|
|
action: "update"
|
|
|
|
template:
|
|
- id: "default"
|
|
storage: "/var/lib/knot"
|
|
file: "%s.zone"
|
|
acl: "allow_dynamic_updates"
|
|
zonefile-sync: "60"
|
|
zonefile-load: "difference"
|
|
journal-content: "all"
|
|
|
|
|
|
zone:
|
|
#{ds.dns_zones.map { |dz| "- domain: \"#{dz.name}.\"" }.join("\n ")}
|
|
CONF
|
|
|
|
sshable.cmd("sudo tee /etc/knot/knot.conf > /dev/null", stdin: knot_config)
|
|
|
|
hop_sync_zones
|
|
end
|
|
|
|
label def sync_zones
|
|
nap 5 if ds.dns_zones.any?(&:refresh_dns_servers_set?)
|
|
|
|
ds.dns_zones.each do |dz|
|
|
zone_config = <<-CONF
|
|
#{dz.name}. 3600 SOA ns.#{dz.name}. #{dz.name}. 37 86400 7200 1209600 #{dz.neg_ttl}
|
|
#{dz.name}. 3600 NS #{ds.name}.
|
|
CONF
|
|
sshable.cmd("sudo -u knot tee /var/lib/knot/#{dz.name}.zone > /dev/null", stdin: zone_config)
|
|
end
|
|
|
|
sshable.cmd "sudo systemctl restart knot"
|
|
|
|
ds.dns_zones.each(&:purge_obsolete_records)
|
|
|
|
commands = ds.dns_zones.flat_map do |dz|
|
|
["zone-abort #{dz.name}", "zone-begin #{dz.name}"] +
|
|
dz.records.map do |r|
|
|
"zone-set #{dz.name} #{r.name} #{r.ttl} #{r.type} #{r.data}"
|
|
end + ["zone-commit #{dz.name}", "zone-flush #{dz.name}"]
|
|
end
|
|
|
|
# Put records
|
|
sshable.cmd("sudo -u knot knotc", stdin: commands.join("\n"))
|
|
|
|
hop_validate
|
|
end
|
|
|
|
label def validate
|
|
hop_sync_zones unless Prog::DnsZone::SetupDnsServerVm.vms_in_sync?(ds.vms + [vm])
|
|
ds.add_vm vm unless ds.vms.map(&:id).include? vm.id
|
|
pop "created VM for DnsServer"
|
|
end
|
|
end
|