Files
ubicloud/prog/dns_zone/dns_zone_nexus.rb
Enes Cakir 1d6efd4c9b Get semaphores from the subject of prog
Right now, we define semaphores in both the model and prog, but they are
generally the same. We can retrieve the list of semaphores from the
subject in prog.

We can still define semaphores in prog if it doesn't have a subject,
like in Prog::Test::HetznerServer.
2024-12-26 12:14:33 +03:00

86 lines
2.9 KiB
Ruby

# frozen_string_literal: true
require_relative "../../lib/util"
class Prog::DnsZone::DnsZoneNexus < Prog::Base
subject_is :dns_zone
label def wait
if dns_zone.last_purged_at < Time.now - 60 * 60 * 1 # ~1 hour
register_deadline("wait", 5 * 60)
hop_purge_dns_records
end
when_refresh_dns_servers_set? do
register_deadline("wait", 5 * 60)
hop_refresh_dns_servers
end
nap 10
end
label def refresh_dns_servers
decr_refresh_dns_servers
dns_zone.dns_servers.each do |dns_server|
records_to_rectify = dns_zone.records_dataset
.left_join(:seen_dns_records_by_dns_servers, dns_record_id: :id, dns_server_id: dns_server.id)
.where(Sequel[:seen_dns_records_by_dns_servers][:dns_record_id] => nil)
.order(Sequel.asc(:created_at)).all
next if records_to_rectify.empty?
commands = ["zone-abort #{dns_zone.name}", "zone-begin #{dns_zone.name}"]
records_to_rectify.each do |r|
commands << if r.tombstoned
"zone-unset #{dns_zone.name} #{r.name} #{r.ttl} #{r.type} #{r.data}"
else
"zone-set #{dns_zone.name} #{r.name} #{r.ttl} #{r.type} #{r.data}"
end
end
commands << "zone-commit #{dns_zone.name}"
dns_server.run_commands_on_all_vms(commands)
DB[:seen_dns_records_by_dns_servers].multi_insert(records_to_rectify.map { {dns_record_id: _1.id, dns_server_id: dns_server.id} })
end
hop_wait
end
label def purge_dns_records
# These are the records that are obsoleted by a another record with the
# same fields but newer date. We can delete them even if they are not
# seen yet.
obsoleted_records = dns_zone.records_dataset
.join(
dns_zone.records_dataset
.select_group(:dns_zone_id, :name, :type, :data)
.select_append { max(created_at).as(:latest_created_at) }
.as(:latest_dns_record),
[:dns_zone_id, :name, :type, :data]
)
.where { dns_record[:created_at] < latest_dns_record[:latest_created_at] }.all
# These are the tombstoned records, we can only delete them if they are
# seen by all DNS servers. We join with seen_dns_records_by_dns_servers
# and count the number of DNS servers to ensure that they are seen by all
# DNS servers.
dns_server_ids = dns_zone.dns_servers.map(&:id)
seen_tombstoned_records = dns_zone.records_dataset
.select_group(:id)
.join(:seen_dns_records_by_dns_servers, dns_record_id: :id, dns_server_id: dns_server_ids)
.where(tombstoned: true)
.having { count.function.* =~ dns_server_ids.count }.all
records_to_purge = obsoleted_records + seen_tombstoned_records
DB[:seen_dns_records_by_dns_servers].where(dns_record_id: records_to_purge.map(&:id).uniq).delete(force: true)
records_to_purge.uniq(&:id).map(&:destroy)
dns_zone.last_purged_at = Time.now
dns_zone.save_changes
hop_wait
end
end