main: startup: gate initialization

This commit is contained in:
hayzam
2026-03-26 21:19:23 +05:30
parent eafb681c8a
commit 1e5d978f0b
3 changed files with 189 additions and 8 deletions
+30 -8
View File
@@ -22,6 +22,7 @@ import (
"github.com/alchemillahq/sylve/internal/cmd"
"github.com/alchemillahq/sylve/internal/config"
"github.com/alchemillahq/sylve/internal/db"
dbModels "github.com/alchemillahq/sylve/internal/db/models"
clusterModels "github.com/alchemillahq/sylve/internal/db/models/cluster"
"github.com/alchemillahq/sylve/internal/handlers"
"github.com/alchemillahq/sylve/internal/logger"
@@ -134,22 +135,43 @@ func main() {
defer initCancel()
err = sS.Initialize(aS.(*auth.Service), initContext, qCtx)
go sysS.StartNetlinkWatcher(qCtx)
go sysS.NetlinkEventsCleaner(qCtx)
go libvirtSvc.StartLifecycleWatcher(qCtx)
go db.StartQueue(qCtx)
if err != nil {
logger.L.Fatal().Err(err).Msg("Failed to initialize at startup")
} else {
logger.L.Info().Msg("Basic initializations complete")
}
logger.L.Info().Msg("Basic initializations complete")
startAdvancedStartupWorkers, basicSettings, settingsErr := shouldStartAdvancedStartupWorkers(func() (dbModels.BasicSettings, error) {
var settings dbModels.BasicSettings
if err := d.First(&settings).Error; err != nil {
return dbModels.BasicSettings{}, err
}
return settings, nil
})
if settingsErr != nil {
logger.L.Fatal().Err(settingsErr).Msg("Failed to evaluate startup readiness")
}
go db.StartQueue(qCtx)
if startAdvancedStartupWorkers {
go sysS.StartNetlinkWatcher(qCtx)
go sysS.NetlinkEventsCleaner(qCtx)
if libvirtSvc.IsVirtualizationEnabled() {
go libvirtSvc.StartLifecycleWatcher(qCtx)
}
enqueueCtx, enqueueCancel := context.WithTimeout(context.Background(), 10*time.Second)
if enqueueErr := lifecycleSvc.EnqueueStartupAutostart(enqueueCtx); enqueueErr != nil {
logger.L.Warn().Err(enqueueErr).Msg("failed_to_enqueue_guest_autostart_sequence")
}
enqueueCancel()
} else {
logger.L.Info().
Bool("initialized", basicSettings.Initialized).
Bool("restarted", basicSettings.Restarted).
Msg("System startup not finalized; skipping advanced watchers and autostart queue")
}
err = cS.InitRaft(fsm)
+37
View File
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: BSD-2-Clause
//
// Copyright (c) 2025 The FreeBSD Foundation.
//
// This software was developed by Hayzam Sherif <hayzam@alchemilla.io>
// of Alchemilla Ventures Pvt. Ltd. <hello@alchemilla.io>,
// under sponsorship from the FreeBSD Foundation.
package main
import (
"errors"
"fmt"
"github.com/alchemillahq/sylve/internal/db/models"
"gorm.io/gorm"
)
type basicSettingsLookup func() (models.BasicSettings, error)
func shouldStartAdvancedStartupWorkers(lookup basicSettingsLookup) (bool, models.BasicSettings, error) {
if lookup == nil {
return false, models.BasicSettings{}, fmt.Errorf("basic_settings_lookup_required")
}
settings, err := lookup()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return false, models.BasicSettings{}, nil
}
return false, models.BasicSettings{}, fmt.Errorf("failed_to_fetch_basic_settings: %w", err)
}
return settings.Initialized && settings.Restarted, settings, nil
}
+122
View File
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: BSD-2-Clause
//
// Copyright (c) 2025 The FreeBSD Foundation.
//
// This software was developed by Hayzam Sherif <hayzam@alchemilla.io>
// of Alchemilla Ventures Pvt. Ltd. <hello@alchemilla.io>,
// under sponsorship from the FreeBSD Foundation.
package main
import (
"errors"
"strings"
"testing"
"github.com/alchemillahq/sylve/internal/db/models"
"gorm.io/gorm"
)
func TestShouldStartAdvancedStartupWorkersMissingLookup(t *testing.T) {
_, _, err := shouldStartAdvancedStartupWorkers(nil)
if err == nil {
t.Fatalf("expected error when lookup is nil")
}
if !strings.Contains(err.Error(), "basic_settings_lookup_required") {
t.Fatalf("expected missing lookup error, got: %v", err)
}
}
func TestShouldStartAdvancedStartupWorkersNoBasicSettingsYet(t *testing.T) {
enabled, settings, err := shouldStartAdvancedStartupWorkers(func() (models.BasicSettings, error) {
return models.BasicSettings{}, gorm.ErrRecordNotFound
})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if enabled {
t.Fatalf("expected advanced startup workers to stay disabled when settings are missing")
}
if settings.Initialized || settings.Restarted {
t.Fatalf("expected zero-value settings when basic settings are missing, got %+v", settings)
}
}
func TestShouldStartAdvancedStartupWorkersRequiresBothFlags(t *testing.T) {
cases := []struct {
name string
settings models.BasicSettings
enabled bool
}{
{
name: "initialized false restarted false",
settings: models.BasicSettings{
Initialized: false,
Restarted: false,
},
enabled: false,
},
{
name: "initialized true restarted false",
settings: models.BasicSettings{
Initialized: true,
Restarted: false,
},
enabled: false,
},
{
name: "initialized false restarted true",
settings: models.BasicSettings{
Initialized: false,
Restarted: true,
},
enabled: false,
},
{
name: "initialized true restarted true",
settings: models.BasicSettings{
Initialized: true,
Restarted: true,
},
enabled: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
enabled, settings, err := shouldStartAdvancedStartupWorkers(func() (models.BasicSettings, error) {
return tc.settings, nil
})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if enabled != tc.enabled {
t.Fatalf("expected enabled=%t, got %t", tc.enabled, enabled)
}
if settings.Initialized != tc.settings.Initialized || settings.Restarted != tc.settings.Restarted {
t.Fatalf("expected settings %+v, got %+v", tc.settings, settings)
}
})
}
}
func TestShouldStartAdvancedStartupWorkersLookupFailure(t *testing.T) {
lookupErr := errors.New("db_timeout")
enabled, _, err := shouldStartAdvancedStartupWorkers(func() (models.BasicSettings, error) {
return models.BasicSettings{}, lookupErr
})
if err == nil {
t.Fatalf("expected error, got nil")
}
if enabled {
t.Fatalf("expected advanced startup workers disabled on lookup failure")
}
if !strings.Contains(err.Error(), "failed_to_fetch_basic_settings") {
t.Fatalf("expected wrapped lookup error, got: %v", err)
}
if !errors.Is(err, lookupErr) {
t.Fatalf("expected wrapped error %v, got %v", lookupErr, err)
}
}