/
core.clj
141 lines (117 loc) · 5.03 KB
/
core.clj
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
137
138
139
140
141
(ns magma.core
(:require [clojure.java.io :as io]
[google-credentials.core :as g]
[tick.alpha.api :as t]
[environ.core :refer [env]])
(:import ;storage bucket
com.google.cloud.storage.Bucket
com.google.cloud.storage.BucketInfo
com.google.cloud.storage.Blob
com.google.cloud.storage.BlobId
com.google.cloud.storage.Storage$BucketTargetOption
com.google.cloud.storage.Storage
com.google.cloud.storage.StorageOptions
com.google.cloud.storage.Storage$BlobListOption
com.google.cloud.storage.Storage$BucketSourceOption
com.google.cloud.storage.Storage$BlobSourceOption
;firestore
com.google.firestore.admin.v1.DatabaseName
com.google.longrunning.Operation
com.google.cloud.firestore.v1.FirestoreAdminClient
com.google.cloud.firestore.v1.FirestoreAdminSettings
com.google.firestore.admin.v1.ExportDocumentsRequest
com.google.firestore.admin.v1.ImportDocumentsRequest
com.google.api.gax.core.FixedCredentialsProvider)
(:gen-class))
(def root (atom "magma-"))
(defn- project-id [] (env :google-cloud-project))
(defn- storage ^Storage []
(-> (. StorageOptions newBuilder)
(.setCredentials (g/load-credentials))
(.build)
(.getService)))
(defn- admin-settings ^FirestoreAdminSettings []
(-> (. FirestoreAdminSettings newBuilder)
(.setCredentialsProvider (FixedCredentialsProvider/create (g/load-credentials)))
(.build)))
(defn- admin-client ^FirestoreAdminClient []
(FirestoreAdminClient/create (admin-settings)))
(defn- export-request ^ExportDocumentsRequest [name bucket]
(-> (. ExportDocumentsRequest newBuilder)
(.setName name)
(.setOutputUriPrefix bucket)
(.build)))
(defn- import-request ^ImportDocumentsRequest [name bucket]
(-> (. ImportDocumentsRequest newBuilder)
(.setName name)
(.setInputUriPrefix bucket)
(.build)))
(defn- bucket-info ^BucketInfo [name] (BucketInfo/of name))
(defn- make-timestamp [folder-name]
(-> folder-name
(clojure.string/replace #"/(?!.*\1)" "")
(clojure.string/replace #"_(?!.*\1)" ".")
(t/instant)))
(defn- backup [bucket-uri]
(try
(let [client (admin-client)
name (DatabaseName/of (project-id) "firestore")]
(.exportDocuments client (export-request (.toString name) bucket-uri)))
(catch Exception e (str "Caught exception: " (.getMessage e)))))
(defn- restore [backup-uri]
(try
(let [client (admin-client)
name (DatabaseName/of (project-id) "firestore")]
(.importDocuments client (import-request (.toString name) backup-uri)))
(catch Exception e (str "Caught exception: " (.getMessage e)))))
(defn- list-backups []
(let [page (.list (storage) (str @root (project-id)) (into-array Storage$BlobListOption [(Storage$BlobListOption/currentDirectory)]))]
(let [data (seq (.iterateAll page))]
(sort-by :timestamp
(for [item data]
(let [mapped (bean item)]
{ :id (:blobId mapped)
:name (:name mapped)
:bucket (:bucket mapped)
:timestamp (make-timestamp (:name mapped))
:uri (str "gs://" (:bucket mapped) "/" (:name mapped))}))))))
(defn- last-backup []
(last (list-backups)))
(defn- list-sub-directory [folder]
(let [page (.list (storage) (str @root (project-id)) (into-array Storage$BlobListOption [(Storage$BlobListOption/prefix folder)]))]
(let [data (seq (.iterateAll page))]
(sort-by :name
(for [item data]
(let [mapped (bean item)]
{ :id (:blobId mapped)
:name (:name mapped)
:uri (str "gs://" (:bucket mapped) "/" (:name mapped))}))))))
(defn- delete-backup [folder]
(let [contents (list-sub-directory folder)]
(doseq [file contents]
(.delete (storage) (:id file) (into-array Storage$BlobSourceOption [])))))
(defn create-backup-bucket []
(.create (storage) (bucket-info (str @root (project-id))) (into-array Storage$BucketTargetOption [])))
(defn deleting-your-backup-bucket-is-an-extremely-bad-idea []
(let [backups (list-backups)]
(doseq [backup backups]
(delete-backup (:name backup)))
(Thread/sleep 5000)
(.delete (storage) (str @root (project-id)) (into-array Storage$BucketSourceOption []))))
(defn rename-root [prefix]
(let [clean-prefix (clojure.string/replace (str prefix) #"[!@#$%^&*\\\/]" "")]
(reset! root (str "magma-" clean-prefix "-"))))
(defn list-firestore-backups []
(list-backups))
(defn last-firestore-backup []
(last-backup))
(defn backup-firestore []
(backup (str "gs://" @root (project-id))))
(defn restore-firestore [backup-uri]
(restore backup-uri))
(defn roll-back-firestore []
(let [uri (:uri (last-firestore-backup))]
(Thread/sleep 60000)
(restore-firestore uri)))
(defn deleting-your-backups-is-a-really-bad-idea [backup-name]
(delete-backup backup-name))