aboutsummaryrefslogtreecommitdiffstats
path: root/tools/migration/prosody-migrator.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tools/migration/prosody-migrator.lua')
-rw-r--r--tools/migration/prosody-migrator.lua187
1 files changed, 137 insertions, 50 deletions
diff --git a/tools/migration/prosody-migrator.lua b/tools/migration/prosody-migrator.lua
index 1219d891..26b9ef58 100644
--- a/tools/migration/prosody-migrator.lua
+++ b/tools/migration/prosody-migrator.lua
@@ -1,41 +1,67 @@
#!/usr/bin/env lua
-CFG_SOURCEDIR=os.getenv("PROSODY_SRCDIR");
-CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
+CFG_SOURCEDIR=CFG_SOURCEDIR or os.getenv("PROSODY_SRCDIR");
+CFG_CONFIGDIR=CFG_CONFIGDIR or os.getenv("PROSODY_CFGDIR");
+CFG_PLUGINDIR=CFG_PLUGINDIR or os.getenv("PROSODY_PLUGINDIR");
+CFG_DATADIR=CFG_DATADIR or os.getenv("PROSODY_DATADIR");
--- Substitute ~ with path to home directory in paths
-if CFG_CONFIGDIR then
- CFG_CONFIGDIR = CFG_CONFIGDIR:gsub("^~", os.getenv("HOME"));
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+local function is_relative(path)
+ local path_sep = package.config:sub(1,1);
+ return ((path_sep == "/" and path:sub(1,1) ~= "/")
+ or (path_sep == "\\" and (path:sub(1,1) ~= "/" and path:sub(2,3) ~= ":\\")))
end
+-- Tell Lua where to find our libraries
if CFG_SOURCEDIR then
- CFG_SOURCEDIR = CFG_SOURCEDIR:gsub("^~", os.getenv("HOME"));
+ local function filter_relative_paths(path)
+ if is_relative(path) then return ""; end
+ end
+ local function sanitise_paths(paths)
+ return (paths:gsub("[^;]+;?", filter_relative_paths):gsub(";;+", ";"));
+ end
+ package.path = sanitise_paths(CFG_SOURCEDIR.."/?.lua;"..package.path);
+ package.cpath = sanitise_paths(CFG_SOURCEDIR.."/?.so;"..package.cpath);
+end
+
+-- Substitute ~ with path to home directory in data path
+if CFG_DATADIR then
+ if os.getenv("HOME") then
+ CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
+ end
end
local default_config = (CFG_CONFIGDIR or ".").."/migrator.cfg.lua";
--- Command-line parsing
-local options = {};
-local i = 1;
-while arg[i] do
- if arg[i]:sub(1,2) == "--" then
- local opt, val = arg[i]:match("([%w-]+)=?(.*)");
- if opt then
- options[(opt:sub(3):gsub("%-", "_"))] = #val > 0 and val or true;
- end
- table.remove(arg, i);
- else
- i = i + 1;
+local startup = require "util.startup";
+do
+ startup.parse_args();
+ startup.init_global_state();
+ prosody.process_type = "migrator";
+ startup.force_console_logging();
+ startup.init_logging();
+ startup.init_gc();
+ startup.init_errors();
+ startup.setup_plugindir();
+ startup.setup_plugin_install_path();
+ startup.setup_datadir();
+ startup.chdir();
+ startup.read_version();
+ startup.switch_user();
+ startup.check_dependencies();
+ startup.log_startup_warnings();
+ prosody.config_loaded = true;
+ startup.load_libraries();
+ startup.init_http_client();
+ prosody.core_post_stanza = function ()
+ -- silence assert in core.moduleapi
+ error("Attempt to send stanzas from inside migrator.", 0);
end
end
-if CFG_SOURCEDIR then
- package.path = CFG_SOURCEDIR.."/?.lua;"..package.path;
- package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
-else
- package.path = "../../?.lua;"..package.path
- package.cpath = "../../?.so;"..package.cpath
-end
+-- Command-line parsing
+local options = prosody.opts;
local envloadfile = require "util.envload".envloadfile;
@@ -69,24 +95,14 @@ if not config[to_store] then
print("Error: Output store '"..to_store.."' not found in the config file.");
end
-function load_store_handler(name)
- local store_type = config[name].type;
- if not store_type then
- print("Error: "..name.." store type not specified in the config file");
- return false;
- else
- local ok, err = pcall(require, "migrator."..store_type);
- if not ok then
- print(("Error: Failed to initialize '%s' store:\n\t%s")
- :format(name, err));
- return false;
- end
+for store, conf in pairs(config) do -- COMPAT
+ if conf.type == "prosody_files" then
+ conf.type = "internal";
+ elseif conf.type == "prosody_sql" then
+ conf.type = "sql";
end
- return true;
end
-have_err = have_err or not(load_store_handler(from_store, "input") and load_store_handler(to_store, "output"));
-
if have_err then
print("");
print("Usage: "..arg[0].." FROM_STORE TO_STORE");
@@ -101,17 +117,88 @@ if have_err then
os.exit(1);
end
-local itype = config[from_store].type;
-local otype = config[to_store].type;
-local reader = require("migrator."..itype).reader(config[from_store]);
-local writer = require("migrator."..otype).writer(config[to_store]);
+local async = require "util.async";
+local server = require "net.server";
+local watchers = {
+ error = function (_, err)
+ error(err);
+ end;
+ waiting = function ()
+ server.loop();
+ end;
+};
+
+local cm = require "core.configmanager";
+local hm = require "core.hostmanager";
+local sm = require "core.storagemanager";
+local um = require "core.usermanager";
+
+local function users(store, host)
+ if store.users then
+ return store:users();
+ else
+ return um.users(host);
+ end
+end
-local json = require "util.json";
+local function prepare_config(host, conf)
+ if conf.type == "internal" then
+ sm.olddm.set_data_path(conf.path or prosody.paths.data);
+ elseif conf.type == "sql" then
+ cm.set(host, "sql", conf);
+ end
+end
-io.stderr:write("Migrating...\n");
-for x in reader do
- --print(json.encode(x))
- writer(x);
+local function get_driver(host, conf)
+ prepare_config(host, conf);
+ return assert(sm.load_driver(host, conf.type));
end
-writer(nil); -- close
+
+local migration_runner = async.runner(function (job)
+ for host, stores in pairs(job.input.hosts) do
+ prosody.hosts[host] = startup.make_host(host);
+ sm.initialize_host(host);
+ um.initialize_host(host);
+
+ local input_driver = get_driver(host, job.input);
+
+ local output_driver = get_driver(host, job.output);
+
+ for _, store in ipairs(stores) do
+ local p, typ = store:match("()%-(%w+)$");
+ if typ then store = store:sub(1, p-1); else typ = "keyval"; end
+ log("info", "Migrating host %s store %s (%s)", host, store, typ);
+
+ local origin = assert(input_driver:open(store, typ));
+ local destination = assert(output_driver:open(store, typ));
+
+ if typ == "keyval" then -- host data
+ local data, err = origin:get(nil);
+ assert(not err, err);
+ assert(destination:set(nil, data));
+ end
+
+ for user in users(origin, host) do
+ if typ == "keyval" then
+ local data, err = origin:get(user);
+ assert(not err, err);
+ assert(destination:set(user, data));
+ elseif typ == "archive" then
+ local iter, err = origin:find(user);
+ assert(iter, err);
+ for id, item, when, with in iter do
+ assert(destination:append(user, id, item, when, with));
+ end
+ else
+ error("Don't know how to migrate data of type '"..typ.."'.");
+ end
+ end
+ end
+ end
+end, watchers);
+
+io.stderr:write("Migrating...\n");
+
+migration_runner:run({ input = config[from_store], output = config[to_store] });
+
io.stderr:write("Done!\n");