ananke/internal/config/startup_service_catalog.go

340 lines
9.8 KiB
Go
Raw Permalink Normal View History

package config
import "strings"
// defaultServiceChecklist runs one orchestration or CLI step.
// Signature: defaultServiceChecklist() []ServiceChecklistCheck.
// Why: startup must verify real external behavior per service (not only generic
// ingress reachability) so false positives do not pass drills.
func defaultServiceChecklist() []ServiceChecklistCheck {
return []ServiceChecklistCheck{
{
Name: "gitea-api",
URL: "https://scm.bstein.dev/api/healthz",
AcceptedStatuses: []int{200},
BodyContains: "pass",
TimeoutSeconds: 12,
},
{
Name: "grafana-api",
URL: "https://metrics.bstein.dev/api/health",
AcceptedStatuses: []int{200},
BodyContains: "\"database\":\"ok\"",
TimeoutSeconds: 12,
},
{
Name: "keycloak-oidc",
URL: "https://sso.bstein.dev/realms/atlas/.well-known/openid-configuration",
AcceptedStatuses: []int{200},
BodyContains: "\"issuer\":\"https://sso.bstein.dev/realms/atlas\"",
TimeoutSeconds: 12,
},
{
Name: "harbor-registry-api",
URL: "https://registry.bstein.dev/v2/",
AcceptedStatuses: []int{401},
BodyContains: "unauthorized",
TimeoutSeconds: 12,
},
{
Name: "alerts-ui",
URL: "https://alerts.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "Alertmanager",
TimeoutSeconds: 12,
},
{
Name: "auth-gateway-user-session",
URL: "https://auth.bstein.dev/",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
BodyContains: "Authenticated",
TimeoutSeconds: 12,
},
{
Name: "home-site",
URL: "https://bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "Titan Lab",
TimeoutSeconds: 12,
},
{
Name: "actual-budget-ui",
URL: "https://budget.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Actual",
TimeoutSeconds: 12,
},
{
Name: "element-call-ui",
URL: "https://call.live.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "Element Call",
TimeoutSeconds: 12,
},
{
Name: "flux-gitops-ui",
URL: "https://cd.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "Weave GitOps",
TimeoutSeconds: 12,
},
{
Name: "chat-ai-health",
URL: "https://chat.ai.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "\"ok\": true",
TimeoutSeconds: 12,
},
{
Name: "jenkins-auth-gate",
URL: "https://ci.bstein.dev/",
AcceptedStatuses: []int{403},
BodyContains: "commenceLogin",
TimeoutSeconds: 12,
},
{
Name: "nextcloud-login-redirect",
URL: "https://cloud.bstein.dev/",
AcceptedStatuses: []int{302},
LocationContains: "/index.php/login",
TimeoutSeconds: 12,
},
{
Name: "wger-redirect",
URL: "https://health.bstein.dev/",
AcceptedStatuses: []int{302},
LocationContains: "/en/",
TimeoutSeconds: 12,
},
{
Name: "livekit-edge",
URL: "https://kit.live.bstein.dev/",
AcceptedStatuses: []int{404},
BodyContains: "404 page not found",
TimeoutSeconds: 12,
},
{
Name: "element-web-ui",
URL: "https://live.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Element</title>",
TimeoutSeconds: 12,
},
{
Name: "logging-ui-user-session",
URL: "https://logs.bstein.dev/",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
FinalURLNotContains: "/protocol/openid-connect/auth",
BodyContains: "OpenSearch Dashboards",
TimeoutSeconds: 12,
},
{
Name: "logging-api-user-session",
URL: "https://logs.bstein.dev/api/status",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
BodyContains: "\"state\":\"green\"",
TimeoutSeconds: 12,
},
{
Name: "longhorn-api-user-session",
URL: "https://longhorn.bstein.dev/v1",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
FinalURLNotContains: "/protocol/openid-connect/auth",
BodyContains: "\"id\":\"v1\"",
TimeoutSeconds: 12,
},
{
Name: "matrix-auth-ui",
URL: "https://matrix.live.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "matrix-authentication-service",
TimeoutSeconds: 12,
},
{
Name: "monero-edge",
URL: "https://monero.bstein.dev/",
AcceptedStatuses: []int{404},
TimeoutSeconds: 12,
},
{
Name: "firefly-login-redirect",
URL: "https://money.bstein.dev/",
AcceptedStatuses: []int{302},
LocationContains: "/login",
TimeoutSeconds: 12,
},
{
Name: "outline-ui",
URL: "https://notes.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Outline</title>",
TimeoutSeconds: 12,
},
{
Name: "collabora-probe",
URL: "https://office.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "OK",
TimeoutSeconds: 12,
},
{
Name: "pegasus-ui",
URL: "https://pegasus.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Pegasus</title>",
TimeoutSeconds: 12,
},
{
Name: "harbor-ui",
URL: "https://registry.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Harbor</title>",
TimeoutSeconds: 12,
},
{
Name: "vault-ui-redirect",
URL: "https://secret.bstein.dev/",
AcceptedStatuses: []int{307},
LocationContains: "/ui/",
TimeoutSeconds: 12,
},
{
Name: "sentinel-user-session",
URL: "https://sentinel.bstein.dev/healthz",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
FinalURLNotContains: "/protocol/openid-connect/auth",
BodyContains: "ok",
TimeoutSeconds: 12,
},
{
Name: "keycloak-admin-user-session",
URL: "https://sso.bstein.dev/admin/",
AcceptedStatuses: []int{200},
RequireRobotAuth: true,
FollowRedirects: true,
FinalURLContains: "/admin/master/console/",
FinalURLNotContains: "/login-actions/authenticate",
BodyContains: "Keycloak Administration Console",
TimeoutSeconds: 12,
},
{
Name: "jellyfin-edge",
URL: "https://stream.bstein.dev/",
AcceptedStatuses: []int{302},
LocationContains: "web/",
TimeoutSeconds: 12,
},
{
Name: "planka-ui",
URL: "https://tasks.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>PLANKA</title>",
TimeoutSeconds: 12,
},
{
Name: "vaultwarden-ui",
URL: "https://vault.bstein.dev/",
AcceptedStatuses: []int{200},
BodyContains: "<title>Vaultwarden Web</title>",
TimeoutSeconds: 12,
},
}
}
// defaultCriticalServiceEndpoints runs one orchestration or CLI step.
// Signature: defaultCriticalServiceEndpoints() []string.
// Why: service edge checks are insufficient for protected stacks; endpoint
// presence verifies that backends are actually routable before startup success.
func defaultCriticalServiceEndpoints() []string {
return []string{
"monitoring/victoria-metrics-single-server",
"monitoring/grafana",
"monitoring/kube-state-metrics",
"logging/oauth2-proxy-logs",
"logging/opensearch-dashboards",
"logging/opensearch-master",
}
}
// mergeServiceChecklistDefaults runs one orchestration or CLI step.
// Signature: mergeServiceChecklistDefaults(existing, defaults []ServiceChecklistCheck) []ServiceChecklistCheck.
// Why: host configs can keep custom checks while still inheriting mandatory
// baseline checks introduced after incident learnings.
func mergeServiceChecklistDefaults(existing, defaults []ServiceChecklistCheck) []ServiceChecklistCheck {
if len(existing) == 0 {
out := make([]ServiceChecklistCheck, 0, len(defaults))
out = append(out, defaults...)
return out
}
defaultByName := map[string]struct{}{}
for _, check := range defaults {
name := strings.TrimSpace(check.Name)
if name == "" {
continue
}
defaultByName[name] = struct{}{}
}
out := make([]ServiceChecklistCheck, 0, len(defaults)+len(existing))
out = append(out, defaults...)
for _, check := range existing {
name := strings.TrimSpace(check.Name)
if name == "" {
continue
}
if _, exists := defaultByName[name]; exists {
continue
}
out = append(out, check)
}
return out
}
// mergeStringDefaults runs one orchestration or CLI step.
// Signature: mergeStringDefaults(existing, defaults []string) []string.
// Why: keeps baseline startup guards applied while preserving site-specific
// additions already declared in host configs.
func mergeStringDefaults(existing, defaults []string) []string {
if len(existing) == 0 {
out := make([]string, 0, len(defaults))
out = append(out, defaults...)
return out
}
seen := map[string]struct{}{}
out := make([]string, 0, len(existing)+len(defaults))
for _, item := range existing {
key := strings.TrimSpace(item)
if key == "" {
continue
}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, key)
}
for _, item := range defaults {
key := strings.TrimSpace(item)
if key == "" {
continue
}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, key)
}
return out
}