Skip to content

Commit

Permalink
feat: PC-12940/PC-12941 Allow to configure budget drop condition type (
Browse files Browse the repository at this point in the history
  • Loading branch information
nobl9-adam-szymanski authored Jun 25, 2024
1 parent 48b0ae9 commit f250b20
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 19 deletions.
8 changes: 7 additions & 1 deletion manifest/v1alpha/alertpolicy/measurement.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
MeasurementAverageBurnRate
MeasurementTimeToBurnBudget
MeasurementTimeToBurnEntireBudget
MeasurementBudgetDrop
)

func getMeasurements() map[string]Measurement {
Expand All @@ -25,6 +26,7 @@ func getMeasurements() map[string]Measurement {
"averageBurnRate": MeasurementAverageBurnRate,
"timeToBurnBudget": MeasurementTimeToBurnBudget,
"timeToBurnEntireBudget": MeasurementTimeToBurnEntireBudget,
"budgetDrop": MeasurementBudgetDrop,
}
}

Expand Down Expand Up @@ -58,6 +60,8 @@ func GetDefaultOperatorForMeasurement(measurement Measurement) (v1alpha.Operator
return v1alpha.LessThan, nil
case MeasurementTimeToBurnEntireBudget:
return v1alpha.LessThanEqual, nil
case MeasurementBudgetDrop:
return v1alpha.GreaterThanEqual, nil
default:
return 0, errors.Errorf("unable to return expected operator for provided measurement: '%v'", measurement)
}
Expand All @@ -66,7 +70,8 @@ func GetDefaultOperatorForMeasurement(measurement Measurement) (v1alpha.Operator
// getExpectedOperatorForMeasurement returns the operator that should be paired with a given measurement.
func getExpectedOperatorForMeasurement(measurement Measurement) (v1alpha.Operator, error) {
switch measurement {
case MeasurementAverageBurnRate, MeasurementTimeToBurnBudget, MeasurementTimeToBurnEntireBudget:
case MeasurementAverageBurnRate, MeasurementTimeToBurnBudget,
MeasurementTimeToBurnEntireBudget, MeasurementBudgetDrop:
return GetDefaultOperatorForMeasurement(measurement)
default:
return 0, errors.Errorf("unable to return expected operator for provided measurement: '%v'", measurement)
Expand All @@ -79,5 +84,6 @@ func measurementValidation() validation.SingleRule[string] {
MeasurementAverageBurnRate.String(),
MeasurementTimeToBurnBudget.String(),
MeasurementTimeToBurnEntireBudget.String(),
MeasurementBudgetDrop.String(),
)
}
82 changes: 75 additions & 7 deletions manifest/v1alpha/alertpolicy/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ var conditionValidation = validation.New[AlertCondition](
"lastsFor": func(c AlertCondition) any { return c.LastsForDuration },
}),
measurementWithAlertingWindowValidation,
measurementWithLastsForValidation,
measurementWithRequiredAlertingWindowValidation,
).
Include(timeToBurnBudgetValueValidation).
Include(burnedAndAverageBudgetValueValidation),
Include(timeDurationBasedMeasurementsValueValidation).
Include(floatBasedMeasurementsValueValidation),
validation.Transform(func(c AlertCondition) string { return c.AlertingWindow },
func(alertingWindow string) (time.Duration, error) {
value, err := time.ParseDuration(alertingWindow)
Expand Down Expand Up @@ -114,9 +116,10 @@ var alertMethodRefValidation = validation.New[AlertMethodRef](

const (
errorCodeMeasurementWithAlertingWindow = "measurement_regarding_alerting_window"
errorCodeMeasurementWithLastsFor = "measurement_regarding_lasts_for"
)

var timeToBurnBudgetValueValidation = validation.New[AlertCondition](
var timeDurationBasedMeasurementsValueValidation = validation.New[AlertCondition](
validation.Transform(func(c AlertCondition) interface{} { return c.Value }, transformDurationValue).
WithName("value").
Required().
Expand All @@ -131,18 +134,19 @@ var timeToBurnBudgetValueValidation = validation.New[AlertCondition](
MeasurementTimeToBurnBudget, MeasurementTimeToBurnEntireBudget),
)

var burnedAndAverageBudgetValueValidation = validation.New[AlertCondition](
var floatBasedMeasurementsValueValidation = validation.New[AlertCondition](
validation.Transform(func(c AlertCondition) interface{} { return c.Value }, transformFloat64Value).
WithName("value").
OmitEmpty(),
).
When(
func(c AlertCondition) bool {
return c.Measurement == MeasurementBurnedBudget.String() ||
c.Measurement == MeasurementAverageBurnRate.String()
c.Measurement == MeasurementAverageBurnRate.String() ||
c.Measurement == MeasurementBudgetDrop.String()
},
validation.WhenDescription("measurement is is either '%s' or '%s'",
MeasurementBurnedBudget, MeasurementAverageBurnRate),
validation.WhenDescription("measurement is is either '%s', '%s' or '%s'",
MeasurementBurnedBudget, MeasurementAverageBurnRate, MeasurementBudgetDrop),
)

var measurementWithAlertingWindowValidation = validation.NewSingleRule(func(c AlertCondition) error {
Expand All @@ -169,6 +173,60 @@ var measurementWithAlertingWindowValidation = validation.NewSingleRule(func(c Al
return nil
})

var measurementWithLastsForValidation = validation.NewSingleRule(func(c AlertCondition) error {
isLastsForSupported := false
for _, allowedMeasurement := range lastsForSupportedMeasurements() {
if allowedMeasurement == c.Measurement {
isLastsForSupported = true
break
}
}
if c.LastsForDuration != "" && !isLastsForSupported {
return validation.NewPropertyError(
"measurement",
c.Measurement,
validation.NewRuleError(
fmt.Sprintf(
`must be equal to one of '%s' when 'lastsFor' is defined`,
strings.Join(lastsForSupportedMeasurements(), ","),
),
errorCodeMeasurementWithLastsFor,
),
)
}
return nil
})

var measurementWithRequiredAlertingWindowValidation = validation.NewSingleRule(func(c AlertCondition) error {
isLastsForSupported := false
isAlertingWindowSupported := false
for _, allowedMeasurement := range lastsForSupportedMeasurements() {
if allowedMeasurement == c.Measurement {
isLastsForSupported = true
break
}
}
for _, allowedMeasurement := range alertingWindowSupportedMeasurements() {
if allowedMeasurement == c.Measurement {
isAlertingWindowSupported = true
break
}
}
if c.AlertingWindow == "" && isAlertingWindowSupported && !isLastsForSupported {
return validation.NewPropertyError(
"measurement",
c.Measurement,
validation.NewRuleError(
fmt.Sprintf(
`alerting window is required for measurement '%s'`, c.Measurement,
),
validation.ErrorCodeRequired,
),
)
}
return nil
})

func transformDurationValue(v interface{}) (time.Duration, error) {
valueDuration, ok := v.(string)
if !ok {
Expand Down Expand Up @@ -230,11 +288,21 @@ var operatorValidationRule = validation.NewSingleRule(
},
)

func lastsForSupportedMeasurements() []string {
return []string{
MeasurementAverageBurnRate.String(),
MeasurementTimeToBurnBudget.String(),
MeasurementTimeToBurnEntireBudget.String(),
MeasurementBurnedBudget.String(),
}
}

func alertingWindowSupportedMeasurements() []string {
return []string{
MeasurementAverageBurnRate.String(),
MeasurementTimeToBurnBudget.String(),
MeasurementTimeToBurnEntireBudget.String(),
MeasurementBudgetDrop.String(),
}
}

Expand Down
Loading

0 comments on commit f250b20

Please sign in to comment.