Created
July 8, 2021 15:32
-
-
Save ytti/69e14c90ba15c6e86d6c3b6af5ee7475 to your computer and use it in GitHub Desktop.
CLI UX to Google Cloud DNS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require "google/cloud/dns" | |
require "commander" | |
#require "pry" | |
# | |
class GDNS | |
PROJECT = "resonant-petal-318815" | |
AUTH = File.join(Dir.home, ".config", "google_cloud_sdk_auth.json") | |
TTL = 1800 | |
MX = [ | |
"1 aspmx.l.google.com.", | |
"5 alt1.aspmx.l.google.com.", | |
"5 alt2.aspmx.l.google.com.", | |
"10 alt3.aspmx.l.google.com.", | |
"10 alt4.aspmx.l.google.com." | |
] | |
def initialize | |
auth | |
@dns = Google::Cloud::Dns.new | |
end | |
def get_zone(domain) | |
@dns.zone(Domain.new(domain).name) | |
end | |
def create_zone(domain) | |
@dns.create_zone(*Domain.new(domain).both) | |
end | |
def delete_zone(domain) | |
zone = get_zone(domain) | |
zone.delete(force: true) | |
end | |
def list_zones | |
@dns.zones | |
end | |
def export_zone(domain, file) | |
get_zone(domain).export(file) | |
end | |
def import_zone(domain, file) | |
get_zone(domain).import(file) | |
end | |
def create_zone_standard(domain, ip4=nil, ip6=nil) | |
domain = Domain.new(domain).domain | |
zone = create_zone(domain) | |
zone.update do |tx| | |
tx.add(domain, "A", TTL, ip4) if ip4 | |
tx.add(domain, "AAAA", TTL, ip6) if ip6 | |
tx.add("www", "CNAME", TTL, domain) | |
tx.add(domain, "MX", TTL, MX) | |
end | |
zone | |
end | |
def set(domain, key, type, values, replace: false, ttl: TTL) | |
zone = get_zone(domain) | |
zone.update do |tx| | |
if type.downcase == "txt" | |
values = values.map do |value| | |
value.chars.each_slice(255).map(&:join).map{|e|"\"#{e}\""}.join(" ") | |
end | |
end | |
cmd = replace ? :replace : :add | |
tx.send(cmd, key, type.upcase, ttl, values) | |
end | |
end | |
def del(domain, key, type) | |
zone = get_zone(domain) | |
zone.update do |tx| | |
tx.remove(key, type.upcase) | |
end | |
end | |
def dnssec(domain) | |
zone = get_zone(domain) | |
zone.gapi.dnssec_config.state = "on" | |
zone.update do |tx| | |
# binding.pry | |
end | |
end | |
def auth | |
Google::Cloud::Dns.configure do |config| | |
config.project_id = PROJECT | |
config.credentials = AUTH | |
end | |
end | |
class Domain | |
attr_reader :domain | |
def initialize(domain) | |
@domain = domain | |
@domain = @domain + "." unless @domain[-1] == "." | |
end | |
def name | |
@domain.tr(".", "-")[0..-2] | |
end | |
def both | |
[name, @domain] | |
end | |
end | |
end | |
class GNDS_CLI | |
include Commander::Methods | |
def run | |
program :name, "GDNS" | |
program :version, "1.0.0" | |
program :description, "Interface with google cloud DNS" | |
command :set do |c| | |
c.syntax = "gnds set $domain $key $type $value1 [$value2] |$valueN]" | |
c.description = "set example.com www A 192.0.2.42 192.0.2.222" | |
c.option("-r", "--replace", "Replace existing record") | |
c.action do |args, opt| | |
raise("need 4 or more arguments") unless args.size >= 4 | |
domain, key, type, *values = args | |
key = modkey(key, domain) | |
GDNS.new.set(domain, key, type, values, replace: opt.replace) | |
end | |
end | |
command :get do |c| | |
c.syntax = "gdns get $domain" | |
c.description = "get example.com" | |
c.action do |args, opt| | |
raise("need exactly 1 argument") unless args.size == 1 | |
print_zone(GDNS.new.get_zone(*args)) | |
end | |
end | |
command :del do |c| | |
c.syntax = "gnds del $domain $key $type" | |
c.description = "del example.com www A" | |
c.action do |args, opt| | |
raise("need exactly 3 arguments") unless args.size == 3 | |
domain, key, type = args | |
key = modkey(key, domain) | |
GDNS.new.del(domain, key, type) | |
end | |
end | |
command :create do |c| | |
c.syntax = "gnds create $domain [$ip4] [$ip6]" | |
c.description = "create example.com 192.0.2.42 2001:db8::42" | |
c.action do |args, opt| | |
raise("need 1-3 argument") unless (1..3) === args.size | |
print_zone(GDNS.new.create_zone_standard(*args)) | |
end | |
end | |
command :destroy do |c| | |
c.syntax = "gnds destroy $domain" | |
c.description = "destroy example.com" | |
c.action do |args, opt| | |
raise("need exactly 1 argument") unless args.size == 1 | |
GDNS.new.delete_zone(*args) | |
end | |
end | |
command :domains do |c| | |
c.syntax = "gdns domains" | |
c.description = "domains" | |
c.action do | |
puts GDNS.new.list_zones.map(&:dns) | |
end | |
end | |
command :export do |c| | |
c.syntax = "gdns export $domain $file" | |
c.description = "export example.com /tmp/example.zone" | |
c.action do |args, opt| | |
raise("need exactly 2 arguments") unless args.size == 2 | |
GDNS.new.export_zone(*args) | |
end | |
end | |
command :import do |c| | |
c.syntax = "gdns import $domain $file" | |
c.description = "import example.com /tmp/example.zone" | |
c.action do |args, opt| | |
raise("need exactly 2 arguments") unless args.size == 2 | |
GDNS.new.import_zone(*args) | |
end | |
end | |
def print_zone(zone) | |
indent_name = zone.records.max_by{|r|r.name.size}.name.size | |
indent_type = zone.records.max_by{|r|r.type.size}.type.size | |
indent = indent_name+indent_type+2 | |
zone.records.map do |r| | |
puts"%#{indent_name}s %#{indent_type}s %s\n" % [r.name, r.type, r.data.first] | |
r.data[1..-1].each { |data| puts " "*indent + data } | |
end | |
end | |
def modkey(key, domain) | |
domain = GDNS::Domain.new(domain).domain | |
key == "." ? domain : key | |
end | |
run! | |
end | |
end | |
begin | |
if __FILE__ == $0 | |
GNDS_CLI.new.run | |
end | |
rescue => error | |
warn error | |
#raise error | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment