service: harden daemon coverage for host quality gate
This commit is contained in:
parent
b229f47af8
commit
9732272d17
242
internal/service/daemon_coverage_closeout_test.go
Normal file
242
internal/service/daemon_coverage_closeout_test.go
Normal file
@ -0,0 +1,242 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"scm.bstein.dev/bstein/ananke/internal/config"
|
||||
"scm.bstein.dev/bstein/ananke/internal/metrics"
|
||||
"scm.bstein.dev/bstein/ananke/internal/ups"
|
||||
)
|
||||
|
||||
// TestDaemonRunMetricsEnabledBranchError runs one orchestration or CLI step.
|
||||
// Signature: TestDaemonRunMetricsEnabledBranchError(t *testing.T).
|
||||
// Why: covers Run path where metrics are enabled and startup fails before the ticker loop.
|
||||
func TestDaemonRunMetricsEnabledBranchError(t *testing.T) {
|
||||
stateDir := t.TempDir()
|
||||
orch := newDaemonTestOrchestrator(t, stateDir)
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
UPS: config.UPS{
|
||||
Enabled: true,
|
||||
PollSeconds: 1,
|
||||
RuntimeSafetyFactor: 1.0,
|
||||
DebounceCount: 1,
|
||||
},
|
||||
Metrics: config.Metrics{
|
||||
Enabled: true,
|
||||
BindAddr: "",
|
||||
Path: "/metrics",
|
||||
},
|
||||
State: config.State{
|
||||
IntentPath: filepath.Join(stateDir, "intent.json"),
|
||||
},
|
||||
},
|
||||
orch: orch,
|
||||
targets: []Target{
|
||||
{
|
||||
Name: "Pyrphoros",
|
||||
Target: "pyrphoros@localhost",
|
||||
Provider: &daemonFakeProvider{
|
||||
samples: []ups.Sample{{OnBattery: false, RuntimeSeconds: 10000, RawStatus: "OL"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
log: log.New(io.Discard, "", 0),
|
||||
exporter: metrics.New(),
|
||||
}
|
||||
err := d.Run(context.Background())
|
||||
if err == nil || !strings.Contains(err.Error(), "metrics.bind_addr must not be empty") {
|
||||
t.Fatalf("expected metrics bind error from Run metrics-enabled path, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDaemonRunProviderErrorContinueBranch runs one orchestration or CLI step.
|
||||
// Signature: TestDaemonRunProviderErrorContinueBranch(t *testing.T).
|
||||
// Why: covers provider-read error continue branch where telemetry timeout does not trigger.
|
||||
func TestDaemonRunProviderErrorContinueBranch(t *testing.T) {
|
||||
stateDir := t.TempDir()
|
||||
orch := newDaemonTestOrchestrator(t, stateDir)
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
UPS: config.UPS{
|
||||
Enabled: true,
|
||||
PollSeconds: 1,
|
||||
RuntimeSafetyFactor: 1.0,
|
||||
DebounceCount: 3,
|
||||
TelemetryTimeoutSeconds: 120,
|
||||
},
|
||||
State: config.State{
|
||||
IntentPath: filepath.Join(stateDir, "intent.json"),
|
||||
},
|
||||
},
|
||||
orch: orch,
|
||||
targets: []Target{
|
||||
{
|
||||
Name: "Statera",
|
||||
Target: "statera@localhost",
|
||||
Provider: &daemonFakeProvider{
|
||||
errs: []error{context.DeadlineExceeded, context.DeadlineExceeded, context.DeadlineExceeded},
|
||||
},
|
||||
},
|
||||
},
|
||||
log: log.New(io.Discard, "", 0),
|
||||
exporter: metrics.New(),
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1200*time.Millisecond)
|
||||
defer cancel()
|
||||
if err := d.Run(ctx); err == nil {
|
||||
t.Fatalf("expected context cancellation while exercising provider-error continue path")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTriggerShutdownForwardedIntentWriteFailureWarningBranch runs one orchestration or CLI step.
|
||||
// Signature: TestTriggerShutdownForwardedIntentWriteFailureWarningBranch(t *testing.T).
|
||||
// Why: covers triggerShutdown branch where forwarded completion intent write fails but trigger still succeeds.
|
||||
func TestTriggerShutdownForwardedIntentWriteFailureWarningBranch(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
sshPath := filepath.Join(tmp, "ssh")
|
||||
if err := os.WriteFile(sshPath, []byte("#!/usr/bin/env bash\nset -euo pipefail\necho forwarded\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh: %v", err)
|
||||
}
|
||||
t.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
|
||||
|
||||
intentPath := filepath.Join(tmp, "intent-dir")
|
||||
if err := os.MkdirAll(intentPath, 0o755); err != nil {
|
||||
t.Fatalf("mkdir intent dir: %v", err)
|
||||
}
|
||||
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
SSHUser: "atlas",
|
||||
SSHPort: 2277,
|
||||
State: config.State{
|
||||
IntentPath: intentPath,
|
||||
},
|
||||
Coordination: config.Coordination{
|
||||
ForwardShutdownHost: "titan-db",
|
||||
ForwardShutdownConfig: "/etc/ananke/ananke.yaml",
|
||||
CommandTimeoutSeconds: 3,
|
||||
},
|
||||
},
|
||||
log: log.New(io.Discard, "", 0),
|
||||
exporter: metrics.New(),
|
||||
}
|
||||
if err := d.triggerShutdown(context.Background(), "forwarded-intent-write-fail"); err != nil {
|
||||
t.Fatalf("expected forwarded shutdown to succeed even when completion intent write fails, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTriggerShutdownLocalIntentWriteFailureWarningBranch runs one orchestration or CLI step.
|
||||
// Signature: TestTriggerShutdownLocalIntentWriteFailureWarningBranch(t *testing.T).
|
||||
// Why: covers triggerShutdown local completion intent warning branch when write fails after successful shutdown.
|
||||
func TestTriggerShutdownLocalIntentWriteFailureWarningBranch(t *testing.T) {
|
||||
stateDir := t.TempDir()
|
||||
orch := newDaemonTestOrchestrator(t, stateDir)
|
||||
|
||||
intentPath := filepath.Join(stateDir, "intent-dir")
|
||||
if err := os.MkdirAll(intentPath, 0o755); err != nil {
|
||||
t.Fatalf("mkdir intent dir: %v", err)
|
||||
}
|
||||
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
State: config.State{
|
||||
IntentPath: intentPath,
|
||||
},
|
||||
Shutdown: config.Shutdown{
|
||||
EmergencySkipDrain: true,
|
||||
EmergencySkipEtcd: true,
|
||||
},
|
||||
},
|
||||
orch: orch,
|
||||
log: log.New(io.Discard, "", 0),
|
||||
exporter: metrics.New(),
|
||||
}
|
||||
if err := d.triggerShutdown(context.Background(), "local-intent-write-fail"); err != nil {
|
||||
t.Fatalf("expected local shutdown success even when completion intent write fails, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestForwardShutdownDefaultTimeoutBranch runs one orchestration or CLI step.
|
||||
// Signature: TestForwardShutdownDefaultTimeoutBranch(t *testing.T).
|
||||
// Why: covers forwardShutdown default command-timeout branch when timeout config is unset.
|
||||
func TestForwardShutdownDefaultTimeoutBranch(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
sshPath := filepath.Join(tmp, "ssh")
|
||||
if err := os.WriteFile(sshPath, []byte("#!/usr/bin/env bash\nset -euo pipefail\necho ok\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh: %v", err)
|
||||
}
|
||||
t.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
|
||||
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
SSHUser: "atlas",
|
||||
Coordination: config.Coordination{
|
||||
ForwardShutdownHost: "titan-db",
|
||||
ForwardShutdownConfig: "/etc/ananke/ananke.yaml",
|
||||
CommandTimeoutSeconds: 0,
|
||||
},
|
||||
},
|
||||
log: log.New(io.Discard, "", 0),
|
||||
}
|
||||
if err := d.forwardShutdown(context.Background(), "default-timeout-branch"); err != nil {
|
||||
t.Fatalf("expected forward shutdown with default timeout to succeed, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestForwardShutdownKnownHostsRepairIncludesJumpHost runs one orchestration or CLI step.
|
||||
// Signature: TestForwardShutdownKnownHostsRepairIncludesJumpHost(t *testing.T).
|
||||
// Why: covers known-host repair host-list branch that appends the configured SSH jump host.
|
||||
func TestForwardShutdownKnownHostsRepairIncludesJumpHost(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
attemptMarker := filepath.Join(tmp, "attempt")
|
||||
sshPath := filepath.Join(tmp, "ssh")
|
||||
script := `#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
marker="` + attemptMarker + `"
|
||||
if [[ ! -f "$marker" ]]; then
|
||||
echo "REMOTE HOST IDENTIFICATION HAS CHANGED!" >&2
|
||||
touch "$marker"
|
||||
exit 255
|
||||
fi
|
||||
echo "forwarded"
|
||||
`
|
||||
if err := os.WriteFile(sshPath, []byte(script), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh: %v", err)
|
||||
}
|
||||
sshKeygenPath := filepath.Join(tmp, "ssh-keygen")
|
||||
if err := os.WriteFile(sshKeygenPath, []byte("#!/usr/bin/env bash\nset -euo pipefail\nexit 0\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh-keygen: %v", err)
|
||||
}
|
||||
sshKeyscanPath := filepath.Join(tmp, "ssh-keyscan")
|
||||
if err := os.WriteFile(sshKeyscanPath, []byte("#!/usr/bin/env bash\nset -euo pipefail\necho fake-key\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh-keyscan: %v", err)
|
||||
}
|
||||
t.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
|
||||
|
||||
d := &Daemon{
|
||||
cfg: config.Config{
|
||||
SSHUser: "atlas",
|
||||
SSHPort: 2277,
|
||||
SSHConfigFile: filepath.Join(tmp, "ssh-config"),
|
||||
SSHIdentityFile: filepath.Join(tmp, "id_ed25519"),
|
||||
SSHJumpHost: "titan-jh",
|
||||
Coordination: config.Coordination{
|
||||
ForwardShutdownHost: "titan-db",
|
||||
ForwardShutdownConfig: "/etc/ananke/ananke.yaml",
|
||||
CommandTimeoutSeconds: 3,
|
||||
},
|
||||
},
|
||||
log: log.New(io.Discard, "", 0),
|
||||
}
|
||||
if err := d.forwardShutdown(context.Background(), "repair-includes-jump-host"); err != nil {
|
||||
t.Fatalf("expected forward shutdown to recover after known-host repair with jump host, got %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user