github.com/safing/portbase@v0.19.5/modules/subsystems/subsystem.go (about)

     1  package subsystems
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/safing/portbase/config"
     7  	"github.com/safing/portbase/database/record"
     8  	"github.com/safing/portbase/modules"
     9  )
    10  
    11  // Subsystem describes a subset of modules that represent a part of a
    12  // service or program to the user. Subsystems can be (de-)activated causing
    13  // all related modules to be brought down or up.
    14  type Subsystem struct { //nolint:maligned // not worth the effort
    15  	record.Base
    16  	sync.Mutex
    17  	// ID is a unique identifier for the subsystem.
    18  	ID string
    19  	// Name holds a human readable name of the subsystem.
    20  	Name string
    21  	// Description may holds an optional description of
    22  	// the subsystem's purpose.
    23  	Description string
    24  	// Modules contains all modules that are related to the subsystem.
    25  	// Note that this slice also contains a reference to the subsystem
    26  	// module itself.
    27  	Modules []*ModuleStatus
    28  	// FailureStatus is the worst failure status that is currently
    29  	// set in one of the subsystem's dependencies.
    30  	FailureStatus uint8
    31  	// ToggleOptionKey holds the key of the configuration option
    32  	// that is used to completely enable or disable this subsystem.
    33  	ToggleOptionKey string
    34  	// ExpertiseLevel defines the complexity of the subsystem and is
    35  	// copied from the subsystem's toggleOption.
    36  	ExpertiseLevel config.ExpertiseLevel
    37  	// ReleaseLevel defines the stability of the subsystem and is
    38  	// copied form the subsystem's toggleOption.
    39  	ReleaseLevel config.ReleaseLevel
    40  	// ConfigKeySpace defines the database key prefix that all
    41  	// options that belong to this subsystem have. Note that this
    42  	// value is mainly used to mark all related options with a
    43  	// config.SubsystemAnnotation. Options that are part of
    44  	// this subsystem but don't start with the correct prefix can
    45  	// still be marked by manually setting the appropriate annotation.
    46  	ConfigKeySpace string
    47  
    48  	module       *modules.Module
    49  	toggleOption *config.Option
    50  	toggleValue  config.BoolOption
    51  }
    52  
    53  // ModuleStatus describes the status of a module.
    54  type ModuleStatus struct {
    55  	Name   string
    56  	module *modules.Module
    57  
    58  	// status mgmt
    59  	Enabled bool
    60  	Status  uint8
    61  
    62  	// failure status
    63  	FailureStatus uint8
    64  	FailureID     string
    65  	FailureMsg    string
    66  }
    67  
    68  func (sub *Subsystem) addDependencies(module *modules.Module, seen map[string]struct{}) {
    69  	for _, module := range module.Dependencies() {
    70  		if _, ok := seen[module.Name]; !ok {
    71  			seen[module.Name] = struct{}{}
    72  
    73  			sub.Modules = append(sub.Modules, statusFromModule(module))
    74  			sub.addDependencies(module, seen)
    75  		}
    76  	}
    77  }
    78  
    79  func statusFromModule(module *modules.Module) *ModuleStatus {
    80  	status := &ModuleStatus{
    81  		Name:    module.Name,
    82  		module:  module,
    83  		Enabled: module.Enabled() || module.EnabledAsDependency(),
    84  		Status:  module.Status(),
    85  	}
    86  	status.FailureStatus, status.FailureID, status.FailureMsg = module.FailureStatus()
    87  
    88  	return status
    89  }
    90  
    91  func compareAndUpdateStatus(module *modules.Module, status *ModuleStatus) (changed bool) {
    92  	// check if enabled
    93  	enabled := module.Enabled() || module.EnabledAsDependency()
    94  	if status.Enabled != enabled {
    95  		status.Enabled = enabled
    96  		changed = true
    97  	}
    98  
    99  	// check status
   100  	statusLvl := module.Status()
   101  	if status.Status != statusLvl {
   102  		status.Status = statusLvl
   103  		changed = true
   104  	}
   105  
   106  	// check failure status
   107  	failureStatus, failureID, failureMsg := module.FailureStatus()
   108  	if status.FailureStatus != failureStatus ||
   109  		status.FailureID != failureID {
   110  
   111  		status.FailureStatus = failureStatus
   112  		status.FailureID = failureID
   113  		status.FailureMsg = failureMsg
   114  		changed = true
   115  	}
   116  
   117  	return
   118  }
   119  
   120  func (sub *Subsystem) makeSummary() {
   121  	// find worst failing module
   122  	sub.FailureStatus = 0
   123  	for _, depStatus := range sub.Modules {
   124  		if depStatus.FailureStatus > sub.FailureStatus {
   125  			sub.FailureStatus = depStatus.FailureStatus
   126  		}
   127  	}
   128  }