Add initial impl for migrate-components-v2 manage.py command

This commit is contained in:
Andrey Antukh 2023-11-17 14:06:18 +01:00 committed by Andrés Moya
parent c948f1a087
commit 08166bcebf
5 changed files with 100 additions and 35 deletions

View file

@ -44,11 +44,16 @@ def send_eval(expr):
s.send(b":repl/quit\n\n") s.send(b":repl/quit\n\n")
with s.makefile() as f: with s.makefile() as f:
result = json.load(f) while True:
tag = result.get("tag", None) line = f.readline()
if tag != "ret": result = json.loads(line)
raise RuntimeError("unexpected response from PREPL") tag = result.get("tag", None)
return result.get("val", None), result.get("exception", None) if tag == "ret":
return result.get("val", None), result.get("exception", None)
elif tag == "out":
print(result.get("val"), end="")
else:
raise RuntimeError("unexpected response from PREPL")
def encode(val): def encode(val):
return json.dumps(json.dumps(val)) return json.dumps(json.dumps(val))
@ -60,7 +65,7 @@ def print_error(res):
def run_cmd(params): def run_cmd(params):
try: try:
expr = "(app.srepl.ext/run-json-cmd {})".format(encode(params)) expr = "(app.srepl.cli/exec {})".format(encode(params))
res, failed = send_eval(expr) res, failed = send_eval(expr)
if failed: if failed:
print_error(res) print_error(res)
@ -140,12 +145,22 @@ def derive_password(password):
res = run_cmd(params) res = run_cmd(params)
print(f"Derived password: \"{res}\"") print(f"Derived password: \"{res}\"")
def migrate_components_v2():
params = {
"cmd": "migrate-v2",
"params": {}
}
run_cmd(params)
available_commands = ( available_commands = (
"create-profile", "create-profile",
"update-profile", "update-profile",
"delete-profile", "delete-profile",
"search-profile", "search-profile",
"derive-password", "derive-password",
"migrate-components-v2",
) )
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@ -217,3 +232,8 @@ elif args.action == "search-profile":
email = input("Email: ") email = input("Email: ")
search_profile(email) search_profile(email)
elif args.action == "migrate-components-v2":
migrate_components_v2()

View file

@ -10,7 +10,7 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.common.spec :as us] [app.common.spec :as us]
[app.config :as cf] [app.config :as cf]
[app.srepl.ext] [app.srepl.cli]
[app.srepl.main] [app.srepl.main]
[app.util.json :as json] [app.util.json :as json]
[app.util.locks :as locks] [app.util.locks :as locks]

View file

@ -4,14 +4,16 @@
;; ;;
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.srepl.ext (ns app.srepl.cli
"PREPL API for external usage (CLI or ADMIN)" "PREPL API for external usage (CLI or ADMIN)"
(:require (:require
[app.auth :as auth] [app.auth :as auth]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.main :as main]
[app.rpc.commands.auth :as cmd.auth] [app.rpc.commands.auth :as cmd.auth]
[app.srepl.components-v2]
[app.util.json :as json] [app.util.json :as json]
[app.util.time :as dt] [app.util.time :as dt]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -21,18 +23,18 @@
(or (deref (requiring-resolve 'app.main/system)) (or (deref (requiring-resolve 'app.main/system))
(deref (requiring-resolve 'user/system)))) (deref (requiring-resolve 'user/system))))
(defmulti ^:private run-json-cmd* ::cmd) (defmulti ^:private exec-command ::cmd)
(defn run-json-cmd (defn exec
"Entry point with external tools integrations that uses PREPL "Entry point with external tools integrations that uses PREPL
interface for interacting with running penpot backend." interface for interacting with running penpot backend."
[data] [data]
(let [data (json/decode data) (let [data (json/decode data)]
params (merge {::cmd (keyword (:cmd data "default"))} (-> {::cmd (keyword (:cmd data "default"))}
(:params data))] (merge (:params data))
(run-json-cmd* params))) (exec-command))))
(defmethod run-json-cmd* :create-profile (defmethod exec-command :create-profile
[{:keys [fullname email password is-active] [{:keys [fullname email password is-active]
:or {is-active true}}] :or {is-active true}}]
(when-let [system (get-current-system)] (when-let [system (get-current-system)]
@ -46,7 +48,7 @@
(->> (cmd.auth/create-profile! conn params) (->> (cmd.auth/create-profile! conn params)
(cmd.auth/create-profile-rels! conn)))))) (cmd.auth/create-profile-rels! conn))))))
(defmethod run-json-cmd* :update-profile (defmethod exec-command :update-profile
[{:keys [fullname email password is-active]}] [{:keys [fullname email password is-active]}]
(when-let [system (get-current-system)] (when-let [system (get-current-system)]
(db/with-atomic [conn (:app.db/pool system)] (db/with-atomic [conn (:app.db/pool system)]
@ -67,7 +69,7 @@
{::db/return-keys? false})] {::db/return-keys? false})]
(pos? (:next.jdbc/update-count res)))))))) (pos? (:next.jdbc/update-count res))))))))
(defmethod run-json-cmd* :delete-profile (defmethod exec-command :delete-profile
[{:keys [email soft]}] [{:keys [email soft]}]
(when-not email (when-not email
(ex/raise :type :assertion (ex/raise :type :assertion
@ -87,7 +89,7 @@
{::db/return-keys? false}))] {::db/return-keys? false}))]
(pos? (:next.jdbc/update-count res)))))) (pos? (:next.jdbc/update-count res))))))
(defmethod run-json-cmd* :search-profile (defmethod exec-command :search-profile
[{:keys [email]}] [{:keys [email]}]
(when-not email (when-not email
(ex/raise :type :assertion (ex/raise :type :assertion
@ -101,11 +103,33 @@
" where email similar to ? order by created_at desc limit 100")] " where email similar to ? order by created_at desc limit 100")]
(db/exec! conn [sql email]))))) (db/exec! conn [sql email])))))
(defmethod run-json-cmd* :derive-password (defmethod exec-command :derive-password
[{:keys [password]}] [{:keys [password]}]
(auth/derive-password password)) (auth/derive-password password))
(defmethod run-json-cmd* :default (defmethod exec-command :migrate-v2
[_]
(letfn [(on-start [{:keys [total rollback]}]
(println
(str/ffmt "The components/v2 migration started (rollback:%, teams:%)"
(if rollback "on" "off")
total)))
(on-progress [{:keys [total elapsed progress completed]}]
(println (str/ffmt "Progress % (total: %, completed: %, elapsed: %)"
progress total completed elapsed)))
(on-error [cause]
(println "ERR:" (ex-message cause)))
(on-end [_]
(println "Migration finished"))]
(app.srepl.components-v2/migrate-teams! main/system
:on-start on-start
:on-error on-error
:on-progress on-progress
:on-end on-end)))
(defmethod exec-command :default
[{:keys [::cmd]}] [{:keys [::cmd]}]
(ex/raise :type :internal (ex/raise :type :internal
:code :not-implemented :code :not-implemented

View file

@ -41,21 +41,26 @@
:elapsed (dt/format-duration elapsed)))))) :elapsed (dt/format-duration elapsed))))))
(defn- report-progress-teams (defn- report-progress-teams
[tpoint] [tpoint on-progress]
(fn [_ _ oldv newv] (fn [_ _ oldv newv]
(when (not= (:processed/teams oldv) (when (not= (:processed/teams oldv)
(:processed/teams newv)) (:processed/teams newv))
(let [total (:total/teams newv) (let [total (:total/teams newv)
completed (:processed/teams newv) completed (:processed/teams newv)
progress (/ (* completed 100.0) total) progress (/ (* completed 100.0) total)
elapsed (tpoint)] progress (str (int progress) "%")
elapsed (dt/format-duration (tpoint))]
(when (fn? on-progress)
(on-progress {:total total
:elapsed elapsed
:completed completed
:progress progress}))
(l/dbg :hint "progress" (l/dbg :hint "progress"
:completed-teams (:processed/teams newv) :completed completed
:completed-files (:processed/files newv) :progress progress
:completed-graphics (:processed/graphics newv) :elapsed elapsed)))))
:completed-components (:processed/components newv)
:progress (str (int progress) "%")
:elapsed (dt/format-duration elapsed))))))
(defn- get-total-files (defn- get-total-files
[pool & {:keys [team-id]}] [pool & {:keys [team-id]}]
@ -191,13 +196,23 @@
(let [elapsed (dt/format-duration (tpoint))] (let [elapsed (dt/format-duration (tpoint))]
(l/dbg :hint "migrate:end" :elapsed elapsed)))))) (l/dbg :hint "migrate:end" :elapsed elapsed))))))
(defn default-on-end
[stats]
(print-stats!
(-> stats
(update :elapsed/total dt/format-duration)
(dissoc :total/teams))))
(defn migrate-teams! (defn migrate-teams!
[{:keys [::db/pool] :as system} [{:keys [::db/pool] :as system}
& {:keys [chunk-size max-jobs max-items start-at rollback? preset skip-on-error max-time validate?] & {:keys [chunk-size max-jobs max-items start-at
rollback? validate? preset skip-on-error
max-time on-start on-progress on-error on-end]
:or {chunk-size 10000 :or {chunk-size 10000
validate? false validate? false
rollback? true rollback? true
skip-on-error true skip-on-error true
on-end default-on-end
preset :shutdown-on-failure preset :shutdown-on-failure
max-jobs Integer/MAX_VALUE max-jobs Integer/MAX_VALUE
max-items Long/MAX_VALUE}}] max-items Long/MAX_VALUE}}]
@ -242,7 +257,10 @@
tpoint (dt/tpoint) tpoint (dt/tpoint)
mtime (some-> max-time dt/duration)] mtime (some-> max-time dt/duration)]
(add-watch stats :progress-report (report-progress-teams tpoint)) (when (fn? on-start)
(on-start {:total total :rollback rollback?}))
(add-watch stats :progress-report (report-progress-teams tpoint on-progress))
(binding [feat/*stats* stats (binding [feat/*stats* stats
feat/*semaphore* sem feat/*semaphore* sem
@ -257,13 +275,15 @@
(p/await! scope)) (p/await! scope))
(print-stats! (when (fn? on-end)
(-> (deref feat/*stats*) (-> (deref stats)
(assoc :elapsed/total (dt/format-duration (tpoint))) (assoc :elapsed/total (tpoint))
(dissoc :total/teams))) (on-end)))
(catch Throwable cause (catch Throwable cause
(l/dbg :hint "migrate:error" :cause cause)) (l/dbg :hint "migrate:error" :cause cause)
(when (fn? on-error)
(on-error cause)))
(finally (finally
(let [elapsed (dt/format-duration (tpoint))] (let [elapsed (dt/format-duration (tpoint))]

View file

@ -22,6 +22,7 @@
[app.rpc.commands.auth :as auth] [app.rpc.commands.auth :as auth]
[app.rpc.commands.files-snapshot :as fsnap] [app.rpc.commands.files-snapshot :as fsnap]
[app.rpc.commands.profile :as profile] [app.rpc.commands.profile :as profile]
[app.srepl.cli :as cli]
[app.srepl.fixes :as f] [app.srepl.fixes :as f]
[app.srepl.helpers :as h] [app.srepl.helpers :as h]
[app.storage :as sto] [app.storage :as sto]