Improve modules and subsystems packages

This commit is contained in:
Daniel 2020-04-01 17:10:43 +02:00
parent bff3f5a1dd
commit 8d05b0b7c4
6 changed files with 107 additions and 17 deletions

View file

@ -1,14 +1,19 @@
package modules package modules
import "flag" import (
"flag"
"fmt"
)
var ( var (
// HelpFlag triggers printing flag.Usage. It's exported for custom help handling. // HelpFlag triggers printing flag.Usage. It's exported for custom help handling.
HelpFlag bool HelpFlag bool
printGraphFlag bool
) )
func init() { func init() {
flag.BoolVar(&HelpFlag, "help", false, "print help") flag.BoolVar(&HelpFlag, "help", false, "print help")
flag.BoolVar(&printGraphFlag, "print-module-graph", false, "print the module dependency graph")
} }
func parseFlags() error { func parseFlags() error {
@ -20,5 +25,36 @@ func parseFlags() error {
return ErrCleanExit return ErrCleanExit
} }
if printGraphFlag {
printGraph()
return ErrCleanExit
}
return nil return nil
} }
func printGraph() {
// mark roots
for _, module := range modules {
if len(module.depReverse) == 0 {
// is root, dont print deps in dep tree
module.stopFlag.Set()
}
}
// print
for _, module := range modules {
if module.stopFlag.IsSet() {
// print from root
printModuleGraph("", module, true)
}
}
}
func printModuleGraph(prefix string, module *Module, root bool) {
fmt.Printf("%s├── %s\n", prefix, module.Name)
if root || !module.stopFlag.IsSet() {
for _, dep := range module.Dependencies() {
printModuleGraph(fmt.Sprintf("│ %s", prefix), dep, false)
}
}
}

View file

@ -48,7 +48,9 @@ func Start() error {
// parse flags // parse flags
err = parseFlags() err = parseFlags()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "CRITICAL ERROR: failed to parse flags: %s\n", err) if err != ErrCleanExit {
fmt.Fprintf(os.Stderr, "CRITICAL ERROR: failed to parse flags: %s\n", err)
}
return err return err
} }
@ -92,7 +94,11 @@ func Start() error {
} }
// complete startup // complete startup
log.Infof("modules: started %d modules", len(modules)) if moduleMgmtEnabled.IsSet() {
log.Info("modules: initiated subsystems manager")
} else {
log.Infof("modules: started %d modules", len(modules))
}
go taskQueueHandler() go taskQueueHandler()
go taskScheduleHandler() go taskScheduleHandler()

View file

@ -16,6 +16,11 @@ var (
shutdownCompleteSignal = make(chan struct{}) shutdownCompleteSignal = make(chan struct{})
) )
// IsShuttingDown returns whether the global shutdown is in progress.
func IsShuttingDown() bool {
return shutdownFlag.IsSet()
}
// ShuttingDown returns a channel read on the global shutdown signal. // ShuttingDown returns a channel read on the global shutdown signal.
func ShuttingDown() <-chan struct{} { func ShuttingDown() <-chan struct{} {
return shutdownSignal return shutdownSignal

View file

@ -1,6 +1,9 @@
package subsystems package subsystems
import ( import (
"context"
"flag"
"fmt"
"strings" "strings"
"github.com/safing/portbase/database" "github.com/safing/portbase/database"
@ -14,7 +17,8 @@ const (
) )
var ( var (
module *modules.Module module *modules.Module
printGraphFlag bool
databaseKeySpace string databaseKeySpace string
db = database.NewInterface(nil) db = database.NewInterface(nil)
@ -30,9 +34,16 @@ func init() {
// register event for changes in the subsystem // register event for changes in the subsystem
module.RegisterEvent(subsystemsStatusChange) module.RegisterEvent(subsystemsStatusChange)
flag.BoolVar(&printGraphFlag, "print-subsystem-graph", false, "print the subsystem module dependency graph")
} }
func prep() error { func prep() error {
if printGraphFlag {
printGraph()
return modules.ErrCleanExit
}
return module.RegisterEventHook("config", configChangeEvent, "control subsystems", handleConfigChanges) return module.RegisterEventHook("config", configChangeEvent, "control subsystems", handleConfigChanges)
} }
@ -54,7 +65,10 @@ func start() error {
subsystemsLock.Unlock() subsystemsLock.Unlock()
// apply config // apply config
return handleConfigChanges(module.Ctx, nil) module.StartWorker("initial subsystem configuration", func(ctx context.Context) error {
return handleConfigChanges(module.Ctx, nil)
})
return nil
} }
func (sub *Subsystem) addDependencies(module *modules.Module, seen map[string]struct{}) { func (sub *Subsystem) addDependencies(module *modules.Module, seen map[string]struct{}) {
@ -81,3 +95,23 @@ func SetDatabaseKeySpace(keySpace string) {
} }
} }
} }
func printGraph() {
// mark roots
for _, sub := range subsystems {
sub.module.Enable() // mark as tree root
}
// print
for _, sub := range subsystems {
printModuleGraph("", sub.module, true)
}
}
func printModuleGraph(prefix string, module *modules.Module, root bool) {
fmt.Printf("%s├── %s\n", prefix, module.Name)
if root || !module.Enabled() {
for _, dep := range module.Dependencies() {
printModuleGraph(fmt.Sprintf("│ %s", prefix), dep, false)
}
}
}

View file

@ -14,6 +14,7 @@ type Subsystem struct { //nolint:maligned // not worth the effort
record.Base record.Base
sync.Mutex sync.Mutex
ID string
Name string Name string
Description string Description string
module *modules.Module module *modules.Module
@ -22,7 +23,7 @@ type Subsystem struct { //nolint:maligned // not worth the effort
Dependencies []*ModuleStatus Dependencies []*ModuleStatus
FailureStatus uint8 FailureStatus uint8
ToggleOptionKey string // empty == forced on ToggleOptionKey string
toggleOption *config.Option toggleOption *config.Option
toggleValue func() bool toggleValue func() bool
ExpertiseLevel uint8 // copied from toggleOption ExpertiseLevel uint8 // copied from toggleOption
@ -49,7 +50,9 @@ type ModuleStatus struct {
// Save saves the Subsystem Status to the database. // Save saves the Subsystem Status to the database.
func (sub *Subsystem) Save() { func (sub *Subsystem) Save() {
if databaseKeySpace != "" { if databaseKeySpace != "" {
// sub.SetKey() // FIXME if !sub.KeyIsSet() {
sub.SetKey(databaseKeySpace + sub.ID)
}
err := db.Put(sub) err := db.Put(sub)
if err != nil { if err != nil {
log.Errorf("subsystems: could not save subsystem status to database: %s", err) log.Errorf("subsystems: could not save subsystem status to database: %s", err)

View file

@ -2,7 +2,6 @@ package subsystems
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sync" "sync"
"time" "time"
@ -22,25 +21,26 @@ var (
handlingConfigChanges = abool.New() handlingConfigChanges = abool.New()
) )
// Register registers a new subsystem. The given option must be a bool option. Should be called in the module's prep function. The config option must not yet be registered and will be registered for you. // Register registers a new subsystem. The given option must be a bool option. Should be called in init() directly after the modules.Register() function. The config option must not yet be registered and will be registered for you. Pass a nil option to force enable.
func Register(name, description string, module *modules.Module, configKeySpace string, option *config.Option) error { func Register(id, name, description string, module *modules.Module, configKeySpace string, option *config.Option) {
// lock slice and map // lock slice and map
subsystemsLock.Lock() subsystemsLock.Lock()
defer subsystemsLock.Unlock() defer subsystemsLock.Unlock()
// check if registration is closed // check if registration is closed
if subsystemsLocked.IsSet() { if subsystemsLocked.IsSet() {
return errors.New("subsystems can only be registered in prep phase") panic("subsystems can only be registered in prep phase or earlier")
} }
// check if already registered // check if already registered
_, ok := subsystemsMap[name] _, ok := subsystemsMap[name]
if ok { if ok {
return fmt.Errorf(`subsystem "%s" already registered`, name) panic(fmt.Sprintf(`subsystem "%s" already registered`, name))
} }
// create new // create new
new := &Subsystem{ new := &Subsystem{
ID: id,
Name: name, Name: name,
Description: description, Description: description,
module: module, module: module,
@ -60,16 +60,17 @@ func Register(name, description string, module *modules.Module, configKeySpace s
if option != nil { if option != nil {
err := config.Register(option) err := config.Register(option)
if err != nil { if err != nil {
return fmt.Errorf("failed to register config: %s", err) panic(fmt.Sprintf("failed to register config: %s", err))
} }
new.toggleValue = config.GetAsBool(new.ToggleOptionKey, false)
} else {
// force enabled
new.toggleValue = func() bool { return true }
} }
new.toggleValue = config.GetAsBool(new.ToggleOptionKey, false)
// add to lists // add to lists
subsystemsMap[name] = new subsystemsMap[name] = new
subsystems = append(subsystems, new) subsystems = append(subsystems, new)
return nil
} }
func handleModuleChanges(m *modules.Module) { func handleModuleChanges(m *modules.Module) {
@ -78,6 +79,11 @@ func handleModuleChanges(m *modules.Module) {
return return
} }
// check if shutting down
if modules.IsShuttingDown() {
return
}
// find module status // find module status
var moduleSubsystem *Subsystem var moduleSubsystem *Subsystem
var moduleStatus *ModuleStatus var moduleStatus *ModuleStatus