mirror of
https://github.com/AlchemillaHQ/Sylve.git
synced 2026-06-14 00:46:34 +03:00
main: startup: gate initialization
This commit is contained in:
+30
-8
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user