...

Source file src/golang.conradwood.net/go-easyops/utils/functionchain/functionchain.go

Documentation: golang.conradwood.net/go-easyops/utils/functionchain

     1  package functionchain
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"golang.conradwood.net/go-easyops/errors"
     9  )
    10  
    11  /*
    12  a "function chain" is a chain of idempotent functions that enable or disable something. The chain will attempt to keep ALL functions enabled or disabled. In other words, it attempts to keep all functions either disabled or enabled.
    13  */
    14  type FunctionChain struct {
    15  	sync.Mutex
    16  	functions              []*function_ref
    17  	signal_stop_to_enable  bool
    18  	signal_stop_to_disable bool
    19  }
    20  
    21  type Function interface {
    22  	SetTo(ctx context.Context, activate bool) error
    23  }
    24  
    25  func NewFunctionChain() *FunctionChain {
    26  	return &FunctionChain{}
    27  }
    28  
    29  func (fc *FunctionChain) Add(f Function) *function_ref {
    30  	fc.Lock()
    31  	defer fc.Unlock()
    32  	fr := &function_ref{functions: []Function{f}}
    33  	fc.functions = append(fc.functions, fr)
    34  	return fr
    35  }
    36  
    37  func (fc *FunctionChain) SetTo(ctx context.Context, b bool) error {
    38  	fc.Lock()
    39  	defer fc.Unlock()
    40  	if len(fc.functions) == 0 {
    41  		return nil
    42  	}
    43  	stop_signal := &fc.signal_stop_to_disable
    44  	if b {
    45  		stop_signal = &fc.signal_stop_to_enable
    46  	}
    47  	*stop_signal = false
    48  	start_time := time.Now()
    49  	end_time := start_time.Add(time.Duration(30) * time.Second) // max runtime
    50  	repeat := true
    51  	for repeat {
    52  		if *stop_signal {
    53  			break
    54  		}
    55  		if time.Now().After(end_time) {
    56  			return errors.Errorf("timeout enabling functionchain (%0.1fs)", time.Since(start_time).Seconds())
    57  		}
    58  		repeat = false
    59  		for _, frw := range fc.functions {
    60  			if *stop_signal {
    61  				break
    62  			}
    63  			frw.Lock()
    64  			if time.Now().After(end_time) {
    65  				frw.Unlock()
    66  				return errors.Errorf("timeout enabling functionchain (%0.1fs)", time.Since(start_time).Seconds())
    67  			}
    68  			err := frw.SetTo(ctx, b)
    69  			frw.Unlock()
    70  			if err != nil {
    71  				repeat = true
    72  			}
    73  		}
    74  		if repeat {
    75  			time.Sleep(time.Duration(100) * time.Millisecond) // wait a little before retry
    76  		}
    77  
    78  	}
    79  	return nil
    80  }
    81  func (fc *FunctionChain) Enable(ctx context.Context) error {
    82  	fc.signal_stop_to_disable = true
    83  	return fc.SetTo(ctx, true)
    84  
    85  }
    86  func (fc *FunctionChain) Disable(ctx context.Context) error {
    87  	fc.signal_stop_to_enable = true
    88  	return fc.SetTo(ctx, false)
    89  }
    90  

View as plain text