234 lines
6.2 KiB
Lua
234 lines
6.2 KiB
Lua
local DB = require "kong.db"
|
|
local log = require "kong.cmd.utils.log"
|
|
local tty = require "kong.cmd.utils.tty"
|
|
local meta = require "kong.meta"
|
|
local conf_loader = require "kong.conf_loader"
|
|
local kong_global = require "kong.global"
|
|
local prefix_handler = require "kong.cmd.utils.prefix_handler"
|
|
local migrations_utils = require "kong.cmd.utils.migrations"
|
|
|
|
|
|
local lapp = [[
|
|
Usage: kong migrations COMMAND [OPTIONS]
|
|
|
|
Manage database schema migrations.
|
|
|
|
The available commands are:
|
|
bootstrap Bootstrap the database and run all
|
|
migrations.
|
|
|
|
up Run any new migrations.
|
|
|
|
finish Finish running any pending migrations after
|
|
'up'.
|
|
|
|
list List executed migrations.
|
|
|
|
reset Reset the database. The `reset` command erases all of the data in Kong's database and deletes all of the schemas.
|
|
|
|
status Dump the database migration status in JSON format
|
|
|
|
Options:
|
|
-y,--yes Assume "yes" to prompts and run
|
|
non-interactively.
|
|
|
|
-q,--quiet Suppress all output.
|
|
|
|
-f,--force Run migrations even if database reports
|
|
as already executed.
|
|
|
|
--db-timeout (optional number) Timeout, in seconds, for all database
|
|
operations.
|
|
|
|
|
|
--lock-timeout (default 60) Timeout, in seconds, for nodes waiting on
|
|
the leader node to finish running
|
|
migrations.
|
|
|
|
-c,--conf (optional string) Configuration file.
|
|
|
|
-p,--prefix (optional string) Override prefix directory.
|
|
|
|
]]
|
|
|
|
|
|
local function confirm_prompt(q)
|
|
local MAX = 3
|
|
local ANSWERS = {
|
|
y = true,
|
|
Y = true,
|
|
yes = true,
|
|
YES = true,
|
|
n = false,
|
|
N = false,
|
|
no = false,
|
|
NO = false
|
|
}
|
|
|
|
while MAX > 0 do
|
|
io.write("> " .. q .. " [y/n] ")
|
|
local a = io.read("*l")
|
|
if ANSWERS[a] ~= nil then
|
|
return ANSWERS[a]
|
|
end
|
|
MAX = MAX - 1
|
|
end
|
|
end
|
|
|
|
|
|
local function execute(args)
|
|
args.db_timeout = args.db_timeout and (args.db_timeout * 1000) or nil
|
|
args.lock_timeout = args.lock_timeout
|
|
|
|
if args.quiet then
|
|
log.disable()
|
|
end
|
|
|
|
local conf = assert(conf_loader(args.conf, {
|
|
prefix = args.prefix
|
|
}))
|
|
|
|
package.path = conf.lua_package_path .. ";" .. package.path
|
|
|
|
conf.pg_timeout = args.db_timeout or conf.pg_timeout -- connect + send + read
|
|
|
|
assert(prefix_handler.prepare_prefix(conf, args.nginx_conf, true))
|
|
|
|
_G.kong = kong_global.new()
|
|
kong_global.init_pdk(_G.kong, conf)
|
|
|
|
local db = assert(DB.new(conf))
|
|
assert(db:init_connector())
|
|
|
|
local schema_state = assert(db:schema_state())
|
|
|
|
if args.command == "list" then
|
|
if schema_state.needs_bootstrap then
|
|
log(migrations_utils.NEEDS_BOOTSTRAP_MSG)
|
|
os.exit(3)
|
|
end
|
|
|
|
local r = ""
|
|
|
|
if schema_state.executed_migrations then
|
|
log("Executed migrations:\n%s", schema_state.executed_migrations)
|
|
r = "\n"
|
|
end
|
|
|
|
if schema_state.pending_migrations then
|
|
log("%sPending migrations:\n%s", r, schema_state.pending_migrations)
|
|
r = "\n"
|
|
end
|
|
|
|
if schema_state.new_migrations then
|
|
log("%sNew migrations available:\n%s", r, schema_state.new_migrations)
|
|
r = "\n"
|
|
end
|
|
|
|
if schema_state.pending_migrations and schema_state.new_migrations then
|
|
if r ~= "" then
|
|
log("")
|
|
end
|
|
|
|
log.warn("Database has pending migrations from a previous upgrade, " ..
|
|
"and new migrations from this upgrade (version %s)",
|
|
tostring(meta._VERSION))
|
|
|
|
log("\nRun 'kong migrations finish' when ready to complete pending " ..
|
|
"migrations (%s %s will be incompatible with the previous Kong " ..
|
|
"version)", db.strategy, db.infos.db_desc)
|
|
|
|
os.exit(4)
|
|
end
|
|
|
|
if schema_state.pending_migrations then
|
|
log("\nRun 'kong migrations finish' when ready")
|
|
os.exit(4)
|
|
end
|
|
|
|
if schema_state.new_migrations then
|
|
log("\nRun 'kong migrations up' to proceed")
|
|
os.exit(5)
|
|
end
|
|
|
|
-- exit(0)
|
|
|
|
elseif args.command == "status" then
|
|
|
|
-- Clean up the schema_state data structure so that it can be
|
|
-- serialized as json.
|
|
local function cleanup (namespace_migrations)
|
|
if namespace_migrations then
|
|
for _, namespace_migration in pairs(namespace_migrations) do
|
|
for i = 1, #namespace_migration.migrations do
|
|
namespace_migration.migrations[i] = namespace_migration.migrations[i].name
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
cleanup(schema_state.new_migrations)
|
|
cleanup(schema_state.pending_migrations)
|
|
cleanup(schema_state.executed_migrations)
|
|
|
|
local cjson = require "cjson"
|
|
print(cjson.encode(schema_state))
|
|
|
|
elseif args.command == "bootstrap" then
|
|
if args.force then
|
|
migrations_utils.reset(schema_state, db, args.lock_timeout)
|
|
schema_state = assert(db:schema_state())
|
|
end
|
|
migrations_utils.bootstrap(schema_state, db, args.lock_timeout)
|
|
|
|
elseif args.command == "reset" then
|
|
if not args.yes then
|
|
if not tty.isatty() then
|
|
error("not a tty: invoke 'reset' non-interactively with the --yes flag")
|
|
end
|
|
|
|
if not schema_state.needs_bootstrap and
|
|
not confirm_prompt("Are you sure? This operation is irreversible.") then
|
|
log("cancelled")
|
|
return
|
|
end
|
|
end
|
|
|
|
local ok = migrations_utils.reset(schema_state, db, args.lock_timeout)
|
|
if not ok then
|
|
os.exit(1)
|
|
end
|
|
os.exit(0)
|
|
|
|
elseif args.command == "up" then
|
|
migrations_utils.up(schema_state, db, {
|
|
ttl = args.lock_timeout,
|
|
force = args.force,
|
|
abort = true, -- exit the mutex if another node acquired it
|
|
})
|
|
|
|
elseif args.command == "finish" then
|
|
migrations_utils.finish(schema_state, db, {
|
|
ttl = args.lock_timeout,
|
|
force = args.force,
|
|
})
|
|
|
|
else
|
|
error("unreachable")
|
|
end
|
|
end
|
|
|
|
|
|
return {
|
|
lapp = lapp,
|
|
execute = execute,
|
|
sub_commands = {
|
|
bootstrap = true,
|
|
up = true,
|
|
finish = true,
|
|
list = true,
|
|
reset = true,
|
|
status = true
|
|
}
|
|
}
|