diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dcda362..4b8e11f41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added +- `tt connect`: \history command. + ### Changed ### Fixed diff --git a/README.md b/README.md index d1da32460..7fce2561d 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,7 @@ env: inc_dir: path/to/inc_dir restart_on_failure: bool tarantoolctl_layout: bool + ouptut_history_max: 15 modules: directory: path/to/modules/dir app: @@ -341,6 +342,8 @@ templates: compatible mode for artifact files: control socket, pid, log files. Data files (wal, vinyl, snapshots) and multi-instance applications are not affected by this option. +- `ouptut_history_max` (int) - the maximum number of commands output + by the "\history" command. Default vaule is 10 #### modules diff --git a/cli/cfg/dump_test.go b/cli/cfg/dump_test.go index fbde4f0f8..604bf3782 100644 --- a/cli/cfg/dump_test.go +++ b/cli/cfg/dump_test.go @@ -92,6 +92,7 @@ env: instances_enabled: %[1]s restart_on_failure: false tarantoolctl_layout: false + ouptut_history_max: 0 modules: directory: /root/modules app: @@ -128,6 +129,7 @@ env: instances_enabled: %[1]s restart_on_failure: false tarantoolctl_layout: false + ouptut_history_max: 0 modules: directory: - /root/modules @@ -166,6 +168,7 @@ env: instances_enabled: %[1]s/instances.enabled restart_on_failure: false tarantoolctl_layout: false + ouptut_history_max: 0 modules: directory: %[1]s/my_modules app: @@ -203,6 +206,7 @@ env: instances_enabled: . restart_on_failure: false tarantoolctl_layout: false + ouptut_history_max: 0 modules: directory: /root/modules app: diff --git a/cli/cmd/connect.go b/cli/cmd/connect.go index 28365dcc3..e5f373551 100644 --- a/cli/cmd/connect.go +++ b/cli/cmd/connect.go @@ -124,6 +124,13 @@ func makeConnOpts(network, address string, connCtx connect.ConnectCtx) connector } } +func getMaxHistoryOutputLen(cliOpts *config.CliOpts) int { + if cliOpts.Env == nil || cliOpts.Env.OutputHistoryMax <= 0 { + return connect.DefaultOutputHistorySize + } + return cliOpts.Env.OutputHistoryMax +} + // resolveConnectOpts tries to resolve target argument to replace it // with a control socket or as a URI with/without credentials. // It returns connection options and the remaining args. @@ -183,6 +190,7 @@ func resolveConnectOpts(cmdCtx *cmdcontext.CmdCtx, cliOpts *config.CliOpts, if connectCtx.ConnectTarget == "" { connectCtx.ConnectTarget = target } + connOpts.MaxOutputHistoryLen = getMaxHistoryOutputLen(cliOpts) return connOpts, err } diff --git a/cli/cmd/connect_test.go b/cli/cmd/connect_test.go new file mode 100644 index 000000000..5e7945832 --- /dev/null +++ b/cli/cmd/connect_test.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tarantool/tt/cli/config" + "github.com/tarantool/tt/cli/connect" +) + +func TestGenMaxHistoryOption(t *testing.T) { + t.Run("not configured", func(t *testing.T) { + opts := config.CliOpts{} + actual := getMaxHistoryOutputLen(&opts) + assert.Equal(t, connect.DefaultOutputHistorySize, actual) + }) + t.Run("invalid configuration", func(t *testing.T) { + opts := config.CliOpts{Env: &config.TtEnvOpts{OutputHistoryMax: 0}} + actual := getMaxHistoryOutputLen(&opts) + assert.Equal(t, connect.DefaultOutputHistorySize, actual) + }) + t.Run("correct configuration", func(t *testing.T) { + opts := config.CliOpts{Env: &config.TtEnvOpts{OutputHistoryMax: 2}} + actual := getMaxHistoryOutputLen(&opts) + assert.Equal(t, 2, actual) + }) +} diff --git a/cli/config/config.go b/cli/config/config.go index 9d93eafbb..2d82dcb94 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -67,6 +67,9 @@ type TtEnvOpts struct { // application sub-directories are not created for runtime artifacts like // control socket, pid files and logs. TarantoolctlLayout bool `mapstructure:"tarantoolctl_layout" yaml:"tarantoolctl_layout"` + // The maximum number of commands output by the "\history" command. + // Default vaule is 10 + OutputHistoryMax int `mapstructure:"ouptut_history_max" yaml:"ouptut_history_max"` } // TemplateOpts contains configuration for applications templates. diff --git a/cli/connect/commands.go b/cli/connect/commands.go index b71534911..6f30cd5a5 100644 --- a/cli/connect/commands.go +++ b/cli/connect/commands.go @@ -414,6 +414,18 @@ func getShortcutsFunc(console *Console, cmd string, args []string) (string, erro return shortcutListText, nil } +// getHistoryFunc returns last {maxHistorySize} executed commands +func getHistoryFunc(console *Console, _ string, _ []string) (string, error) { + if len(console.history.commands) == 0 { + return "", nil + } + outputData := console.history.commands + if len(console.history.commands) > console.connOpts.MaxOutputHistoryLen { + outputData = outputData[len(outputData)-console.connOpts.MaxOutputHistoryLen:] + } + return strings.Join(outputData, "\n-----\n"), nil +} + // setQuitFunc sets the quit flag for the console. func setQuitFunc(console *Console, cmd string, arg []string) (string, error) { console.quit = true @@ -558,6 +570,13 @@ var cmdInfos = []cmdInfo{ newBaseCmd([]string{getShortcutsList}, getShortcutsFunc), ), }, + { + Short: getHistoryList, + Long: "show history of executed commands", + Cmd: newNoArgsCmdDecorator( + newBaseCmd([]string{getHistoryList}, getHistoryFunc), + ), + }, // The Tarantool console has `\quit` command, but it requires execute // access. { diff --git a/cli/connect/commands_test.go b/cli/connect/commands_test.go new file mode 100644 index 000000000..66a448b8b --- /dev/null +++ b/cli/connect/commands_test.go @@ -0,0 +1,52 @@ +package connect + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tarantool/tt/cli/connector" +) + +func TestConsoleHistoryOutput(t *testing.T) { + t.Run("empty command list", func(t *testing.T) { + history, err := newCommandHistory("test", 100) + assert.NoError(t, err) + console := Console{history: history} + actual, err := getHistoryFunc(&console, "", nil) + assert.NoError(t, err) + assert.Equal(t, "", actual) + }) + t.Run("history is less than max len", func(t *testing.T) { + history, err := newCommandHistory("test", 100) + history.appendCommand("test1") + history.appendCommand("test2") + assert.NoError(t, err) + console := Console{history: history, connOpts: connector.ConnectOpts{MaxOutputHistoryLen: 5}} + actual, err := getHistoryFunc(&console, "", nil) + assert.NoError(t, err) + assert.Equal(t, "test1\n-----\ntest2", actual) + }) + t.Run("history is equal max len", func(t *testing.T) { + history, err := newCommandHistory("test", 100) + history.appendCommand("test1") + history.appendCommand("test2") + history.appendCommand("test3") + assert.NoError(t, err) + console := Console{history: history, connOpts: connector.ConnectOpts{MaxOutputHistoryLen: 3}} + actual, err := getHistoryFunc(&console, "", nil) + assert.NoError(t, err) + assert.Equal(t, "test1\n-----\ntest2\n-----\ntest3", actual) + }) + t.Run("history is more max len", func(t *testing.T) { + history, err := newCommandHistory("test", 100) + history.appendCommand("test1") + history.appendCommand("test2") + history.appendCommand("test3") + history.appendCommand("test4") + assert.NoError(t, err) + console := Console{history: history, connOpts: connector.ConnectOpts{MaxOutputHistoryLen: 3}} + actual, err := getHistoryFunc(&console, "", nil) + assert.NoError(t, err) + assert.Equal(t, "test2\n-----\ntest3\n-----\ntest4", actual) + }) +} diff --git a/cli/connect/const.go b/cli/connect/const.go index 105862fc3..4af4de720 100644 --- a/cli/connect/const.go +++ b/cli/connect/const.go @@ -55,6 +55,9 @@ var setQuit = []string{"\\quit", "\\q"} // getShortcutsList is a command to get shortcuts and a list of hotkeys. const getShortcutsList = "\\shortcuts" +// getHistoryList is a command to get history of executed commands. +const getHistoryList = "\\history" + // getHelpCmd is a command to get a help message. var getHelp = []string{"\\help", "?"} @@ -82,3 +85,6 @@ const shortcutListText = `--- Alt + B -- Move backwards one word Alt + F -- Move forwards one word ...` + +// Default maximal count of commands to output in \history command. +const DefaultOutputHistorySize = 10 diff --git a/cli/connector/opts.go b/cli/connector/opts.go index 8159805ec..e7c7ea2bb 100644 --- a/cli/connector/opts.go +++ b/cli/connector/opts.go @@ -18,6 +18,8 @@ type ConnectOpts struct { Password string // Ssl options for a connection. Ssl SslOpts + // maximal count of commands to output in \history command. + MaxOutputHistoryLen int } // SslOpts is a way to configure SSL connection. diff --git a/test/integration/connect/test_connect.py b/test/integration/connect/test_connect.py index 6b643dc19..5720c37d1 100644 --- a/test/integration/connect/test_connect.py +++ b/test/integration/connect/test_connect.py @@ -186,6 +186,7 @@ def test_connect_and_get_commands_outputs(tt_cmd, tmpdir_with_cfg): \\x[l,t,T,y] -- set output format lua, table, ttable or yaml \\x[g,G] -- disables/enables pseudographics for table modes \\shortcuts -- show available hotkeys and shortcuts + \\history -- show history of executed commands \\quit, \\q -- quit from the console """