fix: make thothctl doctor portable

This commit is contained in:
2026-08-14 18:31:52 +02:00
parent a35f52790d
commit 59a04123ea
4 changed files with 59 additions and 5 deletions
+1
View File
@@ -29,6 +29,7 @@ tools/replay/web/
# === Secrets — NEVER commit ===
.env
harness/.env
harness/workspaces/psd.yaml
deploy/thothii.env
*.pem
ca-chain.pem
-1
View File
@@ -1 +0,0 @@
/Users/mp/projects/tht-workspace-psd/psd.yaml
+16 -3
View File
@@ -660,7 +660,7 @@ func requireLF(root string) error {
if walkErr != nil {
return walkErr
}
if entry.IsDir() || !requiresLF(entry.Name()) {
if entry.IsDir() || entry.Type()&os.ModeSymlink != 0 || !requiresLF(entry.Name()) {
return nil
}
contents, err := os.ReadFile(path)
@@ -700,13 +700,26 @@ func requireVolumes(renderedConfig string) error {
}
func requireHealthyServices(status string) error {
var services []struct {
type serviceStatus struct {
Service string `json:"Service"`
State string `json:"State"`
Health string `json:"Health"`
}
var services []serviceStatus
if err := json.Unmarshal([]byte(status), &services); err != nil {
return errors.New("Compose returned invalid service status")
decoder := json.NewDecoder(strings.NewReader(status))
for {
var service serviceStatus
if err := decoder.Decode(&service); errors.Is(err, io.EOF) {
break
} else if err != nil {
return errors.New("Compose returned invalid service status")
}
services = append(services, service)
}
if len(services) == 0 {
return errors.New("Compose returned invalid service status")
}
}
seen := map[string]bool{}
for _, service := range services {
+42 -1
View File
@@ -591,6 +591,46 @@ func TestRunDoctorValidatesTheRenderedInstallation(t *testing.T) {
}
}
func TestRunDoctorDoesNotDereferenceSymlinksDuringLineEndingCheck(t *testing.T) {
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
fixture.setEnvironment(t)
testsupport.SymlinkOrSkip(
t,
filepath.Join(fixture.projectDirectory, "missing-workspace.yaml"),
filepath.Join(fixture.projectDirectory, "legacy-workspace.yaml"),
)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "doctor"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("run() exit code = %d, stderr = %s", exitCode, stderr.String())
}
if stdout.String() != "Doctor checks passed.\n" {
t.Errorf("stdout = %q, want doctor success", stdout.String())
}
}
func TestRunDoctorAcceptsComposeJSONLinesServiceStatus(t *testing.T) {
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
fixture.setEnvironment(t)
t.Setenv(
"THOTHCTL_FAKE_PS",
"{\"Service\":\"core\",\"State\":\"running\",\"Health\":\"healthy\"}\n"+
"{\"Service\":\"frontend\",\"State\":\"running\",\"Health\":\"healthy\"}",
)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "doctor"}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("run() exit code = %d, stderr = %s", exitCode, stderr.String())
}
if stdout.String() != "Doctor checks passed.\n" {
t.Errorf("stdout = %q, want doctor success", stdout.String())
}
}
func TestRunPreservesChildExitCodes(t *testing.T) {
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
fixture.setEnvironment(t)
@@ -991,7 +1031,7 @@ case " $* " in
exit "$THOTHCTL_FAKE_MIGRATION_EXIT"
fi
printf '%s\n' '{"applied":[],"drifted":[],"pending":[]}' ;;
*" ps --format json "*) printf '%s\n' '[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"}]' ;;
*" ps --format json "*) printf '%s\n' "$THOTHCTL_FAKE_PS" ;;
*" ps -q core "*) printf '%s\n' 'core-id' ;;
*" image inspect --format {{.Id}} "*) printf '%s\n' "${THOTHCTL_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;;
*"inspect --format {{.Image}} core-id"*) printf '%s\n' "${THOTHCTL_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;;
@@ -1046,6 +1086,7 @@ func (f cliFixture) setEnvContents(t *testing.T, env string) {
t.Setenv("THOTHCTL_FAKE_FAILURE", "")
t.Setenv("THOTHCTL_FAKE_FAIL_ON", "")
t.Setenv("THOTHCTL_FAKE_STOPPED_PS", "[]")
t.Setenv("THOTHCTL_FAKE_PS", `[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"}]`)
t.Setenv("THOTHCTL_FAKE_CONFIG", "")
t.Setenv("THOTHCTL_FAKE_MIGRATION_FAILURE", "")
t.Setenv("THOTHCTL_FAKE_MIGRATION_EXIT", "0")