module GClouder::Resources::DNS::Records

Public Class Methods

abort_transaction(args, project_id) click to toggle source
# File lib/gclouder/resources/dns.rb, line 257
def self.abort_transaction(args, project_id)
  info "aborting dns record-set transaction", indent: 4
  gcloud "dns record-sets transaction abort #{args}", project_id: project_id
  # FIXME: remove transaction file..
end
add_record_set(name, value, zone, type, ttl, project_id) click to toggle source

FIXME: if a record exists but ttl or ip are different, an update should be performed

# File lib/gclouder/resources/dns.rb, line 273
def self.add_record_set(name, value, zone, type, ttl, project_id)
  if record_exists?(project_id, zone, name, type)
    good "#{name} IN #{type} #{value} #{ttl}", indent: 4
    return
  end

  add "#{name} IN #{type} #{value} #{ttl}", indent: 4

  gcloud "dns record-sets transaction add --name=#{name} --zone=#{zone} --type=#{type} --ttl=#{ttl} #{value}", project_id: project_id
end
dependencies() click to toggle source
# File lib/gclouder/resources/dns.rb, line 333
def self.dependencies
  return unless project.key?("dns")
  return unless project["dns"].key?("zones")

  project["dns"]["zones"].each do |zone, zone_config|
    project_id = zone_project_id(zone_config)
    zone_name  = zone.tr(".", "-")

    # skip zone unless manage_nameservers is true
    next unless zone_config.key?("manage_nameservers")
    next unless zone_config["manage_nameservers"]

    # parent zone data
    parent_zone = zone.split(".")[1..-1].join(".")
    parent_zone_name = parent_zone.tr(".", "-")

    parent_zone_config = project["dns"]["zones"][parent_zone]

    # get project_id for parent zone - if it isn't set then assume the zone exists in current project
    parent_project_id = parent_zone_config.key?("project_id") ? parent_zone_config["project_id"] : project_id

    info "ensuring nameservers for zone: #{zone}, project_id: #{parent_project_id}, parent_zone: #{parent_zone}"

    next if cli_args[:dry_run]

    # find nameservers for this zone
    nameservers = zone_nameservers(project_id, zone_name)

    # ensure parent zone exists
    create_zone(parent_project_id, parent_zone, parent_zone_name)

    # create nameservers in parent zone
    start_transaction(parent_project_id, parent_zone_name)
    add_record_set zone, nameservers.join(" "), parent_zone_name, "NS", 600, parent_project_id
    execute_transaction(parent_project_id, parent_zone_name)
  end
end
describe_zone(project_id, zone_name) click to toggle source
# File lib/gclouder/resources/dns.rb, line 323
def self.describe_zone(project_id, zone_name)
  gcloud "--format json dns managed-zones describe #{zone_name}", project_id: project_id, force: true
end
ensure(project_id, zone) click to toggle source
# File lib/gclouder/resources/dns.rb, line 210
def self.ensure(project_id, zone)
  return unless zone.key?("records")

  start_transaction(project_id, zone["name"])

  zone["records"].each do |record|
    next unless record_is_valid(record)

    values = []

    if record.key?("value") && record["value"].is_a?(Array)
        values << record["value"].join(" ")

    elsif record.key?("value") && record["value"].is_a?(String)
        values << record["value"]

    elsif record.key?("static_ips")
      record["static_ips"].each do |ip|
        values << static_ip(project_id, zone["name"], ip)
      end

    else
      bad "no 'value' or 'static_ips' key found for record: #{record["name"]}"
      fatal "failure due to invalid config"
    end

    values.each do |value|
      unless record["name"].match(/\.$/)
        bad "record name missing '.' suffix: #{record["name"]}"
        fatal "failure due to invalid config"
      end
      ttl = record.key?("ttl") ? record["ttl"] : "300"
      add_record_set record["name"], value, zone["name"], record["type"], ttl, project_id
    end
  end

  execute_transaction(project_id, zone["name"])
end
execute_transaction(project_id, zone_name) click to toggle source
# File lib/gclouder/resources/dns.rb, line 253
def self.execute_transaction(project_id, zone_name)
  gcloud "dns record-sets transaction execute --zone=#{zone_name}", project_id: project_id
end
lookup_ip(name, context) click to toggle source
# File lib/gclouder/resources/dns.rb, line 288
def self.lookup_ip(name, context)
  args = context == "global" ? "--global" : "--regions #{context}"
  ip = gcloud("compute addresses list #{name} #{args}", force: true)
  return false if ip.empty?
  ip[0]["address"]
end
record_exists?(project_id, zone, name, type) click to toggle source
# File lib/gclouder/resources/dns.rb, line 284
def self.record_exists?(project_id, zone, name, type)
  Resource.resource?("dns record-sets", name, "--zone=#{zone}", filter: "name = #{name} AND type = #{type}", project_id: project_id, silent: true)
end
record_is_valid(record) click to toggle source
# File lib/gclouder/resources/dns.rb, line 263
def self.record_is_valid(record)
  if record["type"] == "CNAME" && !record["value"].end_with?(".")
    info "CNAME value must end with '.'"
    return false
  end

  true
end
start_transaction(project_id, zone_name) click to toggle source
# File lib/gclouder/resources/dns.rb, line 249
def self.start_transaction(project_id, zone_name)
  gcloud "dns record-sets transaction start --zone=#{zone_name}", project_id: project_id
end
static_ip(project_id, zone_name, static_ip_config) click to toggle source
# File lib/gclouder/resources/dns.rb, line 295
def self.static_ip(project_id, zone_name, static_ip_config)
  %w(name context).each do |key|
    unless static_ip_config[key]
      bad "missing key '#{key}' for record"
      abort_transaction "--zone=#{zone_name}", project_id
      fatal "failure due to invalid config"
    end
  end

  name = static_ip_config["name"]
  context = static_ip_config["context"]

  ip = lookup_ip(name, context)

  unless ip
    unless cli_args[:dry_run]
      bad "ip address not found for context/name: #{context}/#{name}"
      abort_transaction "--zone=#{zone_name}", project_id
      fatal "failure due to invalid config"
    end

    # on dry runs assume the ip address has not been created but config is valid
    ip = "<#{context}/#{name}>"
  end

  ip
end
zone_nameservers(project_id, zone_name) click to toggle source
# File lib/gclouder/resources/dns.rb, line 327
def self.zone_nameservers(project_id, zone_name)
  remote_zone_definition = describe_zone(project_id, zone_name)
  fatal "nameservers not found for zone: #{zone_name}" unless remote_zone_definition.key?("nameServers")
  remote_zone_definition["nameServers"]
end