garage/script/jepsen.garage/src/jepsen/garage/grg.clj
Alex 84d43501ce
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
refactor jepsen setup logic
2023-10-18 15:34:12 +02:00

179 lines
5.5 KiB
Clojure

(ns jepsen.garage.grg
(:require [clojure.tools.logging :refer :all]
[jepsen [control :as c]
[core :as jepsen]
[db :as db]]
[jepsen.control.util :as cu]
[amazonica.aws.s3 :as s3]
[slingshot.slingshot :refer [try+]]))
; CONSTANTS -- HOW GARAGE IS SET UP
(def dir "/opt/garage")
(def data-dir (str dir "/data"))
(def meta-dir (str dir "/meta"))
(def binary (str dir "/garage"))
(def logfile (str dir "/garage.log"))
(def pidfile (str dir "/garage.pid"))
(def grg-admin-token "icanhazadmin")
(def grg-key "jepsen")
(def grg-bucket "jepsen")
; THE GARAGE DB
(defn install!
"Download and install Garage"
[node version]
(c/su
(c/trace
(info node "installing garage" version)
(c/exec :mkdir :-p dir)
(let [url (str "https://garagehq.deuxfleurs.fr/_releases/" version "/x86_64-unknown-linux-musl/garage")
cache (cu/cached-wget! url)]
(c/exec :cp cache binary))
(c/exec :chmod :+x binary))))
(defn configure!
"Configure Garage"
[node]
(c/su
(c/trace
(cu/write-file!
(str "rpc_secret = \"0fffabe52542c2b89a56b2efb7dfd477e9dafb285c9025cbdf1de7ca21a6b372\"\n"
"rpc_bind_addr = \"0.0.0.0:3901\"\n"
"rpc_public_addr = \"" node ":3901\"\n"
"db_engine = \"lmdb\"\n"
"replication_mode = \"3\"\n"
"data_dir = \"" dir "/data\"\n"
"metadata_dir = \"" dir "/meta\"\n"
"[s3_api]\n"
"s3_region = \"us-east-1\"\n"
"api_bind_addr = \"0.0.0.0:3900\"\n"
"[k2v_api]\n"
"api_bind_addr = \"0.0.0.0:3902\"\n"
"[admin]\n"
"api_bind_addr = \"0.0.0.0:3903\"\n"
"admin_token = \"" grg-admin-token "\"\n")
"/etc/garage.toml"))))
(defn connect-node!
"Connect a Garage node to the rest of the cluster"
[test node]
(c/trace
(let [node-id (c/exec binary :node :id :-q)]
(info node "node id:" node-id)
(c/on-many (:nodes test)
(c/exec binary :node :connect node-id)))))
(defn configure-node!
"Configure a Garage node to be part of a cluster layout"
[test node]
(c/trace
(let [node-id (c/exec binary :node :id :-q)]
(c/on (jepsen/primary test)
(c/exec binary :layout :assign (subs node-id 0 16) :-c :1G :-z :dc1 :-t node)))))
(defn finalize-config!
"Apply the layout and create a key/bucket pair in the cluster"
[node]
(c/trace
(c/exec binary :layout :apply :--version 1)
(info node "garage status:" (c/exec binary :status))
(c/exec binary :key :create grg-key)
(c/exec binary :bucket :create grg-bucket)
(c/exec binary :bucket :allow :--read :--write grg-bucket :--key grg-key)
(info node "key info: " (c/exec binary :key :info grg-key))))
(defn db
"Garage DB for a particular version"
[version]
(reify db/DB
(setup! [_ test node]
(install! node version)
(configure! node)
(cu/start-daemon!
{:logfile logfile
:pidfile pidfile
:chdir dir}
binary
:server)
(c/exec :sleep 3)
(jepsen/synchronize test)
(connect-node! test node)
(jepsen/synchronize test)
(configure-node! test node)
(jepsen/synchronize test)
(when (= node (jepsen/primary test))
(finalize-config! node)))
(teardown! [_ test node]
(info node "tearing down garage" version)
(c/su
(cu/stop-daemon! binary pidfile)
(c/exec :rm :-rf data-dir)
(c/exec :rm :-rf meta-dir)))
db/LogFiles
(log-files [_ test node]
[logfile])))
; GARAGE S3 HELPER FUNCTIONS
(defn s3-creds
"Get S3 credentials for node"
[node]
(let [key-info (c/on node (c/exec binary :key :info grg-key :--show-secret))
[_ ak sk] (re-matches
#"(?s).*Key ID: (.*)\nSecret key: (.*)\nCan create.*"
key-info)]
{:access-key ak
:secret-key sk
:endpoint (str "http://" node ":3900")
:bucket grg-bucket
:client-config {:path-style-access-enabled true}}))
(defn s3-get
"Helper for GetObject"
[creds k]
(try+
(-> (s3/get-object creds (:bucket creds) k)
:input-stream
slurp)
(catch (re-find #"Key not found" (.getMessage %)) ex
nil)))
(defn s3-put
"Helper for PutObject or DeleteObject (is a delete if value is nil)"
[creds k v]
(if (= v nil)
(s3/delete-object creds
:bucket-name (:bucket creds)
:key k)
(let [some-bytes (.getBytes v "UTF-8")
bytes-stream (java.io.ByteArrayInputStream. some-bytes)]
(s3/put-object creds
:bucket-name (:bucket creds)
:key k
:input-stream bytes-stream
:metadata {:content-length (count some-bytes)}))))
(defn s3-list
"Helper for ListObjects -- just lists everything in the bucket"
[creds prefix]
(defn list-inner [ct accum]
(let [list-result (s3/list-objects-v2 creds
{:bucket-name (:bucket creds)
:prefix prefix
:continuation-token ct})
new-object-summaries (:object-summaries list-result)
new-objects (map (fn [d] (:key d)) new-object-summaries)
objects (concat new-objects accum)]
(if (:truncated? list-result)
(list-inner (:next-continuation-token list-result) objects)
objects)))
(list-inner nil []))