Skip to content

Commit 9810846

Browse files
r.inyakinbigbes
authored andcommitted
start: insufficient permissions to directories
Fixed a bug where an error did not appear when access rights to the var directive were insufficient. Closes #1238
1 parent b7accee commit 9810846

4 files changed

Lines changed: 126 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1313

1414
### Fixed
1515

16+
- `tt start`: fixed a bug where an error did not appear when access rights
17+
to the var directive were insufficient.
18+
1619
## [2.12.0] - 2026-03-30
1720

1821
This maintenance release marks the end of active development on the v2

cli/cmd/start.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sync"
1111
"syscall"
1212

13+
"github.com/apex/log"
1314
"github.com/spf13/cobra"
1415
"github.com/tarantool/tt/cli/cmd/internal"
1516
"github.com/tarantool/tt/cli/cmdcontext"
@@ -92,9 +93,12 @@ func startInstancesInteractive(cmdCtx *cmdcontext.CmdCtx, instances []running.In
9293
prefix := running.GetAppInstanceName(instCtx) + " "
9394
wg.Add(1)
9495
go func(inst running.InstanceCtx) {
95-
running.RunInstance(ctx, cmdCtx, inst,
96+
err := running.RunInstance(ctx, cmdCtx, inst,
9697
running.NewColorizedPrefixWriter(os.Stdout, clr, prefix),
9798
running.NewColorizedPrefixWriter(os.Stderr, clr, prefix))
99+
if err != nil {
100+
log.Error(err.Error())
101+
}
98102
wg.Done()
99103
}(instCtx)
100104
}

cli/running/running.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/tarantool/tt/cli/util/regexputil"
2727
libcluster "github.com/tarantool/tt/lib/cluster"
2828
"github.com/tarantool/tt/lib/integrity"
29+
"golang.org/x/sys/unix"
2930
)
3031

3132
const defaultDirPerms = 0o770
@@ -789,15 +790,19 @@ func RunInstance(ctx context.Context, cmdCtx *cmdcontext.CmdCtx, inst InstanceCt
789790

790791
// Start an Instance.
791792
func Start(cmdCtx *cmdcontext.CmdCtx, inst *InstanceCtx) error {
792-
if err := createInstanceDataDirectories(*inst); err != nil {
793-
return fmt.Errorf("failed to create a directory: %s", err)
794-
}
795793
logger, err := createLogger(inst)
796794
if err != nil {
797795
return fmt.Errorf("cannot create a logger: %s", err)
798796
}
799797
logger.Println("[INFO] Start") // Create a log file before any other actions.
800798

799+
if err := createInstanceDataDirectories(*inst); err != nil {
800+
logger.Fatalf("[ERROR] Failed to create data directories for instance %q: %s",
801+
inst.InstName,
802+
err)
803+
return fmt.Errorf("failed to create a directory: %s", err)
804+
}
805+
801806
provider := providerImpl{cmdCtx: cmdCtx, instanceCtx: inst}
802807
preStartAction := func() error {
803808
if err := process_utils.CreatePIDFile(inst.PIDFile, os.Getpid()); err != nil {
@@ -966,6 +971,17 @@ Cluster config path: %q`, tntVersion.Str, inst.ClusterConfigPath)
966971
func StartWatchdog(cmdCtx *cmdcontext.CmdCtx, ttExecutable string, instance InstanceCtx,
967972
args []string,
968973
) error {
974+
err := util.CreateDirectory(instance.LogDir, defaultDirPerms)
975+
if err != nil {
976+
return fmt.Errorf("failed to create log directory for instance %q: %s",
977+
GetAppInstanceName(instance), err)
978+
}
979+
980+
if err := syscall.Access(instance.LogDir, unix.W_OK); err != nil {
981+
return fmt.Errorf("instance %q has non-writable log directory %q: %s",
982+
instance.InstName, instance.RunDir, err)
983+
}
984+
969985
appName := GetAppInstanceName(instance)
970986
// If an instance is already running don't try to start it again.
971987
// To restart an instance use tt restart command.

test/integration/start/test_start.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
13
import pytest
24
import tt_helper
35

@@ -109,3 +111,100 @@ def test_start_multi_inst(tt, tt_app, target):
109111
)
110112
def test_start_cluster(tt, tt_app, target):
111113
check_start(tt, tt_app, target)
114+
115+
116+
################################################################
117+
# Permission denied
118+
119+
120+
@pytest.mark.skipif(skip_cluster_cond, reason=skip_cluster_reason)
121+
@pytest.mark.skipif(
122+
os.getuid() == 0,
123+
reason="Skipping the test, it shouldn't run as root",
124+
)
125+
@pytest.mark.tt_app(**tt_cluster_app)
126+
@pytest.mark.parametrize(
127+
"tt_running_targets",
128+
[
129+
pytest.param([], id="running:none"),
130+
],
131+
)
132+
@pytest.mark.parametrize(
133+
"denied_dir, expect_console_error",
134+
[
135+
pytest.param(["var"], True, id="var_denied"),
136+
pytest.param(["var", "log"], True, id="var_log_denied"),
137+
pytest.param(["var", "lib"], False, id="var_lib_denied"),
138+
],
139+
)
140+
def test_start_permission_denied(tt, tt_app, denied_dir, expect_console_error):
141+
target_dir = tt_app.path(*denied_dir)
142+
os.makedirs(target_dir, exist_ok=True)
143+
os.chmod(target_dir, 0o444)
144+
145+
try:
146+
rc, out = tt.exec("start")
147+
148+
if expect_console_error:
149+
assert rc != 0
150+
assert "permission denied" in out.lower()
151+
else:
152+
assert rc == 0
153+
154+
logs = list(tt_helper.log_files(tt_app, tt_app.instances))
155+
assert utils.wait_files(5, logs)
156+
157+
error_found = False
158+
for log_file in logs:
159+
with open(log_file, "r") as f:
160+
if "permission denied" in f.read().lower():
161+
error_found = True
162+
break
163+
164+
assert error_found, "Permission denied error was not found in logs."
165+
finally:
166+
os.chmod(target_dir, 0o755)
167+
168+
169+
@pytest.mark.skipif(skip_cluster_cond, reason=skip_cluster_reason)
170+
@pytest.mark.skipif(
171+
os.getuid() == 0,
172+
reason="Skipping the test, it shouldn't run as root",
173+
)
174+
@pytest.mark.tt_app(**tt_cluster_app)
175+
@pytest.mark.parametrize(
176+
"tt_running_targets",
177+
[
178+
pytest.param([], id="running:none"),
179+
],
180+
)
181+
def test_start_permission_denied_permissions_denied_files(tt, tt_app):
182+
rc, _ = tt.exec("start")
183+
assert rc == 0
184+
assert utils.wait_files(5, tt_helper.pid_files(tt_app, tt_app.instances))
185+
186+
rc, _ = tt.exec("stop", "-y")
187+
assert rc == 0
188+
189+
log_dir = tt_app.path("var", "log")
190+
lib_dir = tt_app.path("var", "lib")
191+
run_dir = tt_app.path("var", "run")
192+
193+
dirs_to_lock = []
194+
for data_dir in [log_dir, lib_dir, run_dir]:
195+
for root, dirs, files in os.walk(data_dir, topdown=False):
196+
dirs_to_lock.extend(os.path.join(root, f) for f in files)
197+
dirs_to_lock.extend(os.path.join(root, d) for d in dirs)
198+
dirs_to_lock.append(data_dir)
199+
200+
for d in dirs_to_lock:
201+
os.chmod(d, 0o444)
202+
203+
try:
204+
rc, out = tt.exec("start")
205+
assert rc != 0
206+
assert "permission denied" in out.lower()
207+
finally:
208+
for d in dirs_to_lock:
209+
if os.path.exists(d):
210+
os.chmod(d, 0o755)

0 commit comments

Comments
 (0)