Files
cc-backend/internal/fleet/servicetype.go
T
moebiusbandandClaude Opus 5 ecb1ba2bbe feat(fleet): add service type enum and infra scope
Replace the free-form service_type string with a ServiceType enum (ccms,
ccmc, ccb, cces, ccsa, ccnc, ccem). The short code is the canonical value
stored in the DB, the config-tree directory name and a NATS discovery
subject token; Description() gives the human-readable name.

Add a second scope alongside per-node agents: ScopeInfra covers
cluster-independent monitoring infrastructure services, which are
identified by (hostname, service_type) and carry an empty cluster.
InfraRegistry is the ScopeInfra sibling of Registry and shares the same
table, state machine and config-revision handshake. It deliberately
exposes no StartSweep: MarkStale ages rows by last_heartbeat regardless
of scope, so a single sweep goroutine per process covers both scopes.

On the repository side ServiceDB gains a Scope column and the two
queries the discovery publisher and infra enumeration need, ListActive()
and ListByScope(). ResetConnection() now also resets the fleet
repository singleton so tests get a fresh handle.

Migration 13 is edited in place rather than superseded by a new
migration: the service table is unreleased, so no existing deployment
has it yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 08:06:50 +02:00

86 lines
3.5 KiB
Go

// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
// All rights reserved. This file is part of cc-backend.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package fleet
// ServiceType is the kind of cc-* service a fleet member is. It is a
// string-backed enum (mirroring schema.MonitoringState and the local
// ScopeCluster/ScopeInfra constants): the short code is the canonical value
// stored in the DB, used as the config-tree directory name, and used as a NATS
// discovery-subject token. Description() gives the human-readable name.
type ServiceType string
const (
ServiceTypeMetricStore ServiceType = "ccms" // cc-metric-store
ServiceTypeCollector ServiceType = "ccmc" // cc-metric-collector
ServiceTypeBackend ServiceType = "ccb" // cc-backend
ServiceTypeEventStore ServiceType = "cces" // cc-event-store
ServiceTypeSlurmAdapter ServiceType = "ccsa" // cc-slurm-adapter
ServiceTypeNodeController ServiceType = "ccnc" // cc-node-controller
ServiceTypeEnergyManager ServiceType = "ccem" // cc-energy-manager
)
// AllServiceTypes lists every valid service type. Order is stable so callers
// that iterate (e.g. the discovery publisher) produce deterministic output.
var AllServiceTypes = []ServiceType{
ServiceTypeMetricStore,
ServiceTypeCollector,
ServiceTypeBackend,
ServiceTypeEventStore,
ServiceTypeSlurmAdapter,
ServiceTypeNodeController,
ServiceTypeEnergyManager,
}
var serviceTypeDescriptions = map[ServiceType]string{
ServiceTypeMetricStore: "cc-metric-store",
ServiceTypeCollector: "cc-metric-collector",
ServiceTypeBackend: "cc-backend",
ServiceTypeEventStore: "cc-event-store",
ServiceTypeSlurmAdapter: "cc-slurm-adapter",
ServiceTypeNodeController: "cc-node-controller",
ServiceTypeEnergyManager: "cc-energy-manager",
}
// Valid reports whether t is a known service type.
func (t ServiceType) Valid() bool {
_, ok := serviceTypeDescriptions[t]
return ok
}
// Description returns the full human-readable service name (e.g. "cc-metric-store"),
// or the raw code if unknown.
func (t ServiceType) Description() string {
if d, ok := serviceTypeDescriptions[t]; ok {
return d
}
return string(t)
}
// relevantProviders is the single source of truth for which provider service
// types each consumer type needs to discover. Keep it here and edit in one
// place.
//
// Confirmed universal edge: every service must reach cc-backend (ccb) to
// register and pull its config, so ccb is relevant to all of them. Richer
// peer edges (a collector wanting the metric store, an energy manager wanting
// the node controller, …) are left commented for an operator to enable once the
// concrete topology is settled — they are intentionally not assumed here.
var relevantProviders = map[ServiceType][]ServiceType{
ServiceTypeMetricStore: {ServiceTypeBackend},
ServiceTypeCollector: {ServiceTypeBackend /*, ServiceTypeMetricStore */},
ServiceTypeEventStore: {ServiceTypeBackend},
ServiceTypeSlurmAdapter: {ServiceTypeBackend},
ServiceTypeNodeController: {ServiceTypeBackend},
ServiceTypeEnergyManager: {ServiceTypeBackend /*, ServiceTypeMetricStore, ServiceTypeNodeController */},
ServiceTypeBackend: {}, // ccb discovers no peers by default
}
// RelevantProviders returns the provider service types that consumer should
// discover. The returned slice must not be mutated by callers.
func RelevantProviders(consumer ServiceType) []ServiceType {
return relevantProviders[consumer]
}