feat(auth): include authentication in workspace and tht diagnostics
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/authconfig"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/compose"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/config"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/output"
|
||||
@@ -98,7 +99,7 @@ func RunWithProbe(ctx context.Context, installation config.Installation, runner
|
||||
return Report{}, errors.New("doctor requires an HTTP probe")
|
||||
}
|
||||
secretValues := secretValues(installation)
|
||||
report := Report{Checks: make([]Check, 0, 11)}
|
||||
report := Report{Checks: make([]Check, 0, 12)}
|
||||
add := func(name, status, detail string) {
|
||||
report.Checks = append(report.Checks, Check{Name: name, Status: status, Detail: output.SanitizeDetail(detail, secretValues)})
|
||||
}
|
||||
@@ -117,6 +118,7 @@ func RunWithProbe(ctx context.Context, installation config.Installation, runner
|
||||
if !commandCheck(ctx, runner, []string{"version", "--format", "{{.Client.Version}}"}, secretValues, add, "docker", "Docker Engine") {
|
||||
add("compose", StatusSkipped, "Docker Engine is unavailable")
|
||||
add("configuration", StatusSkipped, "Docker Engine is unavailable")
|
||||
add("authentication", StatusSkipped, "core is unavailable")
|
||||
add("services", StatusSkipped, "Docker Engine is unavailable")
|
||||
add("core-http", StatusSkipped, "core is unavailable")
|
||||
add("frontend-http", StatusSkipped, "frontend is unavailable")
|
||||
@@ -127,6 +129,7 @@ func RunWithProbe(ctx context.Context, installation config.Installation, runner
|
||||
}
|
||||
if !commandCheck(ctx, runner, []string{"compose", "version", "--short"}, secretValues, add, "compose", "Docker Compose") {
|
||||
add("configuration", StatusSkipped, "Docker Compose is unavailable")
|
||||
add("authentication", StatusSkipped, "core is unavailable")
|
||||
add("services", StatusSkipped, "Docker Compose is unavailable")
|
||||
add("core-http", StatusSkipped, "core is unavailable")
|
||||
add("frontend-http", StatusSkipped, "frontend is unavailable")
|
||||
@@ -150,7 +153,7 @@ func RunWithProbe(ctx context.Context, installation config.Installation, runner
|
||||
add("configuration", StatusPassed, "Compose configuration and required volumes are valid")
|
||||
}
|
||||
|
||||
status, statusAvailable := serviceStatus(ctx, installation, runner, secretValues, add)
|
||||
status, statusAvailable, servicesCheck := serviceStatus(ctx, installation, runner, secretValues)
|
||||
coreRunning := false
|
||||
if statusAvailable {
|
||||
var err error
|
||||
@@ -159,6 +162,14 @@ func RunWithProbe(ctx context.Context, installation config.Installation, runner
|
||||
coreRunning = false
|
||||
}
|
||||
}
|
||||
if !configReady || !coreRunning {
|
||||
add("authentication", StatusSkipped, "core is unavailable")
|
||||
} else if authenticationCheck(ctx, installation, runner, secretValues) {
|
||||
add("authentication", StatusPassed, "container-local authentication diagnostics passed")
|
||||
} else {
|
||||
add("authentication", StatusFailed, "container-local authentication diagnostics failed")
|
||||
}
|
||||
add(servicesCheck.Name, servicesCheck.Status, servicesCheck.Detail)
|
||||
if !coreRunning {
|
||||
add("core-http", StatusSkipped, "core is not running")
|
||||
add("frontend-http", StatusSkipped, "core is not running")
|
||||
@@ -226,18 +237,33 @@ func commandCheck(ctx context.Context, runner Runner, args []string, secrets []s
|
||||
return true
|
||||
}
|
||||
|
||||
func serviceStatus(ctx context.Context, installation config.Installation, runner Runner, secrets []string, add func(string, string, string)) (string, bool) {
|
||||
func serviceStatus(ctx context.Context, installation config.Installation, runner Runner, secrets []string) (string, bool, Check) {
|
||||
result, err := runner.Run(ctx, installation.ComposeArgs("ps", "--all", "--format", "json"), nil)
|
||||
if err != nil {
|
||||
add("services", StatusFailed, commandDetail("Compose service status", result, err, secrets))
|
||||
return "", false
|
||||
return "", false, Check{Name: "services", Status: StatusFailed, Detail: commandDetail("Compose service status", result, err, secrets)}
|
||||
}
|
||||
if err := service.Healthy(result.Stdout); err != nil {
|
||||
add("services", StatusFailed, err.Error())
|
||||
return result.Stdout, true
|
||||
return result.Stdout, true, Check{Name: "services", Status: StatusFailed, Detail: err.Error()}
|
||||
}
|
||||
add("services", StatusPassed, "required services are running and reachable through Docker health checks")
|
||||
return result.Stdout, true
|
||||
return result.Stdout, true, Check{Name: "services", Status: StatusPassed, Detail: "required services are running and reachable through Docker health checks"}
|
||||
}
|
||||
|
||||
func authenticationCheck(ctx context.Context, installation config.Installation, runner Runner, secrets []string) bool {
|
||||
report, err := authconfig.Check(ctx, installation, runner, false, true)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if !report.Ready {
|
||||
return false
|
||||
}
|
||||
// Check performs stream redaction before decoding; retain this sanitization call as a boundary
|
||||
// if future report fields are added to the backend machine contract.
|
||||
for _, check := range report.Checks {
|
||||
if output.Sanitize(check.Message, secrets) != check.Message {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func workflowCheck(ctx context.Context, installation config.Installation, runner Runner, secrets []string, add func(string, string, string)) {
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestRunSkipsContainerDiagnosticsWhenCoreIsStopped(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if report.OK || checkStatus(report, "workflow") != "skipped" || checkStatus(report, "pi") != "skipped" {
|
||||
if report.OK || checkStatus(report, "authentication") != "skipped" || checkStatus(report, "workflow") != "skipped" || checkStatus(report, "pi") != "skipped" {
|
||||
t.Fatalf("Run() report = %#v, want stopped-core skips", report)
|
||||
}
|
||||
if strings.Contains(strings.Join(runner.calls, "\n"), " exec -T core ") {
|
||||
@@ -69,11 +69,14 @@ func TestRunUsesOnlyContainerLocalWorkflowAndPiDiagnosticsWhenCoreRuns(t *testin
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !report.OK || checkStatus(report, "workflow") != "passed" || checkStatus(report, "pi") != "passed" {
|
||||
if !report.OK || checkStatus(report, "authentication") != "passed" || checkStatus(report, "workflow") != "passed" || checkStatus(report, "pi") != "passed" {
|
||||
t.Fatalf("Run() report = %#v, want successful container diagnostics", report)
|
||||
}
|
||||
assertChecklist(t, report, []string{"descriptor", "files", "docker", "compose", "configuration", "services", "core-http", "frontend-http", "workspace-registry", "workflow", "pi"})
|
||||
assertChecklist(t, report, []string{"descriptor", "files", "docker", "compose", "configuration", "authentication", "services", "core-http", "frontend-http", "workspace-registry", "workflow", "pi"})
|
||||
calls := strings.Join(runner.calls, "\n")
|
||||
if !strings.Contains(calls, "exec -T core node dist/auth/diagnostic-command.js --json") {
|
||||
t.Fatalf("Run() calls = %s, want core-local authentication diagnostic", calls)
|
||||
}
|
||||
if !strings.Contains(calls, "exec -T core tht doctor --json") {
|
||||
t.Fatalf("Run() calls = %s, want core-local workflow doctor", calls)
|
||||
}
|
||||
@@ -203,6 +206,8 @@ func (r *doctorRunner) Run(_ context.Context, args []string, _ io.Reader) (compo
|
||||
return compose.Result{Stderr: r.workflowFailure, ExitCode: 23}, errors.New("workflow failed")
|
||||
}
|
||||
return compose.Result{Stdout: `{"ok":true,"checks":[]}`}, nil
|
||||
case strings.Contains(call, "dist/auth/diagnostic-command.js --json"):
|
||||
return compose.Result{Stdout: `{"ready":true,"mode":"oidc","checks":[{"level":"info","code":"auth_ready","message":"Authentication is ready."}]}`}, nil
|
||||
case strings.Contains(call, "workspace-registry/state/active.json"):
|
||||
if r.registryInvalid {
|
||||
return compose.Result{ExitCode: 23, Stderr: "invalid workspace registry"}, errors.New("registry invalid")
|
||||
|
||||
Reference in New Issue
Block a user