garage/script/jepsen.garage/src/jepsen/garage.clj

170 lines
6.7 KiB
Clojure

(ns jepsen.garage
(:require [clojure.tools.logging :refer :all]
[clojure.string :as str]
[jepsen [checker :as checker]
[cli :as cli]
[client :as client]
[control :as c]
[db :as db]
[generator :as gen]
[nemesis :as nemesis]
[tests :as tests]]
[jepsen.checker.timeline :as timeline]
[jepsen.control.util :as cu]
[jepsen.os.debian :as debian]
[knossos.model :as model]
[slingshot.slingshot :refer [try+]]
[amazonica.aws.s3 :as s3]
[amazonica.aws.s3transfer :as s3transfer]))
(def dir "/opt/garage")
(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")
(def grg-object "1")
(defn db
"Garage DB for a particular version"
[version]
(reify db/DB
(setup! [_ test node]
(info node "installing garage" version)
(c/su
(c/exec :mkdir :-p dir)
(let [url (str "https://garagehq.deuxfleurs.fr/_releases/" version "/x86_64-unknown-linux-musl/garage")
cache (cu/wget! url)]
(c/exec :cp cache binary))
(c/exec :chmod :+x binary)
(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")
(cu/start-daemon!
{:logfile logfile
:pidfile pidfile
:chdir dir}
binary
:server)
(Thread/sleep 100)
(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))
(c/exec binary :layout :assign (subs node-id 0 16) :-c 1 :-z :dc1 :-t node))
(if (= node (first (:nodes test)))
(do
(Thread/sleep 2000)
(c/exec binary :layout :apply :--version 1)
(info node "garage status:" (c/exec binary :status))
(c/exec binary :key :new :--name 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))))))
(teardown! [_ test node]
(info node "tearing down garage" version)
(c/su
(cu/stop-daemon! binary pidfile)
(c/exec :rm :-rf dir)))
db/LogFiles
(log-files [_ test node]
[logfile])))
(defn op-get [_ _] {:type :invoke, :f :read, :value nil})
(defn op-put [_ _] {:type :invoke, :f :write, :value (str (rand-int 9))})
(defn op-del [_ _] {:type :invoke, :f :write, :value nil})
(defrecord Client [creds]
client/Client
(open! [this test node]
(let [key-info (c/on node (c/exec binary :key :info grg-key))
[_ ak sk] (re-matches
#"(?s).*Key ID: (.*)\nSecret key: (.*)\nCan create.*"
key-info)
creds {:access-key ak
:secret-key sk
:endpoint (str "http://" node ":3900")
:client-config {:path-style-access-enabled true}}]
(info node "s3 credentials:" creds)
(assoc this :creds creds)))
(setup! [this test])
(invoke! [this test op]
(case (:f op)
:read (try+
(let [value
(-> (s3/get-object (:creds this) grg-bucket grg-object)
:input-stream
slurp)]
(assoc op :type :ok, :value value))
(catch (re-find #"Key not found" (.getMessage %)) ex
(assoc op :type :ok, :value nil)))
:write
(if (= (:value op) nil)
(do
(s3/delete-object (:creds this)
:bucket-name grg-bucket
:key grg-object)
(assoc op :type :ok, :value nil))
(let [some-bytes (.getBytes (:value op) "UTF-8")
bytes-stream (java.io.ByteArrayInputStream. some-bytes)]
(s3/put-object (:creds this)
:bucket-name grg-bucket
:key grg-object
:input-stream bytes-stream
:metadata {:content-length (count some-bytes)})
(assoc op :type :ok)))))
(teardown! [this test])
(close! [this test]))
(defn garage-test
"Given an options map from the command line runner (e.g. :nodes, :ssh,
:concurrency, ...), constructs a test map."
[opts]
(merge tests/noop-test
opts
{:pure-generators true
:name "garage"
:os debian/os
:db (db "v0.8.2")
:client (Client. nil)
:nemesis (nemesis/partition-random-halves)
:checker (checker/compose
{:perf (checker/perf)
:timeline (timeline/html)
:linear (checker/linearizable
{:model (model/register)
:algorithm :linear})})
:generator (->> (gen/mix [op-get op-put op-del])
(gen/stagger 0.02)
(gen/nemesis nil)
; (gen/nemesis
; (cycle [(gen/sleep 5)
; {:type :info, :f :start}
; (gen/sleep 5)
; {:type :info, :f :stop}]))
(gen/time-limit (+ (:time-limit opts) 5)))}))
(defn -main
"Handles command line arguments. Can either run a test, or a web server for
browsing results."
[& args]
(cli/run! (merge (cli/single-test-cmd {:test-fn garage-test})
(cli/serve-cmd))
args))