about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2019-12-22T01·16+0000
committerVincent Ambo <tazjin@google.com>2020-04-20T21·39+0100
commit8c86b9b5f60af4d2bd16352ace7b19fdde04ffda (patch)
tree8ab32c4c09e7e2ce64562936bb323962e9b537c6
parentd6f5ca7cafcaad006697abedfb40c885427f3d58 (diff)
feat(defzone): Add an Emacs package for generating zone files r/641
This currently only supports a small subset of available records, but
I actually kind of like the nesting.
-rw-r--r--tools/emacs-pkgs/defzone/defzone.el48
-rw-r--r--tools/emacs-pkgs/defzone/example.el39
2 files changed, 87 insertions, 0 deletions
diff --git a/tools/emacs-pkgs/defzone/defzone.el b/tools/emacs-pkgs/defzone/defzone.el
new file mode 100644
index 000000000000..b67501cbd860
--- /dev/null
+++ b/tools/emacs-pkgs/defzone/defzone.el
@@ -0,0 +1,48 @@
+;;; defzone.el --- Generate zone files from Elisp  -*- lexical-binding: t; -*-
+
+(require 'dash)
+(require 'dash-functional)
+(require 's)
+
+(defun record-to-record (zone record &optional subdomain)
+  "Evaluate a record definition and turn it into a zone file
+  record in ZONE, optionally prefixed with SUBDOMAIN."
+
+  (declare (indent defun))             ; TODO(tazjin): remove
+  (let ((name (if subdomain (s-join "." (list subdomain zone)) zone)))
+    (pcase record
+      (`(SOA . (,ttl . (,mname ,rname ,serial ,refresh ,retry ,expire ,min)))
+       (format "%s %s IN SOA %s %s %s %s %s %s %s"
+               name ttl mname rname serial refresh retry expire min))
+
+      (`(NS . (,ttl . ,targets))
+       (->> targets
+            (-map (lambda (target) (format "%s %s IN NS %s" name ttl target)))
+            (s-join "\n")))
+
+      (`(MX . (,ttl . ,pairs))
+       (->> pairs
+            (-map (-lambda ((preference . exchange))
+                    (format "%s %s IN MX %s %s" name ttl preference exchange)))
+            (s-join "\n")))
+
+      (`(TXT ,ttl ,text) (format "%s %s IN TXT %s" name ttl (prin1-to-string text)))
+
+      (`(A . (,ttl . ,ips))
+       (->> ips
+            (-map (lambda (ip) (format "%s %s IN A %s" name ttl ip)))
+            (s-join "\n")))
+
+      (`(CNAME ,ttl ,target) (format "%s %s IN CNAME %s" name ttl target))
+
+      ((and `(,sub . ,records)
+            (guard (stringp sub)))
+       (s-join "\n" (-map (lambda (r) (record-to-record zone r sub)) records)))
+
+      (_ (error "Invalid record definition: %s" record)))))
+
+(defmacro defzone (fqdn &rest records)
+  "Generate zone file for the zone at FQDN from a simple DSL."
+  (declare (indent defun))
+
+  `(s-join "\n" (-map (lambda (r) (record-to-record ,fqdn r)) (quote ,records))))
diff --git a/tools/emacs-pkgs/defzone/example.el b/tools/emacs-pkgs/defzone/example.el
new file mode 100644
index 000000000000..6405eaf2f72c
--- /dev/null
+++ b/tools/emacs-pkgs/defzone/example.el
@@ -0,0 +1,39 @@
+;;; example.el - usage example for defzone macro
+
+(defzone "tazj.in."
+  (SOA 21600 "ns-cloud-a1.googledomains.com." "cloud-dns-hostmaster.google.com."
+       123 21600 3600 1209600 300)
+
+  (NS 21600
+      "ns-cloud-a1.googledomains.com."
+      "ns-cloud-a2.googledomains.com."
+      "ns-cloud-a3.googledomains.com."
+      "ns-cloud-a4.googledomains.com.")
+
+  (MX 300
+      (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."))
+
+  (TXT 3600 "google-site-verification=d3_MI1OwD6q2OT42Vvh0I9w2u3Q5KFBu-PieNUE1Fig")
+
+  (A 300 "34.98.120.189")
+
+  ;; Nested record sets are indicated by a list that starts with a
+  ;; string (this is just joined, so you can nest multiple levels at
+  ;; once)
+  ("blog"
+   ;; Blog "storage engine" is in a separate DNS zone
+   (NS 21600
+       "ns-cloud-c1.googledomains.com."
+       "ns-cloud-c2.googledomains.com."
+       "ns-cloud-c3.googledomains.com."
+       "ns-cloud-c4.googledomains.com."))
+
+  ("git"
+   (A 300 "34.98.120.189")
+   (TXT 300 "<3 edef"))
+
+  ("files" (CNAME 300 "c.storage.googleapis.com.")))