fix(setup): CompleteAsNode idempotent — konvertiert Standalone zu Cluster-Node
Wenn setup bereits abgeschlossen war (z.B. zuvor als Standalone eingerichtet), wird der Node jetzt ohne Fehler auf is_cluster_node=true umgestellt statt mit "setup already completed" zu fehlschlagen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ import (
|
||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||
)
|
||||
|
||||
var version = "1.1.151"
|
||||
var version = "1.1.152"
|
||||
|
||||
func main() {
|
||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
||||
)
|
||||
|
||||
var version = "1.1.151"
|
||||
var version = "1.1.152"
|
||||
|
||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import (
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||
)
|
||||
|
||||
var version = "1.1.151"
|
||||
var version = "1.1.152"
|
||||
|
||||
const (
|
||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||
|
||||
@@ -168,6 +168,10 @@ func (s *Store) Complete(req Request) (*State, error) {
|
||||
// CompleteAsNode configures this node as a secondary cluster member.
|
||||
// No admin credentials are created; login becomes available after PG
|
||||
// replication brings users from the primary.
|
||||
//
|
||||
// Idempotent: if setup was already completed (e.g. the node was
|
||||
// previously configured as standalone), it is converted to cluster-node
|
||||
// mode. Calling again after a successful join is a no-op.
|
||||
func (s *Store) CompleteAsNode(req NodeRequest) (*State, error) {
|
||||
if !looksLikeFQDN(req.FQDN) {
|
||||
return nil, fmt.Errorf("fqdn %q does not look like a fully-qualified hostname", req.FQDN)
|
||||
@@ -179,16 +183,22 @@ func (s *Store) CompleteAsNode(req NodeRequest) (*State, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prev.Completed {
|
||||
return nil, errors.New("setup already completed")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
completedAt := &now
|
||||
if prev.CompletedAt != nil {
|
||||
completedAt = prev.CompletedAt
|
||||
}
|
||||
st := &State{
|
||||
FQDN: strings.TrimSpace(req.FQDN),
|
||||
ACMEEmail: strings.ToLower(strings.TrimSpace(req.ACMEEmail)),
|
||||
IsClusterNode: true,
|
||||
Completed: true,
|
||||
CompletedAt: &now,
|
||||
CompletedAt: completedAt,
|
||||
// Carry over non-auth fields from previous state.
|
||||
LicenseKey: prev.LicenseKey,
|
||||
IPv6Enabled: prev.IPv6Enabled,
|
||||
BackupRetentionKeep: prev.BackupRetentionKeep,
|
||||
AuditRetentionDays: prev.AuditRetentionDays,
|
||||
}
|
||||
if err := s.Save(st); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user