Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ linters:
max-distance: 6
ignore-names:
- err
- fn
- wg
- s
- ch
Expand Down
10 changes: 6 additions & 4 deletions cmds/dutagent/dutagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ type agent struct {
server string

// state
config config
config config
modulesNeedDeinit bool
}

// config holds the dutagent configuration that is parsed from YAML data.
Expand All @@ -95,9 +96,8 @@ const (
// Afterwards agt.exit is called. If clean-up fails, agt.exit is called with code 1,
// otherwise with provided exitCode.
func (agt *agent) cleanup(code exitCode) {
devlist := agt.config.Devices
if devlist != nil {
err := dutagent.Deinit(devlist)
if agt.modulesNeedDeinit {
err := dutagent.Deinit(agt.config.Devices)
if err != nil {
Comment thread
RiSKeD marked this conversation as resolved.
printInitErr(err)
log.Print("System might be in an UNKNOWN STATE !!!")
Expand Down Expand Up @@ -136,6 +136,8 @@ func (agt *agent) loadConfig() error {
}

func (agt *agent) initModules() error {
agt.modulesNeedDeinit = true

return dutagent.Init(agt.config.Devices)
}

Expand Down
23 changes: 21 additions & 2 deletions internal/dutagent/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ import (
"github.com/BlindspotSoftware/dutctl/pkg/dut"
)

// catchPanic calls fn and recovers from any panic, returning it as an error.
// This intentionally does NOT re-panic. A module panic is recorded as an error
// so the Init/Deinit loop can continue with the remaining modules.
func catchPanic(fn func() error) error {
var err error

func() {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()

err = fn()
}()

return err
}

// ModuleInitError is a container for errors that occur during module
// initialization.
type ModuleInitError struct {
Expand Down Expand Up @@ -45,7 +64,7 @@ func Init(devices dut.Devlist) error {
for devname, device := range devices {
for cmdname, cmd := range device.Cmds {
for _, module := range cmd.Modules {
err := module.Init()
err := catchPanic(module.Init)
if err != nil {
ierr.Errs = append(ierr.Errs, ModuleInitErrorDetails{
Dev: devname,
Expand Down Expand Up @@ -81,7 +100,7 @@ func Deinit(devices dut.Devlist) error {
for devname, device := range devices {
for cmdname, cmd := range device.Cmds {
for _, module := range cmd.Modules {
err := module.Deinit()
err := catchPanic(module.Deinit)
if err != nil {
derr.Errs = append(derr.Errs, ModuleInitErrorDetails{
Dev: devname,
Expand Down
8 changes: 8 additions & 0 deletions pkg/module/gpio/gpio.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ func (b *Button) Init() error {
func (b *Button) Deinit() error {
log.Println("gpio.Button module: Deinit called")

if b.gpio == nil {
return nil
}

return b.Low(b.Pin)
}

Expand Down Expand Up @@ -258,6 +262,10 @@ func (s *Switch) Init() error {
func (s *Switch) Deinit() error {
log.Println("gpio.Switch module: Deinit called")

if s.gpio == nil {
return nil
}

return s.Low(s.Pin)
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ type Module interface {
// Deinit is called when the module is unloaded by dutagent or an internal error occurs.
// It is used to clean up any resources that were allocated during the Init phase and
// shall guarantee a graceful shutdown of the service.
//
// Implementations must be safe to call even if Init was never called or failed partway.
// Init may fail after partially allocating resources that still need cleanup.
Deinit() error
// Run is the entry point and executes the module with the given arguments.
Run(ctx context.Context, s Session, args ...string) error
Expand Down