1
#!/usr/bin/env tarantool
3
local fio = require('fio')
4
local errno = require('errno')
5
local urilib = require('uri')
6
local console = require('console')
7
local term = require('term')
8
local log = require('log')
9
local yaml = require('yaml')
11
local TARANTOOL_DEFAULT_PORT = 3301
12
local CONSOLE_SOCKET_PATH = 'unix/:/var/run/tarantool/tarantool.sock'
13
local CFG_FILE_PATH = '/etc/tarantool/config.yml'
16
local orig_cfg = box.cfg
18
local function read_config()
19
local f = io.open(CFG_FILE_PATH, "rb")
21
log.error("Can't open " .. CFG_FILE_PATH ..": ", errno.strerror())
24
local content = f:read("*all")
26
return yaml.decode(content)
29
local function write_config(cfg)
30
local f = io.open(CFG_FILE_PATH, "w+")
32
print("Can't open " .. CFG_FILE_PATH ..": ", errno.strerror())
35
local content = yaml.encode(cfg)
40
local function parse_replication_source(replication_source, user_name, user_password)
41
if replication_source == nil then
45
local replication_source_table = {}
46
for uri in string.gmatch(replication_source, "[^,]+") do
47
local parsed_uri = urilib.parse(uri)
48
if parsed_uri == nil then
49
error("Incorrect replication source URI format: '"..uri.."'")
51
local host = parsed_uri.host
52
local port = parsed_uri.service or TARANTOOL_DEFAULT_PORT
53
local user = parsed_uri.login or user_name
54
local password = parsed_uri.password or user_password
56
if user == 'guest' or user == nil then
57
replication_source = string.format("%s:%s", host, port)
58
elseif password == nil then
59
replication_source = string.format("%s:@%s:%s", user, host, port)
61
replication_source = string.format("%s:%s@%s:%s", user, password,
65
table.insert(replication_source_table, replication_source)
68
return replication_source_table
71
local function choose_option(main, substitute, cfg)
75
if cfg[substitute] then
81
function set_replication_source(replication_source, user_name, user_password)
82
local replication_source_table = parse_replication_source(
83
replication_source, user_name, user_password
85
local choice = choose_option('replication', 'replication_source', box.cfg)
86
box.cfg{[choice] = replication_source_table}
87
log.info("Updated box.cfg.%s to %s", choice, replication_source)
90
local function create_user(user_name, user_password)
91
if user_name ~= 'guest' and user_password == nil then
94
local warn_str = [[****************************************************
95
WARNING: No password has been set for the database.
96
This will allow anyone with access to the
97
Tarantool port to access your database. In
98
Docker's default configuration, this is
99
effectively any other container on the same
101
Use "-e TARANTOOL_USER_PASSWORD=password"
102
to set it in "docker run".
103
****************************************************]]
104
log.warn('\n'..warn_str)
107
if user_name == 'guest' and user_password == nil then
108
local warn_str = [[****************************************************
109
WARNING: 'guest' is chosen as primary user.
110
Since it is not allowed to set a password for
111
guest user, your instance will be accessible
112
by anyone having direct access to the Tarantool
114
If you wanted to create an authenticated user,
115
specify "-e TARANTOOL_USER_NAME=username" and
116
pick a user name other than "guest".
117
****************************************************]]
118
log.warn('\n'..warn_str)
121
if user_name == 'guest' and user_password ~= nil then
124
local warn_str = [[****************************************************
125
WARNING: A password for guest user has been specified.
126
In Tarantool, guest user can't have a password
127
and is always allowed to login, if it has
129
If you wanted to create an authenticated user,
130
specify "-e TARANTOOL_USER_NAME=username" and
131
pick a user name other than "guest".
132
****************************************************]]
133
log.warn('\n'..warn_str)
136
if user_name ~= 'admin' and user_name ~= 'guest' then
137
if not box.schema.user.exists(user_name) then
138
log.info("Creating user '%s'", user_name)
139
box.schema.user.create(user_name)
143
if user_name ~= 'admin' then
144
log.info("Granting admin privileges to user '%s'", user_name)
145
box.schema.user.grant(user_name, 'read,write,execute,create,drop',
146
'universe', nil, {if_not_exists = true})
147
box.schema.user.grant(user_name, 'replication',
148
nil, nil, {if_not_exists = true})
151
if user_name ~= 'guest' then
152
log.info("Setting password for user '%s'", user_name)
153
box.schema.user.passwd(user_name, user_password)
157
function set_credentials(user_name, user_password)
158
create_user(user_name, user_password)
161
local function wrapper_cfg(override)
162
local work_dir = '/var/lib/tarantool'
163
local snap_filename = "*.snap"
164
local snap_path = work_dir..'/'..snap_filename
166
local first_run = false
167
if next(fio.glob(snap_path)) == nil then
173
local config_file_exists = fio.stat(CFG_FILE_PATH) ~= nil
174
if not config_file_exists then
175
log.info("Creating configuration file: " .. CFG_FILE_PATH)
177
file_cfg.TARANTOOL_USER_NAME = os.getenv('TARANTOOL_USER_NAME')
178
file_cfg.TARANTOOL_USER_PASSWORD = os.getenv('TARANTOOL_USER_PASSWORD')
179
file_cfg.TARANTOOL_SLAB_ALLOC_ARENA = os.getenv('TARANTOOL_SLAB_ALLOC_ARENA')
180
file_cfg.TARANTOOL_SLAB_ALLOC_FACTOR = os.getenv('TARANTOOL_SLAB_ALLOC_FACTOR')
181
file_cfg.TARANTOOL_SLAB_ALLOC_MINIMAL = os.getenv('TARANTOOL_SLAB_ALLOC_MINIMAL')
182
file_cfg.TARANTOOL_SLAB_ALLOC_MAXIMAL = os.getenv('TARANTOOL_SLAB_ALLOC_MAXIMAL')
183
file_cfg.TARANTOOL_PORT = os.getenv('TARANTOOL_PORT')
184
file_cfg.TARANTOOL_FORCE_RECOVERY = os.getenv('TARANTOOL_FORCE_RECOVERY')
185
file_cfg.TARANTOOL_LOG_FORMAT = os.getenv('TARANTOOL_LOG_FORMAT')
186
file_cfg.TARANTOOL_LOG_LEVEL = os.getenv('TARANTOOL_LOG_LEVEL')
187
file_cfg.TARANTOOL_WAL_MODE = os.getenv('TARANTOOL_WAL_MODE')
188
file_cfg.TARANTOOL_REPLICATION_SOURCE = os.getenv('TARANTOOL_REPLICATION_SOURCE')
189
file_cfg.TARANTOOL_REPLICATION = os.getenv('TARANTOOL_REPLICATION')
190
file_cfg.TARANTOOL_SNAPSHOT_PERIOD = os.getenv('TARANTOOL_SNAPSHOT_PERIOD')
191
file_cfg.TARANTOOL_MEMTX_MEMORY = os.getenv('TARANTOOL_MEMTX_MEMORY')
192
file_cfg.TARANTOOL_CHECKPOINT_INTERVAL = os.getenv('TARANTOOL_CHECKPOINT_INTERVAL')
193
file_cfg.TARANTOOL_MEMTX_MIN_TUPLE_SIZE = os.getenv('TARANTOOL_MEMTX_MIN_TUPLE_SIZE')
194
file_cfg.TARANTOOL_MEMTX_MAX_TUPLE_SIZE = os.getenv('TARANTOOL_MEMTX_MAX_TUPLE_SIZE')
196
write_config(file_cfg)
198
log.info("Loading existing configuration file: " .. CFG_FILE_PATH)
200
file_cfg = read_config()
203
local user_name = file_cfg.TARANTOOL_USER_NAME or
204
os.getenv('TARANTOOL_USER_NAME') or 'guest'
205
local user_password = file_cfg.TARANTOOL_USER_PASSWORD or
206
os.getenv('TARANTOOL_USER_PASSWORD')
209
local cfg = override or {}
210
-- Placeholders for deprecated options
211
cfg.slab_alloc_arena = tonumber(file_cfg.TARANTOOL_SLAB_ALLOC_ARENA) or
212
override.slab_alloc_arena
213
cfg.slab_alloc_maximal = tonumber(file_cfg.TARANTOOL_SLAB_ALLOC_MAXIMAL) or
214
override.slab_alloc_maximal
215
cfg.slab_alloc_minimal = tonumber(file_cfg.TARANTOOL_SLAB_ALLOC_MINIMAL) or
216
override.slab_alloc_minimal
217
cfg.snapshot_period = tonumber(file_cfg.TARANTOOL_SNAPSHOT_PERIOD) or
218
override.snapshot_period
219
-- Replacements for deprecated options
220
cfg.memtx_memory = tonumber(file_cfg.TARANTOOL_MEMTX_MEMORY) or
221
override.memtx_memory
222
cfg.memtx_min_tuple_size = tonumber(file_cfg.TARANTOOL_MEMTX_MIN_TUPLE_SIZE) or
223
override.memtx_min_tuple_size
224
cfg.memtx_max_tuple_size = tonumber(file_cfg.TARANTOOL_MEMTX_MAX_TUPLE_SIZE) or
225
override.memtx_max_tuple_size
226
cfg.checkpoint_interval = tonumber(file_cfg.TARANTOOL_CHECKPOINT_INTERVAL) or
227
override.checkpoint_interval
228
-- Deprecated options with default values
229
local choice = choose_option('memtx_dir', 'snap_dir', override)
230
cfg[choice] = override[choice] or '/var/lib/tarantool'
232
-- Remaining configuration
233
cfg.slab_alloc_factor = tonumber(file_cfg.TARANTOOL_SLAB_ALLOC_FACTOR) or
234
override.slab_alloc_factor
235
cfg.listen = tonumber(file_cfg.TARANTOOL_PORT) or
236
override.listen or TARANTOOL_DEFAULT_PORT
237
cfg.wal_mode = file_cfg.TARANTOOL_WAL_MODE or
240
cfg.force_recovery = file_cfg.TARANTOOL_FORCE_RECOVERY == 'true'
241
cfg.log_format = file_cfg.TARANTOOL_LOG_FORMAT or 'plain'
242
cfg.log_level = tonumber(file_cfg.TARANTOOL_LOG_LEVEL) or 5
244
cfg.wal_dir = override.wal_dir or '/var/lib/tarantool'
245
cfg.vinyl_dir = override.vinyl_dir or '/var/lib/tarantool'
246
cfg.pid_file = override.pid_file or '/var/run/tarantool/tarantool.pid'
248
local choice = choose_option('TARANTOOL_REPLICATION', 'TARANTOOL_REPLICATION_SOURCE', file_cfg)
249
local replication_source_table = parse_replication_source(file_cfg[choice],
253
if replication_source_table then
254
cfg.replication = replication_source_table
256
local choice = choose_option('replication', 'replication_source', override)
257
cfg[choice] = override[choice]
260
log.info("Config:\n" .. yaml.encode(cfg))
264
box.once('tarantool-entrypoint', function ()
266
log.info("Initializing database")
268
create_user(user_name, user_password)
272
console.listen(CONSOLE_SOCKET_PATH)
274
local metrics_port = tonumber(os.getenv('TARANTOOL_PROMETHEUS_DEFAULT_METRICS_PORT')) or 0
275
if metrics_port > 0 then
276
require('metrics').enable_default_metrics()
277
local prometheus = require('metrics.plugins.prometheus')
278
local httpd = require('http.server').new('0.0.0.0', metrics_port)
279
httpd:route( { path = '/metrics' }, prometheus.collect_http)
286
-- re-run the script passed as parameter with all arguments that follow
287
execute_script = arg[1]
288
if execute_script == nil then
291
if term.isatty(io.stdout) then
298
arg[narg] = arg[narg + 1]
299
if arg[narg] == nil then
305
dofile(execute_script)