github.com/helmwave/helmwave@v0.36.4-0.20240509190856-b35563eba4c6/pkg/monitor/config.go (about)

     1  package monitor
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/helmwave/helmwave/pkg/monitor/http"
     9  	"github.com/helmwave/helmwave/pkg/monitor/prometheus"
    10  	log "github.com/sirupsen/logrus"
    11  	"gopkg.in/yaml.v3"
    12  )
    13  
    14  const (
    15  	DefaultTotalTimeout     = time.Minute * 5
    16  	DefaultIterationTimeout = time.Second * 10
    17  	DefaultInterval         = time.Minute
    18  	DefaultSuccessThreshold = 3
    19  	DefaultFailureThreshold = 3
    20  )
    21  
    22  // Config is the main monitor Config.
    23  type config struct {
    24  	Prometheus       *prometheus.Config `yaml:"prometheus" json:"prometheus" jsonschema:"title=Config for prometheus type,oneof_required=prometheus"`
    25  	HTTP             *http.Config       `yaml:"http" json:"http" jsonschema:"title=Config for http type,oneof_required=http"`
    26  	subConfig        SubConfig          `yaml:"-" json:"-"`
    27  	log              *log.Entry         `yaml:"-" json:"-"`
    28  	NameF            string             `yaml:"name" json:"name" jsonschema:"required"`
    29  	Type             string             `yaml:"type" json:"type" jsonschema:"enum=prometheus,enum=http,required"`
    30  	TotalTimeout     time.Duration      `yaml:"total_timeout" json:"total_timeout" jsonschema:"title=Timeout for the whole monitor,description=After this timeout hits monitor will fail regardless of current streak,default=5m,oneof_type=string;integer"`
    31  	IterationTimeout time.Duration      `yaml:"iteration_timeout" json:"iteration_timeout" jsonschema:"title=Timeout for each timeout execution,description=After this timeout hits monitor iteration will be considered as failed,default=10s,oneof_type=string;integer"`
    32  	Interval         time.Duration      `yaml:"interval" json:"interval" jsonschema:"default=1m,oneof_type=string;integer"`
    33  	SuccessThreshold uint8              `yaml:"success_threshold" json:"success_threshold" jsonschema:"default=3"`
    34  	FailureThreshold uint8              `yaml:"failure_threshold" json:"failure_threshold" jsonschema:"default=3"`
    35  }
    36  
    37  type typeConfig struct {
    38  	Type string `yaml:"type" json:"type"`
    39  }
    40  
    41  type _config config
    42  
    43  func (c *config) setDefaults() {
    44  	c.TotalTimeout = DefaultTotalTimeout
    45  	c.IterationTimeout = DefaultIterationTimeout
    46  	c.Interval = DefaultInterval
    47  	c.SuccessThreshold = DefaultSuccessThreshold
    48  	c.FailureThreshold = DefaultFailureThreshold
    49  }
    50  
    51  func (c *config) Name() string {
    52  	return c.NameF
    53  }
    54  
    55  func (c *config) Logger() *log.Entry {
    56  	if c.log == nil {
    57  		c.log = log.WithField("monitor", c.Name())
    58  	}
    59  
    60  	return c.log
    61  }
    62  
    63  func (c *config) Run(ctx context.Context) error {
    64  	ctx, cancel := context.WithTimeout(ctx, c.TotalTimeout)
    65  	defer cancel()
    66  
    67  	c.Logger().Debug("initializing monitor")
    68  	err := c.subConfig.Init(ctx, c.Logger())
    69  	if err != nil {
    70  		return NewMonitorInitError(err)
    71  	}
    72  
    73  	c.Logger().Debug("starting monitor")
    74  
    75  	timer := time.NewTimer(c.Interval)
    76  	defer timer.Stop()
    77  
    78  	var successStreak uint8 = 0
    79  	var failureStreak uint8 = 0
    80  
    81  	for (successStreak < c.SuccessThreshold) && (failureStreak < c.FailureThreshold) {
    82  		select {
    83  		case <-timer.C:
    84  			timer.Reset(c.Interval)
    85  			ctx, cancel := context.WithTimeout(ctx, c.IterationTimeout)
    86  			err := c.subConfig.Run(ctx)
    87  			cancel()
    88  
    89  			if err == nil {
    90  				successStreak += 1
    91  				failureStreak = 0
    92  				c.Logger().
    93  					WithField("streak", fmt.Sprintf("%d/%d", successStreak, c.SuccessThreshold)).
    94  					Info("monitor succeeded")
    95  			} else {
    96  				successStreak = 0
    97  				failureStreak += 1
    98  				c.Logger().
    99  					WithField("streak", fmt.Sprintf("%d/%d", failureStreak, c.FailureThreshold)).
   100  					WithField("error", err).
   101  					Info("monitor did not succeed")
   102  			}
   103  		case <-ctx.Done():
   104  			return ctx.Err()
   105  		}
   106  	}
   107  
   108  	if failureStreak > 0 {
   109  		return ErrFailureStreak
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // UnmarshalYAML is an unmarshaller for gopkg.in/yaml.v3 to parse subconfig.
   116  func (c *config) UnmarshalYAML(node *yaml.Node) error {
   117  	t := typeConfig{}
   118  	err := node.Decode(&t)
   119  	if err != nil {
   120  		return NewYAMLDecodeError(err)
   121  	}
   122  
   123  	c.setDefaults()
   124  
   125  	cfg := (*_config)(c)
   126  
   127  	switch t.Type {
   128  	case prometheus.TYPE:
   129  		cfg.Prometheus = prometheus.NewConfig()
   130  		cfg.subConfig = cfg.Prometheus
   131  	case http.TYPE:
   132  		cfg.HTTP = http.NewConfig()
   133  		cfg.subConfig = cfg.HTTP
   134  	default:
   135  		return fmt.Errorf("unknown monitor type %q", t.Type)
   136  	}
   137  
   138  	err = node.Decode(cfg)
   139  	if err != nil {
   140  		return NewYAMLDecodeError(err)
   141  	}
   142  
   143  	return nil
   144  }