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
import "flag"
import (
"flag"
"fmt"
)
var (
// HelpFlag triggers printing flag.Usage. It's exported for custom help handling.
HelpFlag bool
HelpFlag bool
printGraphFlag bool
)
func init() {
flag.BoolVar(&HelpFlag, "help", false, "print help")
flag.BoolVar(&printGraphFlag, "print-module-graph", false, "print the module dependency graph")
}
func parseFlags() error {
@ -20,5 +25,36 @@ func parseFlags() error {
return ErrCleanExit
}
if printGraphFlag {
printGraph()
return ErrCleanExit
}
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
err = parseFlags()
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
}
@ -92,7 +94,11 @@ func Start() error {
}
// 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 taskScheduleHandler()

View file

@ -16,6 +16,11 @@ var (
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.
func ShuttingDown() <-chan struct{} {
return shutdownSignal

View file

@ -1,6 +1,9 @@
package subsystems
import (
"context"
"flag"
"fmt"
"strings"
"github.com/safing/portbase/database"
@ -14,7 +17,8 @@ const (
)
var (
module *modules.Module
module *modules.Module
printGraphFlag bool
databaseKeySpace string
db = database.NewInterface(nil)
@ -30,9 +34,16 @@ func init() {
// register event for changes in the subsystem
module.RegisterEvent(subsystemsStatusChange)
flag.BoolVar(&printGraphFlag, "print-subsystem-graph", false, "print the subsystem module dependency graph")
}
func prep() error {
if printGraphFlag {
printGraph()
return modules.ErrCleanExit
}
return module.RegisterEventHook("config", configChangeEvent, "control subsystems", handleConfigChanges)
}
@ -54,7 +65,10 @@ func start() error {
subsystemsLock.Unlock()
// 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{}) {
@ -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
sync.Mutex
ID string
Name string
Description string
module *modules.Module
@ -22,7 +23,7 @@ type Subsystem struct { //nolint:maligned // not worth the effort
Dependencies []*ModuleStatus
FailureStatus uint8
ToggleOptionKey string // empty == forced on
ToggleOptionKey string
toggleOption *config.Option
toggleValue func() bool
ExpertiseLevel uint8 // copied from toggleOption
@ -49,7 +50,9 @@ type ModuleStatus struct {
// Save saves the Subsystem Status to the database.
func (sub *Subsystem) Save() {
if databaseKeySpace != "" {
// sub.SetKey() // FIXME
if !sub.KeyIsSet() {
sub.SetKey(databaseKeySpace + sub.ID)
}
err := db.Put(sub)
if err != nil {
log.Errorf("subsystems: could not save subsystem status to database: %s", err)

View file

@ -2,7 +2,6 @@ package subsystems
import (
"context"
"errors"
"fmt"
"sync"
"time"
@ -22,25 +21,26 @@ var (
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.
func Register(name, description string, module *modules.Module, configKeySpace string, option *config.Option) error {
// 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(id, name, description string, module *modules.Module, configKeySpace string, option *config.Option) {
// lock slice and map
subsystemsLock.Lock()
defer subsystemsLock.Unlock()
// check if registration is closed
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
_, ok := subsystemsMap[name]
if ok {
return fmt.Errorf(`subsystem "%s" already registered`, name)
panic(fmt.Sprintf(`subsystem "%s" already registered`, name))
}
// create new
new := &Subsystem{
ID: id,
Name: name,
Description: description,
module: module,
@ -60,16 +60,17 @@ func Register(name, description string, module *modules.Module, configKeySpace s
if option != nil {
err := config.Register(option)
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
subsystemsMap[name] = new
subsystems = append(subsystems, new)
return nil
}
func handleModuleChanges(m *modules.Module) {
@ -78,6 +79,11 @@ func handleModuleChanges(m *modules.Module) {
return
}
// check if shutting down
if modules.IsShuttingDown() {
return
}
// find module status
var moduleSubsystem *Subsystem
var moduleStatus *ModuleStatus