128 lines
6.0 KiB
Go
128 lines
6.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"time"
|
|
|
|
"scm.bstein.dev/bstein/ananke/internal/config"
|
|
"scm.bstein.dev/bstein/ananke/internal/metrics"
|
|
)
|
|
|
|
type TestHookGitOpsRunner func(context.Context, string, ...string) ([]byte, error)
|
|
|
|
// TestHookParseGitRepositories exposes GitRepository parsing to the top-level
|
|
// testing module.
|
|
// Signature: TestHookParseGitRepositories(raw []byte) ([]metrics.GitOpsFluxSource, error).
|
|
// Why: Ananke keeps split-module tests outside internal packages, but the
|
|
// parser needs direct contract coverage without a live cluster.
|
|
func TestHookParseGitRepositories(raw []byte) ([]metrics.GitOpsFluxSource, error) {
|
|
return parseGitRepositories(raw)
|
|
}
|
|
|
|
// TestHookParseKustomizations exposes Kustomization parsing to the top-level
|
|
// testing module.
|
|
// Signature: TestHookParseKustomizations(raw []byte) ([]metrics.GitOpsKustomization, error).
|
|
// Why: dashboard-facing labels should be verified from representative Flux JSON
|
|
// without relying on the current production cluster shape.
|
|
func TestHookParseKustomizations(raw []byte) ([]metrics.GitOpsKustomization, error) {
|
|
return parseKustomizations(raw)
|
|
}
|
|
|
|
// TestHookParseHelmReleases exposes HelmRelease parsing to the top-level
|
|
// testing module.
|
|
// Signature: TestHookParseHelmReleases(raw []byte) ([]metrics.GitOpsHelmRelease, error).
|
|
// Why: Helm status fields vary by reconciliation phase, and split-module tests
|
|
// need a stable seam for parser coverage.
|
|
func TestHookParseHelmReleases(raw []byte) ([]metrics.GitOpsHelmRelease, error) {
|
|
return parseHelmReleases(raw)
|
|
}
|
|
|
|
// TestHookCollectGitOpsSnapshotWithRunner runs snapshot collection with an
|
|
// injected kubectl runner.
|
|
// Signature: TestHookCollectGitOpsSnapshotWithRunner(ctx context.Context, cfg config.Config, runner TestHookGitOpsRunner) metrics.GitOpsSnapshot.
|
|
// Why: this covers success and partial-failure collection paths without shelling
|
|
// out to kubectl from unit tests.
|
|
func TestHookCollectGitOpsSnapshotWithRunner(ctx context.Context, cfg config.Config, runner TestHookGitOpsRunner) metrics.GitOpsSnapshot {
|
|
original := gitOpsKubectlOutput
|
|
defer func() { gitOpsKubectlOutput = original }()
|
|
gitOpsKubectlOutput = runner
|
|
return collectGitOpsSnapshot(ctx, cfg)
|
|
}
|
|
|
|
// TestHookMaybeStartGitOpsSnapshot starts the daemon's background GitOps scrape
|
|
// from split-module tests.
|
|
// Signature: TestHookMaybeStartGitOpsSnapshot(ctx context.Context, cfg config.Config, exporter *metrics.Exporter, logger *log.Logger, lastRun *time.Time, running bool, done chan<- struct{}) bool.
|
|
// Why: the daemon intentionally keeps scraping asynchronous so GitOps telemetry
|
|
// cannot delay UPS polling; the behavior needs direct coverage.
|
|
func TestHookMaybeStartGitOpsSnapshot(ctx context.Context, cfg config.Config, exporter *metrics.Exporter, logger *log.Logger, lastRun *time.Time, running bool, done chan<- struct{}) bool {
|
|
d := &Daemon{cfg: cfg, exporter: exporter, log: logger}
|
|
return d.maybeStartGitOpsSnapshot(ctx, lastRun, running, done)
|
|
}
|
|
|
|
// TestHookMaybeStartGitOpsSnapshotWithRunner starts a background GitOps scrape
|
|
// with an injected runner and restores the production runner after completion.
|
|
// Signature: TestHookMaybeStartGitOpsSnapshotWithRunner(ctx context.Context, cfg config.Config, exporter *metrics.Exporter, logger *log.Logger, lastRun *time.Time, running bool, done chan<- struct{}, runner TestHookGitOpsRunner) bool.
|
|
// Why: the scrape is asynchronous, so split-module tests need a seam that keeps
|
|
// fake kubectl behavior installed until the goroutine exits.
|
|
func TestHookMaybeStartGitOpsSnapshotWithRunner(ctx context.Context, cfg config.Config, exporter *metrics.Exporter, logger *log.Logger, lastRun *time.Time, running bool, done chan<- struct{}, runner TestHookGitOpsRunner) bool {
|
|
original := gitOpsKubectlOutput
|
|
gitOpsKubectlOutput = runner
|
|
proxyDone := make(chan struct{}, 1)
|
|
d := &Daemon{cfg: cfg, exporter: exporter, log: logger}
|
|
started := d.maybeStartGitOpsSnapshot(ctx, lastRun, running, proxyDone)
|
|
if !started || running {
|
|
gitOpsKubectlOutput = original
|
|
return started
|
|
}
|
|
go func() {
|
|
<-proxyDone
|
|
gitOpsKubectlOutput = original
|
|
select {
|
|
case done <- struct{}{}:
|
|
default:
|
|
}
|
|
}()
|
|
return started
|
|
}
|
|
|
|
// TestHookRunGitOpsKubectlOutput exposes the production command runner to
|
|
// split-module tests.
|
|
// Signature: TestHookRunGitOpsKubectlOutput(ctx context.Context, name string, args ...string) ([]byte, error).
|
|
// Why: stderr preservation and empty-stderr failure behavior are part of the
|
|
// operator-facing diagnostics contract.
|
|
func TestHookRunGitOpsKubectlOutput(ctx context.Context, name string, args ...string) ([]byte, error) {
|
|
return runGitOpsKubectlOutput(ctx, name, args...)
|
|
}
|
|
|
|
// TestHookGitOpsDefaultString exposes defaultString to split-module tests.
|
|
// Signature: TestHookGitOpsDefaultString(value string, fallback string) string.
|
|
// Why: fallback label behavior is intentionally tiny but important for readable
|
|
// Grafana tables during startup.
|
|
func TestHookGitOpsDefaultString(value string, fallback string) string {
|
|
return defaultString(value, fallback)
|
|
}
|
|
|
|
// TestHookGitOpsFirstNonEmpty exposes firstNonEmpty to split-module tests.
|
|
// Signature: TestHookGitOpsFirstNonEmpty(values ...string) string.
|
|
// Why: revision fallback order should remain deterministic as Flux status
|
|
// payloads change.
|
|
func TestHookGitOpsFirstNonEmpty(values ...string) string {
|
|
return firstNonEmpty(values...)
|
|
}
|
|
|
|
// TestHookGitOpsErrorSummary exposes gitOpsErrorSummary to split-module tests.
|
|
// Signature: TestHookGitOpsErrorSummary(errors map[string]string) string.
|
|
// Why: sorted scrape warnings are easier to inspect in journald during recovery.
|
|
func TestHookGitOpsErrorSummary(errors map[string]string) string {
|
|
return gitOpsErrorSummary(errors)
|
|
}
|
|
|
|
// TestHookFluxReadyMissing exposes the missing Ready-condition branch to tests.
|
|
// Signature: TestHookFluxReadyMissing() (bool, string).
|
|
// Why: absent Ready conditions should be treated as not-ready and labelled
|
|
// explicitly instead of being mistaken for healthy.
|
|
func TestHookFluxReadyMissing() (bool, string) {
|
|
return fluxReady(nil)
|
|
}
|