-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhetzner.bb
executable file
·136 lines (120 loc) · 5.14 KB
/
hetzner.bb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env bb
(ns hetzner
(:require [babashka.fs :as fs]
[cheshire.core :as json]
[clojure.string :as str]
[babashka.http-client :as http]
[shell-smith.core :as smith]))
(def usage "Hetzner DNS Tool
Usage:
hetzner pull [options]
hetzner validate [options]
hetzner push [options]
hetzner -h | --help
hetzner --version
Options:
-h --help Show this help
-v --version Show version
-t --token=<token> Hetzner DNS API token
-z --zones=<zones> Zone names to operate on comma separated (if not specified, operates on all zones)
-e --endpoint=<endpoint> API endpoint [default: https://dns.hetzner.com/api]
")
(def version "0.1.0")
(defn make-auth-header [token]
{"Auth-API-Token" token})
(def post-header
{"Content-Type" "text/plain"})
(defn- get-zones [endpoint auth-header zone-names]
(let [url (str endpoint "/v1/zones")
response (http/get url
{:headers auth-header})
zones (-> response
:body
(json/parse-string true)
:zones)]
(if zone-names
(let [zone-set (set (str/split zone-names #"\s*,\s*"))]
(filter #(zone-set (:name %)) zones))
zones)))
(defn- export-zone [endpoint auth-header zone]
(let [zone-id (:id zone)
zone-name (:name zone)
url (str endpoint "/v1/zones/" zone-id "/export")
response (http/get url {:headers auth-header})]
(if (#{200} (:status response))
(let [zone-content (:body response)]
(spit (str zone-name ".zone") zone-content)
(println "Exported zone" zone-name "to" (str zone-name ".zone")))
(println "Failed to export zone" zone-name ":" (:stderr response)))))
(defn- validate-zone-file [endpoint auth-header file-path]
(let [url (str endpoint "/v1/zones/file/validate")
response (http/post url
{:headers (merge auth-header post-header)
:body (fs/file file-path)})]
(#{200} (:status response))))
(defn validate-command [{:keys [endpoint token] zone-names :zones :as opts}]
(if-not token
(println "Error: Auth token is required")
(let [auth-header (make-auth-header token)]
(let [zones (get-zones endpoint auth-header zone-names)]
(if (empty? zones)
(println (if zone-names
(str "No zones found with names: " zone-names)
"No zones found"))
(doseq [zone zones]
(let [file-path (str (:name zone) ".zone")]
(if (fs/exists? file-path)
(if (validate-zone-file endpoint auth-header file-path)
(println "Zone file" file-path "is valid")
(println "Zone file" file-path "is invalid"))
(println "Zone file" file-path "does not exist")))))))))
(defn- import-zone [endpoint auth-header zone file-path]
(let [zone-id (:id zone)
zone-name (:name zone)
url (str endpoint "/v1/zones/" zone-id "/import")
response (http/post url
{:headers (merge auth-header post-header)
:body (fs/file file-path)})]
(if (#{200} (:status response))
(println "Successfully updated" zone-name "with" file-path)
(println "Failed to import zone" zone-name ":" (:stderr response)))))
(defn pull-command [{:keys [endpoint token] zone-names :zones :as opts}]
(if-not token
(println "Error: Auth token is required")
(let [auth-header (make-auth-header token)]
(let [zones (get-zones endpoint auth-header zone-names)]
(if (empty? zones)
(println (if zone-names
(str "No zones found with names: " zone-names)
"No zones found"))
(doseq [zone zones]
(export-zone endpoint auth-header zone)))))))
(defn push-command [{:keys [endpoint token] zone-names :zones :as opts}]
(if-not token
(println "Error: Auth token is required")
(let [auth-header (make-auth-header token)]
(let [zones (get-zones endpoint auth-header zone-names)]
(if (empty? zones)
(println (if zone-names
(str "No zones found with names: " zone-names)
"No zones found"))
(doseq [zone zones]
(let [file-path (str (:name zone) ".zone")]
(if (fs/exists? file-path)
(if (validate-zone-file endpoint auth-header file-path)
(do
(println "Zone file" file-path "is valid")
(import-zone endpoint auth-header zone file-path))
(println "Zone file" file-path "is invalid"))
(println "Zone file" file-path "does not exist")))))))))
(defn -main [& args]
(let [config (smith/config usage)]
(cond
(:help config) (println usage)
(:version config) (println "Hetzner Tool " version)
(:pull config) (pull-command config)
(:push config) (push-command config)
(:validate config) (validate-command config)
:else (println usage))))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))