diff --git a/CHANGELOG.md b/CHANGELOG.md index d7c49d6b8..b959e2870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Fixed + +- ``tt cartridge`` command takes into account run dir path from the `tt` environment. So most + of the `tt cartridge` sub-commands are able to work without specifying `--run-dir` option. + ## [1.0.1] - 2023-04-04 ### Added diff --git a/cli/cartridge/extra/006_consider_tt_run_dir.patch b/cli/cartridge/extra/006_consider_tt_run_dir.patch new file mode 100644 index 000000000..42f91c01c --- /dev/null +++ b/cli/cartridge/extra/006_consider_tt_run_dir.patch @@ -0,0 +1,66 @@ +diff --git a/cli/commands/admin.go b/cli/commands/admin.go +index 71b8f4f..e83efc0 100644 +--- a/cli/commands/admin.go ++++ b/cli/commands/admin.go +@@ -1,6 +1,7 @@ + package commands + + import ( ++ "os" + "path/filepath" + "strings" + +@@ -8,6 +9,7 @@ import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/tarantool/cartridge-cli/cli/admin" ++ "github.com/tarantool/cartridge-cli/cli/project" + ) + + var CartridgeCliAdmin *cobra.Command +@@ -74,6 +76,11 @@ func runAdminCommand(cmd *cobra.Command, args []string) error { + return err + } + ctx.Running.RunDir = abspath ++ } else { ++ runDir := os.Getenv(project.EnvRunDir) ++ if runDir != "" { ++ ctx.Running.RunDir = filepath.Join(runDir, ctx.Project.Name) ++ } + } + + // log level is usually set in rootCmd.PersistentPreRun +diff --git a/cli/project/project.go b/cli/project/project.go +index 2eed9cb..3d3a995 100644 +--- a/cli/project/project.go ++++ b/cli/project/project.go +@@ -12,6 +12,7 @@ import ( + ) + + const EnvInstEnabled = "TT_INST_ENABLED" ++const EnvRunDir = "TT_RUN_DIR" + + func FillCtx(ctx *context.Ctx) error { + var err error +@@ -30,7 +31,20 @@ func FillCtx(ctx *context.Ctx) error { + + instEnabled := os.Getenv(EnvInstEnabled) + if instEnabled != "" { +- ctx.Running.AppDir = filepath.Join(instEnabled, ctx.Project.Name) ++ if instEnabled == "." { ++ ctx.Running.AppDir, err = os.Getwd() ++ if err != nil { ++ return fmt.Errorf("Failed to get current directory: %s", err) ++ } ++ } else { ++ ctx.Running.AppDir = filepath.Join(instEnabled, ctx.Project.Name) ++ } ++ } ++ ++ runDir := os.Getenv(EnvRunDir) ++ if runDir != "" { ++ appName := filepath.Base(ctx.Running.AppDir) ++ ctx.Running.RunDir = filepath.Join(runDir, appName) + } + + ctx.Replicasets.File = filepath.Join(ctx.Running.AppDir, "replicasets.yml") diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 0c673035f..8edad63b9 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -212,6 +212,7 @@ func InitRoot() { // Required for cartridge. if cliOpts.App != nil { os.Setenv("TT_INST_ENABLED", cliOpts.App.InstancesEnabled) + os.Setenv("TT_RUN_DIR", cliOpts.App.RunDir) } // Getting modules information. diff --git a/cli/configure/configure.go b/cli/configure/configure.go index 7dab48ac4..99e13fbc9 100644 --- a/cli/configure/configure.go +++ b/cli/configure/configure.go @@ -167,7 +167,8 @@ func updateCliOpts(cliOpts *config.CliOpts, configDir string) error { if cliOpts.App.InstancesEnabled == "" { cliOpts.App.InstancesEnabled = "." - } else if cliOpts.App.InstancesEnabled != "." { + } else if cliOpts.App.InstancesEnabled != "." || (cliOpts.App.InstancesEnabled == "." && + !util.IsApp(configDir)) { if cliOpts.App.InstancesEnabled, err = adjustPathWithConfigLocation(cliOpts.App.InstancesEnabled, configDir, ""); err != nil { return err diff --git a/magefile.go b/magefile.go index 78716b52b..fe1ed6697 100644 --- a/magefile.go +++ b/magefile.go @@ -142,6 +142,7 @@ func PatchCC() error { "003_fix_work_paths.patch", "004_fix_warning.patch", "005_rename_tt_env.patch", + "006_consider_tt_run_dir.patch", } for _, patch := range patches { diff --git a/test/integration/cartridge/test_cartridge.py b/test/integration/cartridge/test_cartridge.py index 552e89102..d0927b2a4 100644 --- a/test/integration/cartridge/test_cartridge.py +++ b/test/integration/cartridge/test_cartridge.py @@ -4,6 +4,7 @@ import time import pytest +import yaml import utils from utils import run_command_and_get_output, wait_file @@ -91,22 +92,155 @@ def test_cartridge_base_functionality(tt_cmd, tmpdir_with_cfg): assert started is True - setup_cmd = [tt_cmd, "cartridge", "replicasets", "setup", - "--bootstrap-vshard", - "--name", cartridge_name, - "--run-dir", os.path.join(tmpdir, "var", "run", cartridge_name)] - setup_rc, setup_out = run_command_and_get_output(setup_cmd, cwd=tmpdir) - assert setup_rc == 0 - assert re.search(r'Bootstrap vshard task completed successfully', setup_out) - - admin_cmd = [tt_cmd, "cartridge", "admin", "probe", - "--conn", "admin:foo@localhost:3301", - "--uri", "localhost:3301", - "--run-dir", os.path.join(tmpdir, utils.run_path, cartridge_name)] - admin_rc, admin_out = run_command_and_get_output(admin_cmd, cwd=tmpdir) - assert admin_rc == 0 - assert re.search(r'Probe "localhost:3301": OK', admin_out) - - stop_cmd = [tt_cmd, "stop", cartridge_name] - stop_rc, stop_out = run_command_and_get_output(stop_cmd, cwd=tmpdir) - assert stop_rc == 0 + try: + setup_cmd = [tt_cmd, "cartridge", "replicasets", "setup", + "--bootstrap-vshard", + "--name", cartridge_name, + "--run-dir", os.path.join(tmpdir, "var", "run", cartridge_name)] + setup_rc, setup_out = run_command_and_get_output(setup_cmd, cwd=tmpdir) + assert setup_rc == 0 + assert re.search(r'Bootstrap vshard task completed successfully', setup_out) + + admin_cmd = [tt_cmd, "cartridge", "admin", "probe", + "--conn", "admin:foo@localhost:3301", + "--uri", "localhost:3301", + "--run-dir", os.path.join(tmpdir, utils.run_path, cartridge_name)] + admin_rc, admin_out = run_command_and_get_output(admin_cmd, cwd=tmpdir) + assert admin_rc == 0 + assert re.search(r'Probe "localhost:3301": OK', admin_out) + + # Admin call without --run-dir. + admin_cmd = [tt_cmd, "cartridge", "admin", "probe", + "--conn", "admin:foo@localhost:3301", + "--uri", "localhost:3301"] + admin_rc, admin_out = run_command_and_get_output(admin_cmd, cwd=tmpdir) + assert admin_rc == 0 + assert re.search(r'Probe "localhost:3301": OK', admin_out) + + # Test replicasets list without --run-dir. + rs_cmd = [tt_cmd, "cartridge", "replicasets", "list", "--name", cartridge_name] + rs_rc, rs_out = run_command_and_get_output(rs_cmd, cwd=tmpdir) + assert rs_rc == 0 + assert 'Current replica sets:' in rs_out + assert 'Role: failover-coordinator | vshard-router | app.roles.custom' in rs_out + + finally: + stop_cmd = [tt_cmd, "stop", cartridge_name] + stop_rc, stop_out = run_command_and_get_output(stop_cmd, cwd=tmpdir) + assert stop_rc == 0 + + +def test_cartridge_base_functionality_in_app_dir(tt_cmd, tmpdir_with_cfg): + tmpdir = tmpdir_with_cfg + create_cmd = [tt_cmd, "create", "cartridge", "--name", cartridge_name] + create_process = subprocess.Popen( + create_cmd, + cwd=tmpdir, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True + ) + create_process.stdin.writelines(["foo\n"]) + create_process.stdin.close() + create_process.wait() + + assert create_process.returncode == 0 + create_out = create_process.stdout.read() + assert re.search(r"Application '" + cartridge_name + "' created successfully", create_out) + + app_dir = os.path.join(tmpdir, cartridge_name) + + # Add cartridge config to simulate existing cartridge app. + config_path = os.path.join(app_dir, ".cartridge.yml") + with open(config_path, "w") as f: + yaml.dump({"stateboard": True}, f) + + # Generate tt env in application directory. + cmd = [tt_cmd, "init"] + rc, out = run_command_and_get_output(cmd, cwd=app_dir) + assert rc == 0 + assert 'Environment config is written to ' in out + + # Generate tt env in application directory. + cmd = [tt_cmd, "build"] + rc, out = run_command_and_get_output(cmd, cwd=app_dir) + assert rc == 0 + assert 'Application was successfully built' in out + + cmd = [tt_cmd, "start"] + subprocess.Popen( + cmd, + cwd=app_dir, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + text=True + ) + + instances = ["router", "stateboard", "s1-master", "s1-replica", "s2-master", "s2-replica"] + + # Wait for the full start of the cartridge. + try: + for inst in instances: + run_dir = os.path.join(app_dir, utils.run_path, cartridge_name, inst) + log_dir = os.path.join(app_dir, utils.log_path, cartridge_name, inst) + + file = wait_file(run_dir, inst + '.pid', []) + assert file != "" + file = wait_file(log_dir, inst + '.log', []) + assert file != "" + + started = False + trying = 0 + while not started: + if inst == "stateboard": + started = True + break + if trying == 200: + break + with open(os.path.join(log_dir, inst + '.log'), "r") as fp: + lines = fp.readlines() + lines = [line.rstrip() for line in lines] + for line in lines: + if re.search("Set default metrics endpoints", line): + started = True + break + fp.close() + time.sleep(0.05) + trying = trying + 1 + + assert started is True + + setup_cmd = [tt_cmd, "cartridge", "replicasets", "setup", + "--bootstrap-vshard"] + setup_rc, setup_out = run_command_and_get_output(setup_cmd, cwd=app_dir) + assert setup_rc == 0 + assert 'Bootstrap vshard task completed successfully' in setup_out + + # Test replicasets list without run-dir and app name + rs_cmd = [tt_cmd, "cartridge", "replicasets", "list"] + rs_rc, rs_out = run_command_and_get_output(rs_cmd, cwd=app_dir) + assert rs_rc == 0 + assert 'Current replica sets:' in rs_out + assert 'Role: failover-coordinator | vshard-router | app.roles.custom' in rs_out + + # Admin call without --run-dir. + admin_cmd = [tt_cmd, "cartridge", "admin", "probe", + "--conn", "admin:foo@localhost:3301", + "--uri", "localhost:3301"] + admin_rc, admin_out = run_command_and_get_output(admin_cmd, cwd=app_dir) + assert admin_rc == 0 + assert 'Probe "localhost:3301": OK' in admin_out + + # Failover command. + failover_cmd = [tt_cmd, "cartridge", "failover", "status"] + failover_rc, failover_out = run_command_and_get_output(failover_cmd, cwd=app_dir) + assert failover_rc == 0 + assert 'Current failover status:' in failover_out + + stop_cmd = [tt_cmd, "stop"] + stop_rc, _ = run_command_and_get_output(stop_cmd, cwd=app_dir) + assert stop_rc == 0 + + finally: + run_command_and_get_output([tt_cmd, "stop"], cwd=app_dir) diff --git a/test/integration/cfg/test_dump.py b/test/integration/cfg/test_dump.py index 8d8b15358..1f8602ea3 100644 --- a/test/integration/cfg/test_dump.py +++ b/test/integration/cfg/test_dump.py @@ -91,9 +91,45 @@ def test_cfg_dump_no_config(tt_cmd, tmpdir): def test_cfg_dump_default_no_config(tt_cmd, tmpdir): - buid_cmd = [tt_cmd, "cfg", "dump"] + dump_cmd = [tt_cmd, "cfg", "dump"] tt_process = subprocess.Popen( - buid_cmd, + dump_cmd, + cwd=tmpdir, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True + ) + tt_process.stdin.close() + tt_process.wait() + assert tt_process.returncode == 0 + + output = tt_process.stdout.read() + print(output) + assert f"bin_dir: {os.path.join(tmpdir, 'bin')}" in output + assert f"run_dir: {os.path.join(tmpdir, 'var', 'run')}" in output + assert f"wal_dir: {os.path.join(tmpdir, 'var', 'lib')}" in output + assert f"vinyl_dir: {os.path.join(tmpdir, 'var', 'lib')}" in output + assert f"memtx_dir: {os.path.join(tmpdir, 'var', 'lib')}" in output + assert f"log_dir: {os.path.join(tmpdir, 'var', 'log')}" in output + assert f"inc_dir: {os.path.join(tmpdir, 'include')}" in output + assert f"directory: {os.path.join(tmpdir, 'modules')}" in output + assert f"distfiles: {os.path.join(tmpdir, 'distfiles')}" in output + assert "log_maxsize: 100" in output + assert "log_maxbackups: 10" in output + assert f"instances_enabled: {tmpdir}" in output + assert f"templates:\n - path: {os.path.join(tmpdir, 'templates')}" in output + assert 'credential_path: ""' in output + + # Create init.lua in current dir making it an application. + + script_path = os.path.join(tmpdir, "init.lua") + with open(script_path, "w") as f: + f.write('print("hello")') + + dump_cmd = [tt_cmd, "cfg", "dump"] + tt_process = subprocess.Popen( + dump_cmd, cwd=tmpdir, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,