| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- package service
- import (
- "encoding/json"
- "fmt"
- "strings"
- "time"
- )
- const AccountSchedulingThresholdReasonSource = "account_scheduling_threshold"
- const (
- defaultTempUnschedReasonErrorMessage = "temporary scheduling block reason unavailable"
- defaultAccountSchedulingThresholdErrorMessage = "account scheduling threshold reached"
- )
- type tempUnschedReasonPayload struct {
- Source string `json:"source,omitempty"`
- Platform string `json:"platform,omitempty"`
- Window string `json:"window,omitempty"`
- Scope string `json:"scope,omitempty"`
- ThresholdPercent int `json:"threshold_percent,omitempty"`
- UsedPercent float64 `json:"used_percent,omitempty"`
- UntilUnix int64 `json:"until_unix,omitempty"`
- TriggeredAtUnix int64 `json:"triggered_at_unix,omitempty"`
- ErrorMessage string `json:"error_message"`
- }
- type AccountSchedulingThresholdReasonInput struct {
- Platform string
- Window string
- Scope string
- ThresholdPercent int
- UsedPercent float64
- Until time.Time
- Now time.Time
- }
- func BuildTempUnschedReasonPayload(source string, errorMessage string) string {
- payload := tempUnschedReasonPayload{
- Source: strings.TrimSpace(source),
- ErrorMessage: normalizeTempUnschedReasonErrorMessage(errorMessage, defaultTempUnschedReasonErrorMessage),
- }
- raw, err := json.Marshal(payload)
- if err != nil {
- return payload.ErrorMessage
- }
- return string(raw)
- }
- func BuildAccountSchedulingThresholdReason(errorMessage string) string {
- return BuildTempUnschedReasonPayload(
- AccountSchedulingThresholdReasonSource,
- normalizeTempUnschedReasonErrorMessage(errorMessage, defaultAccountSchedulingThresholdErrorMessage),
- )
- }
- func BuildDetailedAccountSchedulingThresholdReason(input AccountSchedulingThresholdReasonInput) string {
- triggeredAt := input.Now
- if triggeredAt.IsZero() {
- triggeredAt = time.Now().UTC()
- }
- payload := tempUnschedReasonPayload{
- Source: AccountSchedulingThresholdReasonSource,
- Platform: strings.TrimSpace(input.Platform),
- Window: strings.TrimSpace(input.Window),
- Scope: strings.TrimSpace(input.Scope),
- ThresholdPercent: input.ThresholdPercent,
- UsedPercent: input.UsedPercent,
- TriggeredAtUnix: triggeredAt.Unix(),
- ErrorMessage: buildAccountSchedulingThresholdErrorMessage(input),
- }
- if !input.Until.IsZero() {
- payload.UntilUnix = input.Until.UTC().Unix()
- }
- raw, err := json.Marshal(payload)
- if err != nil {
- return payload.ErrorMessage
- }
- return string(raw)
- }
- func IsAccountSchedulingThresholdReason(rawReason string) bool {
- payload, ok := parseTempUnschedReasonPayload(rawReason)
- if !ok {
- return false
- }
- return payload.Source == AccountSchedulingThresholdReasonSource
- }
- func parseTempUnschedReasonPayload(rawReason string) (tempUnschedReasonPayload, bool) {
- rawReason = strings.TrimSpace(rawReason)
- if rawReason == "" {
- return tempUnschedReasonPayload{}, false
- }
- var payload tempUnschedReasonPayload
- if err := json.Unmarshal([]byte(rawReason), &payload); err != nil {
- return tempUnschedReasonPayload{}, false
- }
- payload.Source = strings.TrimSpace(payload.Source)
- payload.ErrorMessage = strings.TrimSpace(payload.ErrorMessage)
- return payload, true
- }
- func normalizeTempUnschedReasonErrorMessage(errorMessage string, fallback string) string {
- errorMessage = strings.TrimSpace(errorMessage)
- if errorMessage != "" {
- return errorMessage
- }
- fallback = strings.TrimSpace(fallback)
- if fallback != "" {
- return fallback
- }
- return defaultTempUnschedReasonErrorMessage
- }
- func buildAccountSchedulingThresholdErrorMessage(input AccountSchedulingThresholdReasonInput) string {
- platform := strings.TrimSpace(input.Platform)
- if platform == "" {
- platform = "account"
- }
- target := strings.TrimSpace(input.Window)
- if scope := strings.TrimSpace(input.Scope); scope != "" {
- if target == "" {
- target = scope
- } else {
- target = target + "/" + scope
- }
- }
- if target == "" {
- target = "usage window"
- }
- threshold := input.ThresholdPercent
- if threshold <= 0 {
- threshold = 100
- }
- untilText := "the window reset"
- if !input.Until.IsZero() {
- untilText = input.Until.UTC().Format(time.RFC3339)
- }
- return fmt.Sprintf(
- "%s scheduling threshold reached for %s: %.1f%% used >= %d%%; paused until %s",
- platform,
- target,
- input.UsedPercent,
- threshold,
- untilText,
- )
- }
- func tempUnschedStateFromStoredReason(rawReason string, fallbackUntilUnix int64) *TempUnschedState {
- state := &TempUnschedState{
- UntilUnix: fallbackUntilUnix,
- RuleIndex: -1,
- }
- rawReason = strings.TrimSpace(rawReason)
- if rawReason == "" {
- state.ErrorMessage = defaultTempUnschedReasonErrorMessage
- return state
- }
- parsed := TempUnschedState{RuleIndex: -1}
- if err := json.Unmarshal([]byte(rawReason), &parsed); err == nil {
- if fallbackUntilUnix > parsed.UntilUnix {
- parsed.UntilUnix = fallbackUntilUnix
- }
- if strings.TrimSpace(parsed.ErrorMessage) == "" {
- if IsAccountSchedulingThresholdReason(rawReason) {
- parsed.ErrorMessage = defaultAccountSchedulingThresholdErrorMessage
- } else {
- parsed.ErrorMessage = rawReason
- }
- }
- return &parsed
- }
- state.ErrorMessage = rawReason
- return state
- }
|