github.com/thanos-io/thanos@v0.32.5/pkg/rules/rulespb/custom.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package rulespb
     5  
     6  import (
     7  	"encoding/json"
     8  	"math/big"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/prometheus/prometheus/model/labels"
    15  
    16  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    17  )
    18  
    19  const (
    20  	RuleRecordingType = "recording"
    21  	RuleAlertingType  = "alerting"
    22  )
    23  
    24  func NewRuleGroupRulesResponse(rg *RuleGroup) *RulesResponse {
    25  	return &RulesResponse{
    26  		Result: &RulesResponse_Group{
    27  			Group: rg,
    28  		},
    29  	}
    30  }
    31  
    32  func NewWarningRulesResponse(warning error) *RulesResponse {
    33  	return &RulesResponse{
    34  		Result: &RulesResponse_Warning{
    35  			Warning: warning.Error(),
    36  		},
    37  	}
    38  }
    39  
    40  func NewRecordingRule(r *RecordingRule) *Rule {
    41  	return &Rule{
    42  		Result: &Rule_Recording{Recording: r},
    43  	}
    44  }
    45  
    46  // Compare compares equal recording rules r1 and r2 and returns:
    47  //
    48  //	< 0 if r1 < r2  if rule r1 is lexically before rule r2
    49  //	  0 if r1 == r2
    50  //	> 0 if r1 > r2  if rule r1 is lexically after rule r2
    51  //
    52  // More formally, the ordering is determined in the following order:
    53  //
    54  // 1. recording rule last evaluation (earlier evaluation comes first)
    55  //
    56  // Note: This method assumes r1 and r2 are logically equal as per Rule#Compare.
    57  func (r1 *RecordingRule) Compare(r2 *RecordingRule) int {
    58  	if r1.LastEvaluation.Before(r2.LastEvaluation) {
    59  		return 1
    60  	}
    61  
    62  	if r1.LastEvaluation.After(r2.LastEvaluation) {
    63  		return -1
    64  	}
    65  
    66  	return 0
    67  }
    68  
    69  func NewAlertingRule(a *Alert) *Rule {
    70  	return &Rule{
    71  		Result: &Rule_Alert{Alert: a},
    72  	}
    73  }
    74  
    75  func (r *Rule) GetLabels() labels.Labels {
    76  	switch {
    77  	case r.GetRecording() != nil:
    78  		return r.GetRecording().Labels.PromLabels()
    79  	case r.GetAlert() != nil:
    80  		return r.GetAlert().Labels.PromLabels()
    81  	default:
    82  		return nil
    83  	}
    84  }
    85  
    86  func (r *Rule) SetLabels(ls labels.Labels) {
    87  	var result labelpb.ZLabelSet
    88  
    89  	if len(ls) > 0 {
    90  		result = labelpb.ZLabelSet{Labels: labelpb.ZLabelsFromPromLabels(ls)}
    91  	}
    92  
    93  	switch {
    94  	case r.GetRecording() != nil:
    95  		r.GetRecording().Labels = result
    96  	case r.GetAlert() != nil:
    97  		r.GetAlert().Labels = result
    98  	}
    99  }
   100  
   101  func (r *Rule) GetName() string {
   102  	switch {
   103  	case r.GetRecording() != nil:
   104  		return r.GetRecording().Name
   105  	case r.GetAlert() != nil:
   106  		return r.GetAlert().Name
   107  	default:
   108  		return ""
   109  	}
   110  }
   111  
   112  func (r *Rule) GetQuery() string {
   113  	switch {
   114  	case r.GetRecording() != nil:
   115  		return r.GetRecording().Query
   116  	case r.GetAlert() != nil:
   117  		return r.GetAlert().Query
   118  	default:
   119  		return ""
   120  	}
   121  }
   122  
   123  func (r *Rule) GetLastEvaluation() time.Time {
   124  	switch {
   125  	case r.GetRecording() != nil:
   126  		return r.GetRecording().LastEvaluation
   127  	case r.GetAlert() != nil:
   128  		return r.GetAlert().LastEvaluation
   129  	default:
   130  		return time.Time{}
   131  	}
   132  }
   133  
   134  // Compare compares recording and alerting rules r1 and r2 and returns:
   135  //
   136  //	< 0 if r1 < r2  if rule r1 is not equal and lexically before rule r2
   137  //	  0 if r1 == r2 if rule r1 is logically equal to r2 (r1 and r2 are the "same" rules)
   138  //	> 0 if r1 > r2  if rule r1 is not equal and lexically after rule r2
   139  //
   140  // More formally, ordering and equality is determined in the following order:
   141  //
   142  // 1. rule type (alerting rules come before recording rules)
   143  // 2. rule name
   144  // 3. rule labels
   145  // 4. rule query
   146  // 5. for alerting rules: duration
   147  //
   148  // Note: this can still leave ordering undetermined for equal rules (x == y).
   149  // For determining ordering of equal rules, use Alert#Compare or RecordingRule#Compare.
   150  func (r1 *Rule) Compare(r2 *Rule) int {
   151  	if r1.GetAlert() != nil && r2.GetRecording() != nil {
   152  		return -1
   153  	}
   154  
   155  	if r1.GetRecording() != nil && r2.GetAlert() != nil {
   156  		return 1
   157  	}
   158  
   159  	if d := strings.Compare(r1.GetName(), r2.GetName()); d != 0 {
   160  		return d
   161  	}
   162  
   163  	if d := labels.Compare(r1.GetLabels(), r2.GetLabels()); d != 0 {
   164  		return d
   165  	}
   166  
   167  	if d := strings.Compare(r1.GetQuery(), r2.GetQuery()); d != 0 {
   168  		return d
   169  	}
   170  
   171  	if r1.GetAlert() != nil && r2.GetAlert() != nil {
   172  		if d := big.NewFloat(r1.GetAlert().DurationSeconds).Cmp(big.NewFloat(r2.GetAlert().DurationSeconds)); d != 0 {
   173  			return d
   174  		}
   175  	}
   176  
   177  	return 0
   178  }
   179  
   180  func (r *RuleGroups) MarshalJSON() ([]byte, error) {
   181  	if r.Groups == nil {
   182  		// Ensure that empty slices are marshaled as '[]' and not 'null'.
   183  		return []byte(`{"groups":[]}`), nil
   184  	}
   185  	type plain RuleGroups
   186  	return json.Marshal((*plain)(r))
   187  }
   188  
   189  // Compare compares rule group x and y and returns:
   190  //
   191  //	< 0 if x < y   if rule group r1 is not equal and lexically before rule group r2
   192  //	  0 if x == y  if rule group r1 is logically equal to r2 (r1 and r2 are the "same" rule groups)
   193  //	> 0 if x > y   if rule group r1 is not equal and lexically after rule group r2
   194  func (r1 *RuleGroup) Compare(r2 *RuleGroup) int {
   195  	return strings.Compare(r1.Key(), r2.Key())
   196  }
   197  
   198  // Key returns the group key similar resembling Prometheus logic.
   199  // See https://github.com/prometheus/prometheus/blob/869f1bc587e667b79721852d5badd9f70a39fc3f/rules/manager.go#L1062-L1065
   200  func (r *RuleGroup) Key() string {
   201  	if r == nil {
   202  		return ""
   203  	}
   204  
   205  	return r.File + ";" + r.Name
   206  }
   207  
   208  func (m *Rule) UnmarshalJSON(entry []byte) error {
   209  	decider := struct {
   210  		Type string `json:"type"`
   211  	}{}
   212  	if err := json.Unmarshal(entry, &decider); err != nil {
   213  		return errors.Wrapf(err, "rule: type field unmarshal: %v", string(entry))
   214  	}
   215  
   216  	switch strings.ToLower(decider.Type) {
   217  	case "recording":
   218  		r := &RecordingRule{}
   219  		if err := json.Unmarshal(entry, r); err != nil {
   220  			return errors.Wrapf(err, "rule: recording rule unmarshal: %v", string(entry))
   221  		}
   222  
   223  		m.Result = &Rule_Recording{Recording: r}
   224  	case "alerting":
   225  		r := &Alert{}
   226  		if err := json.Unmarshal(entry, r); err != nil {
   227  			return errors.Wrapf(err, "rule: alerting rule unmarshal: %v", string(entry))
   228  		}
   229  
   230  		m.Result = &Rule_Alert{Alert: r}
   231  	case "":
   232  		return errors.Errorf("rule: no type field provided: %v", string(entry))
   233  	default:
   234  		return errors.Errorf("rule: unknown type field provided %s; %v", decider.Type, string(entry))
   235  	}
   236  	return nil
   237  }
   238  
   239  func (m *Rule) MarshalJSON() ([]byte, error) {
   240  	if r := m.GetRecording(); r != nil {
   241  		return json.Marshal(struct {
   242  			*RecordingRule
   243  			Type string `json:"type"`
   244  		}{
   245  			RecordingRule: r,
   246  			Type:          RuleRecordingType,
   247  		})
   248  	}
   249  	a := m.GetAlert()
   250  	if a.Alerts == nil {
   251  		// Ensure that empty slices are marshaled as '[]' and not 'null'.
   252  		a.Alerts = make([]*AlertInstance, 0)
   253  	}
   254  	return json.Marshal(struct {
   255  		*Alert
   256  		Type string `json:"type"`
   257  	}{
   258  		Alert: a,
   259  		Type:  RuleAlertingType,
   260  	})
   261  }
   262  
   263  func (r *RuleGroup) MarshalJSON() ([]byte, error) {
   264  	if r.Rules == nil {
   265  		// Ensure that empty slices are marshaled as '[]' and not 'null'.
   266  		r.Rules = make([]*Rule, 0)
   267  	}
   268  	type plain RuleGroup
   269  	return json.Marshal((*plain)(r))
   270  }
   271  
   272  func (x *AlertState) UnmarshalJSON(entry []byte) error {
   273  	fieldStr, err := strconv.Unquote(string(entry))
   274  	if err != nil {
   275  		return errors.Wrapf(err, "alertState: unquote %v", string(entry))
   276  	}
   277  
   278  	if fieldStr == "" {
   279  		return errors.New("empty alertState")
   280  	}
   281  
   282  	state, ok := AlertState_value[strings.ToUpper(fieldStr)]
   283  	if !ok {
   284  		return errors.Errorf("unknown alertState: %v", string(entry))
   285  	}
   286  	*x = AlertState(state)
   287  	return nil
   288  }
   289  
   290  func (x *AlertState) MarshalJSON() ([]byte, error) {
   291  	return []byte(strconv.Quote(strings.ToLower(x.String()))), nil
   292  }
   293  
   294  // Compare compares alert state x and y and returns:
   295  //
   296  //	< 0 if x < y  (alert state x is more critical than alert state y)
   297  //	  0 if x == y
   298  //	> 0 if x > y  (alert state x is less critical than alert state y)
   299  //
   300  // For sorting this makes sure that more "critical" alert states come first.
   301  func (x AlertState) Compare(y AlertState) int {
   302  	return int(y) - int(x)
   303  }
   304  
   305  // Compare compares two equal alerting rules a1 and a2 and returns:
   306  //
   307  //	< 0 if a1 < a2  if rule a1 is lexically before rule a2
   308  //	  0 if a1 == a2
   309  //	> 0 if a1 > a2  if rule a1 is lexically after rule a2
   310  //
   311  // More formally, the ordering is determined in the following order:
   312  //
   313  // 1. alert state
   314  // 2. alert last evaluation (earlier evaluation comes first)
   315  //
   316  // Note: This method assumes a1 and a2 are logically equal as per Rule#Compare.
   317  func (a1 *Alert) Compare(a2 *Alert) int {
   318  	if d := a1.State.Compare(a2.State); d != 0 {
   319  		return d
   320  	}
   321  
   322  	if a1.LastEvaluation.Before(a2.LastEvaluation) {
   323  		return 1
   324  	}
   325  
   326  	if a1.LastEvaluation.After(a2.LastEvaluation) {
   327  		return -1
   328  	}
   329  
   330  	return 0
   331  }