Skip to content

Commit 0b8f301

Browse files
psergeeLeonidVas
authored andcommitted
cfg: implement cfg dump module
cfg dump prints tt environment configuration with all paths resolved. With --raw flag set, it prints config file contents as is. Closes #135
1 parent 89d3bdc commit 0b8f301

File tree

10 files changed

+368
-0
lines changed

10 files changed

+368
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
### Added
1111

1212
- Support of rocks repository specified in tt config.
13+
- ``cfg dump`` module. It prints tt environment configuration.
1314

1415
## [0.3.0] - 2022-12-05
1516

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,4 @@ Common description. For a detailed description, use ``tt help command`` .
420420
* ``uninstall`` - uninstall tarantool/tt.
421421
* ``init`` - create tt environment configuration file.
422422
* ``daemon (experimental)`` - manage tt daemon.
423+
* ``cfg dump`` - print tt environment configuration.

cli/cfg/dump.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cfg
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"io/ioutil"
7+
"os"
8+
9+
"github.com/tarantool/tt/cli/cmdcontext"
10+
"github.com/tarantool/tt/cli/config"
11+
"gopkg.in/yaml.v2"
12+
)
13+
14+
// DumpCtx contains information for tt config dump.
15+
type DumpCtx struct {
16+
// rawDump is a dump mode flag. If set, raw contents of tt configuration file is printed.
17+
RawDump bool
18+
}
19+
20+
// dumpRaw prints raw content of tt config file.
21+
func dumpRaw(writer io.Writer, cmdCtx *cmdcontext.CmdCtx) error {
22+
if cmdCtx.Cli.ConfigPath != "" {
23+
_, err := os.Stat(cmdCtx.Cli.ConfigPath)
24+
if err != nil {
25+
return err
26+
}
27+
fileContent, err := ioutil.ReadFile(cmdCtx.Cli.ConfigPath)
28+
if err != nil {
29+
return err
30+
}
31+
writer.Write([]byte(cmdCtx.Cli.ConfigPath + ":\n"))
32+
writer.Write(fileContent)
33+
} else {
34+
return fmt.Errorf("tt configuration file is not found")
35+
}
36+
37+
return nil
38+
}
39+
40+
// dumpConfiguration prints tt env configuration with all resolved paths.
41+
func dumpConfiguration(writer io.Writer, cmdCtx *cmdcontext.CmdCtx,
42+
cliOpts *config.CliOpts) error {
43+
if cmdCtx.Cli.ConfigPath != "" {
44+
if _, err := os.Stat(cmdCtx.Cli.ConfigPath); err == nil {
45+
writer.Write([]byte(cmdCtx.Cli.ConfigPath + ":\n"))
46+
}
47+
}
48+
err := yaml.NewEncoder(writer).Encode(config.Config{CliConfig: cliOpts})
49+
return err
50+
}
51+
52+
// RunDump prints tt configuration.
53+
func RunDump(writer io.Writer, cmdCtx *cmdcontext.CmdCtx, dumpCtx *DumpCtx,
54+
cliOpts *config.CliOpts) error {
55+
if dumpCtx.RawDump {
56+
return dumpRaw(writer, cmdCtx)
57+
}
58+
return dumpConfiguration(writer, cmdCtx, cliOpts)
59+
}

cli/cfg/dump_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package cfg
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
"github.com/tarantool/tt/cli/cmdcontext"
12+
"github.com/tarantool/tt/cli/config"
13+
"github.com/tarantool/tt/cli/configure"
14+
)
15+
16+
func TestRunDump(t *testing.T) {
17+
type args struct {
18+
cmdCtx *cmdcontext.CmdCtx
19+
dumpCtx *DumpCtx
20+
cliOpts *config.CliOpts
21+
}
22+
23+
cliOpts, configPath, err := configure.GetCliOpts("./testdata/tt_cfg.yaml")
24+
require.NoError(t, err)
25+
require.Equal(t, "testdata/tt_cfg.yaml", configPath)
26+
27+
cwd, err := os.Getwd()
28+
require.NoError(t, err)
29+
configDir := filepath.Join(cwd, "testdata")
30+
31+
tests := []struct {
32+
name string
33+
args args
34+
wantWriter string
35+
wantErr bool
36+
}{
37+
{
38+
name: "Raw dump of configuration file",
39+
args: args{
40+
&cmdcontext.CmdCtx{
41+
Cli: cmdcontext.CliCtx{
42+
ConfigPath: "./testdata/tt_cfg.yaml",
43+
},
44+
},
45+
&DumpCtx{RawDump: true},
46+
configure.GetDefaultCliOpts(),
47+
},
48+
wantWriter: `./testdata/tt_cfg.yaml:
49+
tt:
50+
app:
51+
inc_dir: ./test_inc
52+
log_maxsize: 1024
53+
modules:
54+
directory: /root/modules
55+
`,
56+
wantErr: false,
57+
},
58+
{
59+
name: "Default config dump",
60+
args: args{
61+
&cmdcontext.CmdCtx{
62+
Cli: cmdcontext.CliCtx{
63+
ConfigPath: "./testdata/tt_cfg.yaml",
64+
},
65+
},
66+
&DumpCtx{RawDump: false},
67+
cliOpts,
68+
},
69+
wantWriter: fmt.Sprintf(`./testdata/tt_cfg.yaml:
70+
tt:
71+
modules:
72+
directory: /root/modules
73+
app:
74+
run_dir: %[1]s/var/run
75+
log_dir: %[1]s/var/log
76+
log_maxsize: 1024
77+
log_maxage: 8
78+
log_maxbackups: 10
79+
restart_on_failure: false
80+
data_dir: %[1]s/var/lib
81+
bin_dir: %[1]s/bin
82+
inc_dir: %[1]s/test_inc
83+
instances_enabled: .
84+
ee:
85+
credential_path: ""
86+
templates: []
87+
repo:
88+
rocks: ""
89+
distfiles: %[1]s/distfiles
90+
`, configDir),
91+
wantErr: false,
92+
},
93+
}
94+
95+
for _, tt := range tests {
96+
t.Run(tt.name, func(t *testing.T) {
97+
writer := &bytes.Buffer{}
98+
err := RunDump(writer, tt.args.cmdCtx, tt.args.dumpCtx, tt.args.cliOpts)
99+
if tt.wantErr {
100+
require.Error(t, err)
101+
return
102+
} else {
103+
require.NoError(t, err)
104+
}
105+
gotWriter := writer.String()
106+
require.EqualValues(t, tt.wantWriter, gotWriter)
107+
})
108+
}
109+
}

cli/cfg/testdata/tt_cfg.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
tt:
2+
app:
3+
inc_dir: ./test_inc
4+
log_maxsize: 1024
5+
modules:
6+
directory: /root/modules

cli/cmd/cfg.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
// NewCfgCmd creates a new cfg command.
8+
func NewCfgCmd() *cobra.Command {
9+
var cfgCmd = &cobra.Command{
10+
Use: "cfg <command> [command flags]",
11+
DisableFlagParsing: true,
12+
DisableFlagsInUseLine: true,
13+
Short: "Environment configuration management utility",
14+
Run: func(cmd *cobra.Command, args []string) {
15+
cmd.Help()
16+
},
17+
Example: `# Print tt environment configuration:
18+
19+
$ tt cfg dump`,
20+
}
21+
cfgCmd.AddCommand(
22+
NewDumpCmd(),
23+
)
24+
25+
return cfgCmd
26+
}

cli/cmd/cfg_dump.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
6+
"github.com/spf13/cobra"
7+
"github.com/tarantool/tt/cli/cfg"
8+
"github.com/tarantool/tt/cli/cmdcontext"
9+
"github.com/tarantool/tt/cli/modules"
10+
)
11+
12+
var (
13+
rawDump bool
14+
)
15+
16+
// NewDumpCmd creates a new dump command.
17+
func NewDumpCmd() *cobra.Command {
18+
var dumpCmd = &cobra.Command{
19+
Use: "dump",
20+
Short: "Print environment configuration",
21+
Run: func(cmd *cobra.Command, args []string) {
22+
cmdCtx.CommandName = cmd.Name()
23+
err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo,
24+
internalDumpModule, args)
25+
handleCmdErr(cmd, err)
26+
},
27+
}
28+
29+
dumpCmd.Flags().BoolVarP(&rawDump, "raw", "r", false,
30+
"Display the raw contents of tt environment config.")
31+
32+
return dumpCmd
33+
}
34+
35+
// internalDumpModule is a default dump module.
36+
func internalDumpModule(cmdCtx *cmdcontext.CmdCtx, args []string) error {
37+
dumpCtx := cfg.DumpCtx{
38+
RawDump: rawDump,
39+
}
40+
41+
return cfg.RunDump(os.Stdout, cmdCtx, &dumpCtx, cliOpts)
42+
}

cli/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func NewCmdRoot() *cobra.Command {
120120
NewPackCmd(),
121121
NewInitCmd(),
122122
NewDaemonCmd(),
123+
NewCfgCmd(),
123124
)
124125
if err := injectCmds(rootCmd); err != nil {
125126
panic(err.Error())

test/integration/cfg/test_dump.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import os
2+
import shutil
3+
import subprocess
4+
5+
6+
def test_cfg_dump_default(tt_cmd, tmpdir):
7+
shutil.copy(os.path.join(os.path.dirname(__file__), "tt_cfg.yaml"),
8+
os.path.join(tmpdir, "tarantool.yaml"))
9+
10+
buid_cmd = [tt_cmd, "cfg", "dump"]
11+
tt_process = subprocess.Popen(
12+
buid_cmd,
13+
cwd=tmpdir,
14+
stderr=subprocess.STDOUT,
15+
stdout=subprocess.PIPE,
16+
stdin=subprocess.PIPE,
17+
text=True
18+
)
19+
tt_process.stdin.close()
20+
tt_process.wait()
21+
assert tt_process.returncode == 0
22+
23+
output = tt_process.stdout.read()
24+
assert "bin_dir: /usr/bin" in output
25+
assert "run_dir: /var/run" in output
26+
assert f"data_dir: {os.path.join(tmpdir, 'var', 'lib')}" in output
27+
assert f"log_dir: {os.path.join(tmpdir, 'var', 'log')}" in output
28+
assert f"inc_dir: {os.path.join(tmpdir, 'include')}" in output
29+
assert f"directory: {os.path.join(tmpdir, 'new_modules')}" in output
30+
assert f"distfiles: {os.path.join(tmpdir, 'distfiles')}" in output
31+
assert "log_maxsize: 100" in output
32+
assert "log_maxbackups: 12" in output
33+
assert "instances_enabled: ." in output
34+
assert "templates: []" in output
35+
assert 'credential_path: ""' in output
36+
37+
38+
def test_cfg_dump_raw(tt_cmd, tmpdir):
39+
shutil.copy(os.path.join(os.path.dirname(__file__), "tt_cfg.yaml"),
40+
os.path.join(tmpdir, "tarantool.yaml"))
41+
42+
buid_cmd = [tt_cmd, "cfg", "dump", "--raw"]
43+
tt_process = subprocess.Popen(
44+
buid_cmd,
45+
cwd=tmpdir,
46+
stderr=subprocess.STDOUT,
47+
stdout=subprocess.PIPE,
48+
stdin=subprocess.PIPE,
49+
text=True
50+
)
51+
tt_process.stdin.close()
52+
tt_process.wait()
53+
assert tt_process.returncode == 0
54+
55+
output = tt_process.stdout.read()
56+
assert output == f"""{os.path.join(tmpdir, "tarantool.yaml")}:
57+
tt:
58+
modules:
59+
directory: new_modules
60+
app:
61+
run_dir: /var/run
62+
log_dir: ./var/log
63+
log_maxbackups: 12
64+
data_dir: var/lib
65+
bin_dir: /usr/bin
66+
"""
67+
68+
69+
def test_cfg_dump_no_config(tt_cmd, tmpdir):
70+
buid_cmd = [tt_cmd, "cfg", "dump", "--raw"]
71+
tt_process = subprocess.Popen(
72+
buid_cmd,
73+
cwd=tmpdir,
74+
stderr=subprocess.STDOUT,
75+
stdout=subprocess.PIPE,
76+
stdin=subprocess.PIPE,
77+
text=True
78+
)
79+
tt_process.stdin.close()
80+
tt_process.wait()
81+
assert tt_process.returncode == 1
82+
83+
output = tt_process.stdout.read()
84+
assert "tt configuration file is not found" in output
85+
86+
87+
def test_cfg_dump_default_no_config(tt_cmd, tmpdir):
88+
buid_cmd = [tt_cmd, "cfg", "dump"]
89+
tt_process = subprocess.Popen(
90+
buid_cmd,
91+
cwd=tmpdir,
92+
stderr=subprocess.STDOUT,
93+
stdout=subprocess.PIPE,
94+
stdin=subprocess.PIPE,
95+
text=True
96+
)
97+
tt_process.stdin.close()
98+
tt_process.wait()
99+
assert tt_process.returncode == 0
100+
101+
output = tt_process.stdout.read()
102+
print(output)
103+
assert f"bin_dir: {os.path.join(tmpdir, 'bin')}" in output
104+
assert f"run_dir: {os.path.join(tmpdir, 'var', 'run')}" in output
105+
assert f"data_dir: {os.path.join(tmpdir, 'var', 'lib')}" in output
106+
assert f"log_dir: {os.path.join(tmpdir, 'var', 'log')}" in output
107+
assert f"inc_dir: {os.path.join(tmpdir, 'include')}" in output
108+
assert f"directory: {os.path.join(tmpdir, 'modules')}" in output
109+
assert f"distfiles: {os.path.join(tmpdir, 'distfiles')}" in output
110+
assert "log_maxsize: 100" in output
111+
assert "log_maxbackups: 10" in output
112+
assert "instances_enabled: ." in output
113+
assert f"templates:\n - path: {os.path.join(tmpdir, 'templates')}" in output
114+
assert 'credential_path: ""' in output

test/integration/cfg/tt_cfg.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tt:
2+
modules:
3+
directory: new_modules
4+
app:
5+
run_dir: /var/run
6+
log_dir: ./var/log
7+
log_maxbackups: 12
8+
data_dir: var/lib
9+
bin_dir: /usr/bin

0 commit comments

Comments
 (0)