# frozen_string_literal: true require "logger" require_relative "../helpers/hash_with_indifferent_access_custom" require_relative "../helpers/request_helper" module CoreDns class Etcd class Domain < CoreDns::Domain class Error < StandardError; end VALUES_WHITELIST = %w[host mail port priority text ttl group].freeze def delete(data = {}) data = HashWithIndifferentAccessCustom.new(data).attributes hostname = nil if data[:name] hostname = "#{data.delete(:name)}.#{@namespace}./#{@client.prefix}".split(".").compact.reverse.join("/") elsif data[:host] hostname = list.select { |record| record["host"] == data[:host] }[0]["hostname"] end remove(hostname) if hostname end def get(hostname) self.class.new(@client, "#{hostname}.#{@namespace}") end def add(data = {}) put(@namespace, HashWithIndifferentAccessCustom.new(data).attributes) end def show self.class.new(@client, "").list_all.select { |x| x["name"] == namespace }[0] end def list(level = 1) one_level_records(level).map do |record| hostname = record.delete("hostname") name = format_name hostname.split("/").reverse.reject(&:empty?).join(".") record.merge!({ "name" => name }) end end def one_level_records(level) fetch("").each do |record| levels_difference = (record["hostname"].sub("/#{@client.prefix}/#{@namespace.split(".").reverse.join("/")}", "")).split("/")[1..] next unless levels_difference.count == level record end end def list_all fetch("").map do |record| hostname = record.delete("hostname") name = format_name hostname.split("/").reverse.reject(&:empty?).join(".") record.merge!({ "name" => name }) end end private def format_name(name) [@namespace, @client.prefix].reject(&:empty?).each { |part| name.gsub!(".#{part}", "") } name end def allowed_values?(data) (data.keys - VALUES_WHITELIST).empty? ? false : true end def generate_postfix postfixes = list.collect { |record| record["name"] }.map { |key| key.split("/").last } if postfixes.empty? "#{@client.postfix}1" else ((1..postfixes.count + 1).map { |i| "#{@client.postfix}#{i + 1}" } - postfixes).first end end def fetch(hostname) hostname = [hostname, @namespace.split(".")].flatten.compact.join(".")[1..] unless @namespace == hostname payload = { key: Base64.encode64("/#{@client.prefix}/#{hostname.split(".").reverse.join("/")}/"), range_end: Base64.encode64("/#{@client.prefix}/#{hostname.split(".").reverse.join("/")}~") }.to_json response = CoreDns::Helpers::RequestHelper.request("#{@client.api_url}/kv/range", :post, {}, payload) parsed_response(response) rescue StandardError [] end def parsed_response(response) JSON.parse(response)["kvs"] .map do |encoded_hash| [{ "hostname" => Base64.decode64(encoded_hash["key"]) }, JSON.parse(Base64.decode64(encoded_hash["value"]))] .reduce( {}, :merge ) rescue StandardError next end.compact end def put(key, value) raise ArgumentError, "Unsupported values keys" unless allowed_values?(value) # [:txt].include?(record_type(value)) ? postfix = nil : postfix = "/#{generate_postfix}" postfix = "/#{generate_postfix}" key = "/#{@client.prefix}/#{key.split(".").reverse.join("/")}#{postfix}" payload = { key: Base64.encode64(key), value: Base64.encode64(value.to_json) }.to_json response = CoreDns::Helpers::RequestHelper.request("#{@client.api_url}/kv/put", :post, {}, payload) if response.code == 200 payload else response.code end end def remove(hostname) payload = { key: Base64.encode64(hostname) }.to_json response = CoreDns::Helpers::RequestHelper.request("#{@client.api_url}/kv/deleterange", :post, {}, payload) response.code == 200 ? hostname : response.code rescue StandardError => e @logger.error(e.message) end def record_type(record) require "resolv" if record.keys.include?("text") then :txt elsif record.keys.include?("mail") then :mx elsif record.keys.include?("port") then :srv elsif Resolv::IPv4::Regex.match?(record["host"]) then :a elsif Resolv::IPv6::Regex.match?(record["host"]) then :aaaa elsif record["name"].match?(/ns.dns/) then :ns else :cname end end end end end