Skip to content

cat/play: tip that multiple files can be processed #1029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/cmd/cat.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ var catFlags = checkpoint.Opts{
func NewCatCmd() *cobra.Command {
var catCmd = &cobra.Command{
Use: "cat <FILE>...",
Short: "Print into stdout the contents of .snap/.xlog files",
Short: "Print into stdout the contents of .snap/.xlog FILE(s)",
Run: func(cmd *cobra.Command, args []string) {
cmdCtx.CommandName = cmd.Name()
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo,
internalCatModule, args)
util.HandleCmdErr(cmd, err)
},
Example: "tt cat /path/to/xlog --timestamp 2024-11-13T14:02:36.818700000+00:00\n" +
" tt cat /path/to/snap --timestamp=1731592956.818",
" tt cat /path/to/file.xlog /path/to/file.snap --timestamp=1731592956.818",
}

catCmd.Flags().Uint64Var(&catFlags.To, "to", catFlags.To,
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ var (
func NewPlayCmd() *cobra.Command {
var playCmd = &cobra.Command{
Use: "play <URI> <FILE>...",
Short: "Play the contents of .snap/.xlog files to another Tarantool instance",
Short: "Play the contents of .snap/.xlog FILE(s) to another Tarantool instance",
Run: func(cmd *cobra.Command, args []string) {
cmdCtx.CommandName = cmd.Name()
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo,
internalPlayModule, args)
util.HandleCmdErr(cmd, err)
},
Example: "tt play uri /path/to/xlog --timestamp 2024-11-13T14:02:36.818700000+00:00\n" +
" tt play uri /path/to/xlog --timestamp=1731592956.818",
" tt play uri /path/to/file.xlog /path/to/file.snap --timestamp=1731592956.818",
}

playCmd.Flags().StringVarP(&playUsername, "username", "u", "", "username")
Expand Down
177 changes: 85 additions & 92 deletions test/integration/cat/test_cat.py
Original file line number Diff line number Diff line change
@@ -1,122 +1,115 @@
import datetime
import io
import os
import re
import shutil

import pytest

from utils import run_command_and_get_output


def test_cat_unset_arg(tt_cmd, tmp_path):
# Testing with unset .xlog or .snap file.
cmd = [tt_cmd, "cat"]
rc, output = run_command_and_get_output(cmd, cwd=tmp_path)
assert rc == 1
assert re.search(r"it is required to specify at least one .xlog or .snap file", output)

@pytest.mark.parametrize("args, found", [
(
# Testing with unset .xlog or .snap file.
(),
"it is required to specify at least one .xlog or .snap file",
),
(
"path-to-non-existent-file",
"No such file or directory",
),
])
def test_cat_args_tests_failed(tt_cmd, tmp_path, args, found):
# Copy the .xlog file to the "run" directory.
test_xlog_file = os.path.join(os.path.dirname(__file__), "test_file", "test.xlog")
test_snap_file = os.path.join(os.path.dirname(__file__), "test_file", "test.snap")
shutil.copy(test_xlog_file, tmp_path)
shutil.copy(test_snap_file, tmp_path)

def test_cat_non_existent_file(tt_cmd, tmp_path):
# Testing with non-existent .xlog or .snap file.
cmd = [tt_cmd, "cat", "path-to-non-existent-file"]
cmd = [tt_cmd, "cat"]
cmd.extend(args)
rc, output = run_command_and_get_output(cmd, cwd=tmp_path)
assert rc == 1
assert re.search(r"No such file or directory", output)
assert found in output


def test_cat_snap_file(tt_cmd, tmp_path):
# Copy the .snap file to the "run" directory.
test_app_path = os.path.join(os.path.dirname(__file__), "test_file", "test.snap")
shutil.copy(test_app_path, tmp_path)
@pytest.mark.parametrize("args, found", [
(
("test.snap", "--show-system", "--space=320", "--space=296", "--from=423", "--to=513"),
("lsn: 423", "lsn: 512", "space_id: 320", "space_id: 296"),
),
(
("test.xlog", "--show-system", "--replica=1"),
("replica_id: 1"),
),
(
("test.xlog", "test.snap"),
('Result of cat: the file "test.xlog" is processed below',
'Result of cat: the file "test.snap" is processed below'),
),
])
def test_cat_args_tests_successed(tt_cmd, tmp_path, args, found):
# Copy the .xlog file to the "run" directory.
test_xlog_file = os.path.join(os.path.dirname(__file__), "test_file", "test.xlog")
test_snap_file = os.path.join(os.path.dirname(__file__), "test_file", "test.snap")
shutil.copy(test_xlog_file, tmp_path)
shutil.copy(test_snap_file, tmp_path)

# Testing .snap file.
cmd = [
tt_cmd, "cat", "test.snap", "--show-system",
"--space=320", "--space=296", "--from=423", "--to=513"
]
cmd = [tt_cmd, "cat"]
cmd.extend(args)
rc, output = run_command_and_get_output(cmd, cwd=tmp_path)
assert rc == 0
assert re.search(r"lsn: 423", output)
assert re.search(r"lsn: 512", output)
assert re.search(r"space_id: 320", output)
assert re.search(r"space_id: 296", output)
for item in found:
assert item in output


def test_cat_xlog_file(tt_cmd, tmp_path):
@pytest.mark.parametrize("input, error", [
(
"abcdef",
'failed to parse a timestamp: parsing time "abcdef"',
),
(
"2024-11-14T14:02:36.abc",
'failed to parse a timestamp: parsing time "2024-11-14T14:02:36.abc"',
),
])
def test_cat_test_timestamp_failed(tt_cmd, tmp_path, input, error):
# Copy the .xlog file to the "run" directory.
test_app_path = os.path.join(os.path.dirname(__file__), "test_file", "test.xlog")
test_app_path = os.path.join(os.path.dirname(__file__), "test_file", "timestamp.xlog")
shutil.copy(test_app_path, tmp_path)

# Testing .xlog file.
cmd = [tt_cmd, "cat", "test.xlog", "--show-system", "--replica=1"]
cmd = [tt_cmd, "cat", "timestamp.xlog", f"--timestamp={input}"]
rc, output = run_command_and_get_output(cmd, cwd=tmp_path)
assert rc == 0
assert re.search(r"replica_id: 1", output)


TEST_CAT_TIMESTAMP_PARAMS_CCONFIG = ("input, cat_result, found, not_found")


def make_test_cat_timestamp_param(
input="",
cat_result=0,
found={},
not_found={},
):
return pytest.param(input, cat_result, found, not_found)
assert rc == 1
assert error in output


@pytest.mark.parametrize(TEST_CAT_TIMESTAMP_PARAMS_CCONFIG, [
make_test_cat_timestamp_param(
input="abcdef",
cat_result=1,
found={"failed to parse a timestamp: parsing time \"abcdef\""},
),
make_test_cat_timestamp_param(
input="2024-11-14T14:02:36.abc",
cat_result=1,
found={"failed to parse a timestamp: parsing time \"2024-11-14T14:02:36.abc\""},
),
make_test_cat_timestamp_param(
input="",
cat_result=0,
found={"lsn: 12"},
),
make_test_cat_timestamp_param(
input="1731592956.8182",
cat_result=0,
found={"lsn: 6",
"timestamp: 1731592956.8181"},
not_found={"lsn: 8",
"timestamp: 1731592956.8184"},
),
make_test_cat_timestamp_param(
input="2024-11-14T14:02:36.818299999Z",
cat_result=0,
found={"lsn: 6",
"timestamp: 1731592956.8181"},
not_found={"lsn: 8",
"timestamp: 1731592956.8184"},
),
make_test_cat_timestamp_param(
input="2024-11-14T14:02:35+00:00",
cat_result=0,
not_found={"lsn: 6",
"timestamp: 1731592956.8181",
"lsn: 8",
"timestamp: 1731592956.8184"},
),
@pytest.mark.parametrize("input", [
1731592956.1182,
1731592956.8182,
"2024-11-14T14:02:36.818+00:00",
"2024-11-14T14:02:35+00:00",
])
def test_cat_test_remote_instance_timestamp(tt_cmd, tmp_path, input,
cat_result, found, not_found):
def test_cat_test_timestamp_successed(tt_cmd, tmp_path, input):
# Copy the .xlog file to the "run" directory.
test_app_path = os.path.join(os.path.dirname(__file__), "test_file", "timestamp.xlog")
shutil.copy(test_app_path, tmp_path)

cmd = [tt_cmd, "cat", "timestamp.xlog", "--timestamp={0}".format(input)]
cmd = [tt_cmd, "cat", "timestamp.xlog", f"--timestamp={input}"]
rc, output = run_command_and_get_output(cmd, cwd=tmp_path)
assert rc == cat_result
if cat_result == 0:
for item in found:
assert re.search(r"{0}".format(item), output)
for item in not_found:
assert not re.search(r"{0}".format(item), output)
assert rc == 0

# Convert input to timestamp
input_ts = 0
if type(input) is float or type(input) is int:
input_ts = float(input)
if type(input) is str:
input_ts = float(datetime.datetime.fromisoformat(input).timestamp())

# Compare input value and record's timestamp
buf = io.StringIO(output)
while (line := buf.readline()) != "":
if "timestamp:" in line:
index = line.find(':')
record_ts = line[index+1:].strip()
assert input_ts > float(record_ts)
1 change: 1 addition & 0 deletions test/integration/play/test_file/remote_instance_cfg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ local function configure_instance()
tester:create_index('primary', {type = 'tree', parts = {'id'}})
box.schema.user.grant('guest', 'read,write', 'space', 'tester')
box.schema.user.create('test_user', { password = 'secret' })
box.schema.user.grant('test_user', 'execute', 'universe')
box.schema.user.grant('test_user', 'super')
end

Expand Down
3 changes: 3 additions & 0 deletions test/integration/play/test_file/timestamp/create_space.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
box.schema.space.create('test', { id = 512 })

return box.space.test:create_index('0')
1 change: 1 addition & 0 deletions test/integration/play/test_file/timestamp/get_data.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return box.space.test:select()
Binary file not shown.
Binary file not shown.
Loading
Loading