feat(cluster): Phase 3 Foundation — node.conf + ha_nodes-Drift + UI
Code-Vorbereitung für Multi-Node, ohne dass eine zweite Box nötig ist.
Single-Node-Mode bleibt der Default; alles existiert und wird sichtbar,
sobald ein 2. Knoten joined (Phase 3.2 später).
Migration 0020:
ha_nodes += version (edgeguard-api-Version)
config_hash (drift-Detection-Hash)
mgmt_ip (Management-IP, niemals VIP)
status (online|offline|joining|leaving|unknown)
internal/cluster/local_config.go:
/etc/edgeguard/node.conf — INI-style, node-lokale Identität:
NODE_ID, HOSTNAME, MGMT_IP, ROLE, PEER_HOSTS. NIEMALS zwischen
Cluster-Peers replizieren. LoadLocalConfig / SaveLocalConfig /
EnsureLocalConfig (auto-Generierung beim ersten Boot).
MgmtIP-Default = firstNonLoopbackIPv4(); Operator kann
überschreiben (mehrere Interfaces).
internal/cluster/store.go:
- HANode-Model um die 4 neuen Felder erweitert
- UpsertSelf nimmt jetzt mgmt_ip/version/config_hash/status, COALESCE
erhält werte wenn der Caller sie nicht setzt
- EnsureSelfRegistered-Signatur: + role + version-Argument
internal/handlers/cluster.go:
GET /api/v1/cluster/status — strukturierter Endpoint:
{local_id, local_node, peers[], mode, health, drift_found, updated_at}
GET /api/v1/cluster/nodes bleibt für Tools.
UI (pages/Cluster):
- Header zeigt Mode-Tag (Single-Node / Cluster) + Health-Tag (OK /
degraded / split-brain)
- Self-Card: Descriptions mit FQDN, Node-ID, Status, Role, Version,
MGMT-IP, API-URL, Config-Hash
- Peers-Tabelle nur wenn vorhanden, mit "drift"-Marker pro Row
- Drift-Alert-Banner wenn ein Peer einen anderen config_hash hat
- Single-Node-Mode Hinweis-Alert ("cluster-join kommt in 3.2")
postinst: leeres /etc/edgeguard/node.conf wird angelegt (chown
edgeguard); API auto-befüllt beim ersten boot.
main.go ruft EnsureLocalConfig + EnsureSelfRegistered mit version.
Verifiziert auf der Box (1.0.70):
- /etc/edgeguard/node.conf hat NODE_ID, HOSTNAME, MGMT_IP=89.163.205.6,
ROLE=primary
- ha_nodes-Row: status=online, version=1.0.70, mgmt_ip=89.163.205.6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,18 +2,25 @@ package models
|
||||
|
||||
import "time"
|
||||
|
||||
// HANode mirrort eine Row der ha_nodes-Tabelle. Erweitert in Migration
|
||||
// 0020 um version/config_hash/mgmt_ip/status für Cluster-Phase-3-
|
||||
// Drift-Detection + Health-State.
|
||||
type HANode struct {
|
||||
ID string `gorm:"column:id;primaryKey" json:"id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
FQDN string `gorm:"column:fqdn;uniqueIndex" json:"fqdn"`
|
||||
APIURL string `gorm:"column:api_url" json:"api_url"`
|
||||
PublicIP *string `gorm:"column:public_ip;type:inet" json:"public_ip,omitempty"`
|
||||
InternalIP *string `gorm:"column:internal_ip;type:inet" json:"internal_ip,omitempty"`
|
||||
Role string `gorm:"column:role" json:"role"`
|
||||
LastSeen *time.Time `gorm:"column:last_seen" json:"last_seen,omitempty"`
|
||||
JoinedAt time.Time `gorm:"column:joined_at" json:"joined_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
ID string `gorm:"column:id;primaryKey" json:"id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
FQDN string `gorm:"column:fqdn;uniqueIndex" json:"fqdn"`
|
||||
APIURL string `gorm:"column:api_url" json:"api_url"`
|
||||
PublicIP *string `gorm:"column:public_ip;type:inet" json:"public_ip,omitempty"`
|
||||
InternalIP *string `gorm:"column:internal_ip;type:inet" json:"internal_ip,omitempty"`
|
||||
MgmtIP *string `gorm:"column:mgmt_ip;type:inet" json:"mgmt_ip,omitempty"`
|
||||
Role string `gorm:"column:role" json:"role"`
|
||||
Version *string `gorm:"column:version" json:"version,omitempty"`
|
||||
ConfigHash *string `gorm:"column:config_hash" json:"config_hash,omitempty"`
|
||||
Status string `gorm:"column:status" json:"status"`
|
||||
LastSeen *time.Time `gorm:"column:last_seen" json:"last_seen,omitempty"`
|
||||
JoinedAt time.Time `gorm:"column:joined_at" json:"joined_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (HANode) TableName() string { return "ha_nodes" }
|
||||
|
||||
Reference in New Issue
Block a user