moira

Форк
0
/
datatypes.go 
750 строк · 22.2 Кб
1
package moira
2

3
import (
4
	"bytes"
5
	"encoding/json"
6
	"fmt"
7
	"reflect"
8
	"sort"
9
	"strings"
10
	"time"
11
)
12

13
// Default moira triggers states
14
const (
15
	OK        = "OK"
16
	WARN      = "WARN"
17
	ERROR     = "ERROR"
18
	NODATA    = "NODATA"
19
	EXCEPTION = "EXCEPTION"
20
	DEL       = "DEL"
21
	TEST      = "TEST"
22
)
23

24
// Events' mute reasons
25
const (
26
	EventMutedNone        = 0
27
	EventMutedSchedule    = 1
28
	EventMutedMaintenance = 2
29
	EventMutedSilent      = 3
30
)
31

32
// fixed offset in minutes and seconds
33
const (
34
	FixedTzOffsetMinutes = int64(-180)
35
	FixedTzOffsetSeconds = FixedTzOffsetMinutes * 60
36
)
37

38
// used as the name of metric to indicate the whole trigger
39
const (
40
	WildcardMetric = "*"
41
)
42

43
// limit (in seconds) of trigger's sole check iteration
44
const (
45
	TriggerCheckLimit     = 40
46
	TriggerCheckThreshold = 30
47
)
48

49
const (
50
	SatTakeScreen SaturationType = "take-screenshot"
51

52
	SatGetCMDBDeviceData    SaturationType = "cmdb-device"
53
	SatCheckPort            SaturationType = "check-port"
54
	SatGetDBaaSService      SaturationType = "dbaas-storage-owner-service"
55
	SatGetDBaaSUnit         SaturationType = "dbaas-storage-owner-unit"
56
	SatGetDeploys           SaturationType = "get-service-deploys"
57
	SatGetAllDeployStatuses SaturationType = "get-all-deploy-statuses"
58
	SatRenderDescription    SaturationType = "render-description"
59
)
60

61
var (
62
	eventStates = [...]string{OK, WARN, ERROR, NODATA, TEST}
63

64
	scores = map[string]int64{
65
		OK:        0,
66
		DEL:       0,
67
		WARN:      1,
68
		ERROR:     100,
69
		NODATA:    0,
70
		EXCEPTION: 100000,
71
	}
72
)
73

74
// NotificationEvent represents trigger state changes event
75
type NotificationEvent struct {
76
	ID             string       `json:"id"`
77
	IsForceSent    bool         `json:"force_sent"`
78
	IsTriggerEvent bool         `json:"trigger_event"`
79
	Timestamp      int64        `json:"timestamp"`
80
	Metric         string       `json:"metric"`
81
	State          string       `json:"state"`
82
	OldState       string       `json:"old_state"`
83
	Value          *float64     `json:"value,omitempty"`
84
	OldValue       *float64     `json:"old_value,omitempty"`
85
	TriggerID      string       `json:"trigger_id"`
86
	SubscriptionID *string      `json:"sub_id,omitempty"`
87
	ContactID      string       `json:"contactId,omitempty"`
88
	Message        *string      `json:"msg,omitempty"`
89
	Batch          *EventsBatch `json:"batch"`
90

91
	HasSaturations bool `json:"has_saturations,omitempty"`
92

93
	OverriddenByAncestor bool   `json:"overridden,omitempty"`
94
	DelayedForAncestor   bool   `json:"delayed_for_ancestor,omitempty"`
95
	AncestorTriggerID    string `json:"ancestor_trigger_id,omitempty"`
96
	AncestorMetric       string `json:"ancestor_metric,omitempty"`
97

98
	// properties related to Fan
99
	FanTaskID          string                    `json:"fan_task_id,omitempty"`
100
	WaitingForFanSince int64                     `json:"waiting_for_fan_since"` // this is a timestamp
101
	Context            *NotificationEventContext `json:"context,omitempty"`
102
}
103

104
// IdempotencyKey is supposed to rule out the possibility of repeated processing NotificationEvent
105
func (event *NotificationEvent) IdempotencyKey() string {
106
	var metric string
107
	if event.IsTriggerEvent {
108
		metric = WildcardMetric
109
	} else {
110
		metric = event.Metric
111
	}
112
	return fmt.Sprintf(
113
		"%s:%d:%s",
114
		event.TriggerID,
115
		event.Timestamp,
116
		metric,
117
	)
118
}
119

120
// Matches implements gomock.Matcher
121
func (event *NotificationEvent) Matches(x interface{}) bool {
122
	other, ok := x.(*NotificationEvent)
123
	if !ok {
124
		return false
125
	}
126

127
	val1 := *event
128
	val1.Batch = nil
129

130
	val2 := *other
131
	val2.Batch = nil
132

133
	// 2 events must be equal, except for the Batch
134
	return reflect.DeepEqual(val1, val2)
135
}
136

137
func (event *NotificationEvent) String() string {
138
	return fmt.Sprintf(
139
		"TriggerId: %s, Metric: %s\nValue: %v, OldValue: %v\nState: %s, OldState: %s\nMessage: %s\nTimestamp: %v",
140
		event.TriggerID, event.Metric,
141
		UseFloat64(event.Value), UseFloat64(event.OldValue),
142
		event.State, event.OldState,
143
		UseString(event.Message),
144
		event.Timestamp,
145
	)
146
}
147

148
// EventsBatch is grouping attribute for
149
// different NotificationEvent`s which were pushed during
150
// single trigger check
151
type EventsBatch struct {
152
	ID   string `json:"id"`
153
	Time int64  `json:"ts"`
154
}
155

156
func NewEventsBatch(ts int64) *EventsBatch {
157
	return &EventsBatch{
158
		ID:   NewStrID(),
159
		Time: ts,
160
	}
161
}
162

163
type NotificationEventContext struct {
164
	Deployers       []string           `json:"deployers,omitempty"`
165
	Images          []contextImageData `json:"images,omitempty"`
166
	DeployStatuses  string             `json:"deployStatuses,omitempty"`
167
	ServiceChannels struct {
168
		DBaaS []serviceChannel `json:"dbaas,omitempty"`
169
	} `json:"serviceChannels,omitempty"`
170
}
171
type contextImageData struct {
172
	URL       string `json:"url"`
173
	SourceURL string `json:"sourceURL"`
174
	Caption   string `json:"caption,omitempty"`
175
}
176
type serviceChannel struct {
177
	ServiceName  string `json:"serviceName"`
178
	SlackChannel string `json:"slackChannel"`
179
}
180

181
func (context *NotificationEventContext) UnmarshalJSON(data []byte) error {
182
	type tmp NotificationEventContext
183
	if err := json.Unmarshal(data, (*tmp)(context)); err != nil {
184
		return err
185
	}
186
	sort.Strings(context.Deployers)
187
	return nil
188
}
189

190
func (context *NotificationEventContext) MustMarshal() string {
191
	if context == nil {
192
		return ""
193
	}
194
	result, err := json.Marshal(context)
195
	if err != nil {
196
		panic(err)
197
	}
198
	return string(result)
199
}
200

201
// NotificationEvents represents slice of NotificationEvent
202
type NotificationEvents []NotificationEvent
203

204
// GetContext returns the context of the events
205
// we assume that all events have the same context
206
func (events NotificationEvents) GetContext() *NotificationEventContext {
207
	if len(events) == 0 {
208
		return nil
209
	}
210
	return events[0].Context
211
}
212

213
// GetSubjectState returns the most critical state of events
214
func (events NotificationEvents) GetSubjectState() string {
215
	result := ""
216
	states := make(map[string]bool)
217
	for _, event := range events {
218
		states[event.State] = true
219
	}
220
	for _, state := range eventStates {
221
		if states[state] {
222
			result = state
223
		}
224
	}
225
	return result
226
}
227

228
// TriggerData represents trigger object
229
type TriggerData struct {
230
	ID         string       `json:"id"`
231
	Name       string       `json:"name"`
232
	Desc       string       `json:"desc"`
233
	Targets    []string     `json:"targets"`
234
	Parents    []string     `json:"parents"`
235
	WarnValue  float64      `json:"warn_value"`
236
	ErrorValue float64      `json:"error_value"`
237
	Tags       []string     `json:"__notifier_trigger_tags"`
238
	Dashboard  string       `json:"dashboard"`
239
	Saturation []Saturation `json:"saturation"`
240
}
241

242
// GetTags returns "[tag1][tag2]...[tagN]" string
243
func (trigger *TriggerData) GetTags() string {
244
	var buffer bytes.Buffer
245
	for _, tag := range trigger.Tags {
246
		buffer.WriteString(fmt.Sprintf("[%s]", tag))
247
	}
248
	return buffer.String()
249
}
250

251
type Saturation struct {
252
	Type            SaturationType  `json:"type"`
253
	Fallback        string          `json:"fallback,omitempty"`
254
	ExtraParameters json.RawMessage `json:"extra_parameters,omitempty"`
255
}
256

257
type SaturationType string
258

259
// ContactData represents contact object
260
type ContactData struct {
261
	Type          string `json:"type"`
262
	Value         string `json:"value"`
263
	FallbackValue string `json:"fallback_value,omitempty"`
264
	ID            string `json:"id"`
265
	User          string `json:"user"` // User is the user that _created_ the contact
266
	Expiration    *time.Time
267
}
268

269
func (cd *ContactData) NeedsFallbackValue() bool {
270
	return cd.Type == "slack" && cd.Value[0] == '_'
271
}
272

273
type SilentPatternData struct {
274
	ID      string            `json:"id"`
275
	Login   string            `json:"login"`
276
	Pattern string            `json:"pattern"`
277
	Created int64             `json:"created_at"`
278
	Until   int64             `json:"until"`
279
	Type    SilentPatternType `json:"type"`
280
}
281

282
func (spd *SilentPatternData) IsMetric() bool {
283
	return spd.Type == SPTMetric
284
}
285

286
func (spd *SilentPatternData) IsTag() bool {
287
	return spd.Type == SPTTag
288
}
289

290
type SilentPatternType int
291

292
const (
293
	SPTMetric SilentPatternType = 0
294
	SPTTag    SilentPatternType = 1
295
)
296

297
// EscalationData represents escalation object
298
type EscalationData struct {
299
	ID              string   `json:"id"`
300
	Contacts        []string `json:"contacts"`
301
	OffsetInMinutes int64    `json:"offset_in_minutes"`
302
}
303

304
// SubscriptionData represent user subscription
305
type SubscriptionData struct {
306
	Contacts          []string         `json:"contacts"`
307
	Tags              []string         `json:"tags"`
308
	Schedule          ScheduleData     `json:"sched"`
309
	ID                string           `json:"id"`
310
	Enabled           bool             `json:"enabled"`
311
	ThrottlingEnabled bool             `json:"throttling"`
312
	User              string           `json:"user"`
313
	Escalations       []EscalationData `json:"escalations"`
314
}
315

316
// ScheduleData represent subscription schedule
317
type ScheduleData struct {
318
	Days           []ScheduleDataDay `json:"days"`
319
	TimezoneOffset int64             `json:"tzOffset"`
320
	StartOffset    int64             `json:"startOffset"`
321
	EndOffset      int64             `json:"endOffset"`
322
}
323

324
// GetFixedTzOffset returns Moscow tz offset in minutes
325
func (schedule *ScheduleData) GetFixedTzOffset() int64 {
326
	return int64(-180)
327
}
328

329
// IsScheduleAllows check if the time is in the allowed schedule interval
330
func (schedule *ScheduleData) IsScheduleAllows(eventTs int64) bool {
331
	if schedule == nil {
332
		return true
333
	}
334

335
	eventTs = eventTs - eventTs%60 - FixedTzOffsetSeconds // truncate to minutes
336
	eventTime := time.Unix(eventTs, 0).UTC()
337
	eventWeekday := eventTime.Weekday()
338

339
	// points converted to seconds relative to the day
340
	eventTs = eventTs % 86400
341
	scheduleStart := schedule.StartOffset * 60
342
	scheduleEnd := schedule.EndOffset * 60
343

344
	if scheduleStart > scheduleEnd { // "inverted" schedule, e.g. 22:00 - 08:00
345
		// there are 2 possible ways of moments' disposition:
346
		// 1) schedule start -> event -> midnight -> schedule end
347
		// 2) schedule start -> midnight -> event -> schedule end
348
		isEventPastMidnight := eventTs < scheduleEnd
349
		// if event happened after midnight (the 2nd case) then the previous day enable flag is taken
350
		if !schedule.isScheduleDaysAllows(eventWeekday, isEventPastMidnight) {
351
			return false
352
		}
353

354
		return (scheduleStart <= eventTs && !isEventPastMidnight) || (eventTs <= scheduleEnd && isEventPastMidnight)
355
	} else { // "regular" schedule, e.g. 09:00 - 18:00
356
		if !schedule.isScheduleDaysAllows(eventWeekday, false) {
357
			return false
358
		}
359

360
		return scheduleStart <= eventTs && eventTs <= scheduleEnd
361
	}
362
}
363

364
// isScheduleDaysAllows can tell if the particular day of the week is enabled by the schedule
365
// dayBefore indicates that the day before must be considered instead of the given day
366
func (schedule *ScheduleData) isScheduleDaysAllows(weekday time.Weekday, dayBefore bool) bool {
367
	var (
368
		daysOffset int
369
	)
370

371
	if dayBefore {
372
		daysOffset = 1
373
	} else {
374
		daysOffset = 0
375
	}
376

377
	return schedule.Days[(int(weekday+6)-daysOffset)%7].Enabled
378
}
379

380
// ScheduleDataDay represent week day of schedule
381
type ScheduleDataDay struct {
382
	Enabled bool   `json:"enabled"`
383
	Name    string `json:"name,omitempty"`
384
}
385

386
// ScheduledNotification represent notification object
387
type ScheduledNotification struct {
388
	Event     NotificationEvent `json:"event"`
389
	Trigger   TriggerData       `json:"trigger"`
390
	Contact   ContactData       `json:"contact"`
391
	Timestamp int64             `json:"timestamp"`
392
	SendFail  int               `json:"send_fail"`
393
	NeedAck   bool              `json:"need_ack"`
394
	Throttled bool              `json:"throttled"`
395
}
396

397
// GetKey return notification key to prevent duplication to the same contact
398
func (notification *ScheduledNotification) GetKey() string {
399
	var prefix string
400
	if notification.Event.AncestorTriggerID == "" {
401
		prefix = fmt.Sprintf(
402
			"%s:%s",
403
			notification.Contact.Type,
404
			notification.Contact.Value,
405
		)
406
	} else {
407
		// if the notification event has an ancestor, we ignore the contact name
408
		prefix = fmt.Sprintf(
409
			"%s",
410
			notification.Contact.Type,
411
		)
412
	}
413
	return fmt.Sprintf("%s:%s:%s:%s:%d:%f:%d:%d",
414
		prefix,
415
		notification.Event.TriggerID,
416
		notification.Event.Metric,
417
		notification.Event.State,
418
		notification.Event.Timestamp,
419
		UseFloat64(notification.Event.Value),
420
		notification.SendFail,
421
		notification.Timestamp,
422
	)
423
}
424

425
// TagStats wraps trigger ids and subscriptions which are related to the given tag
426
type TagStats struct {
427
	Name          string             `json:"name"`
428
	Triggers      []string           `json:"triggers"`
429
	Subscriptions []SubscriptionData `json:"subscriptions"`
430
}
431

432
// MatchedMetric represent parsed and matched metric data
433
type MatchedMetric struct {
434
	Metric             string
435
	Patterns           []string
436
	Value              float64
437
	Timestamp          int64
438
	RetentionTimestamp int64
439
	Retention          int
440
}
441

442
// MetricValue represent metric data
443
type MetricValue struct {
444
	RetentionTimestamp int64   `json:"step,omitempty"`
445
	Timestamp          int64   `json:"ts"`
446
	Value              float64 `json:"value"`
447
}
448

449
// Trigger represents trigger data object
450
type Trigger struct {
451
	ID               string        `json:"id"`
452
	Name             string        `json:"name"`
453
	Desc             *string       `json:"desc,omitempty"`
454
	Targets          []string      `json:"targets"`
455
	Parents          []string      `json:"parents"`
456
	WarnValue        *float64      `json:"warn_value"`
457
	ErrorValue       *float64      `json:"error_value"`
458
	Tags             []string      `json:"tags"`
459
	TTLState         *string       `json:"ttl_state,omitempty"`
460
	TTL              int64         `json:"ttl,omitempty"`
461
	Schedule         *ScheduleData `json:"sched,omitempty"`
462
	Expression       *string       `json:"expression,omitempty"`
463
	PythonExpression *string       `json:"python_expression,omitempty"`
464
	Patterns         []string      `json:"patterns"`
465
	IsPullType       bool          `json:"is_pull_type"`
466
	Dashboard        string        `json:"dashboard"`
467
	PendingInterval  int64         `json:"pending_interval"`
468
	Saturation       []Saturation  `json:"saturation"`
469
}
470

471
// IsSimple checks triggers patterns
472
// If patterns more than one or it contains standard graphite wildcard symbols,
473
// when this target can contain more then one metrics, and is it not simple trigger
474
func (trigger *Trigger) IsSimple() bool {
475
	if len(trigger.Targets) > 1 || len(trigger.Patterns) > 1 {
476
		return false
477
	}
478
	for _, pattern := range trigger.Patterns {
479
		if strings.ContainsAny(pattern, "*{?[") {
480
			return false
481
		}
482
	}
483
	return true
484
}
485

486
// TriggerCheck represent trigger data with last check data and check timestamp
487
type TriggerCheck struct {
488
	Trigger
489
	Throttling int64      `json:"throttling"`
490
	LastCheck  *CheckData `json:"last_check"`
491
}
492

493
// CheckData represent last trigger check data
494
type CheckData struct {
495
	IsPending         bool                    `json:"is_pending"`
496
	Message           string                  `json:"msg,omitempty"`
497
	Timestamp         int64                   `json:"timestamp,omitempty"`
498
	EventTimestamp    int64                   `json:"event_timestamp,omitempty"`
499
	Score             int64                   `json:"score"`
500
	State             string                  `json:"state"`
501
	Suppressed        bool                    `json:"suppressed,omitempty"`
502
	Maintenance       int64                   `json:"maintenance,omitempty"`
503
	MaintenanceMetric map[string]int64        `json:"maintenance_metric,omitempty"`
504
	Metrics           map[string]*MetricState `json:"metrics"`
505
	Version           int                     `json:"version"`
506
}
507

508
// GetEventTimestamp gets event timestamp for given check
509
func (checkData CheckData) GetEventTimestamp() int64 {
510
	if checkData.EventTimestamp == 0 {
511
		return checkData.Timestamp
512
	}
513
	return checkData.EventTimestamp
514
}
515

516
// GetOrCreateMetricState gets metric state from check data or create new if CheckData has no state for given metric
517
func (checkData *CheckData) GetOrCreateMetricState(metric string, emptyTimestampValue int64) *MetricState {
518
	_, ok := checkData.Metrics[metric]
519
	if !ok {
520
		checkData.Metrics[metric] = &MetricState{
521
			IsNoData:  true,
522
			State:     NODATA,
523
			Timestamp: emptyTimestampValue,
524
		}
525
	}
526
	return checkData.Metrics[metric]
527
}
528

529
// UpdateScore update and return checkData score, based on metric states and checkData state
530
func (checkData *CheckData) UpdateScore() int64 {
531
	checkData.Score = scores[checkData.State]
532
	for _, metricData := range checkData.Metrics {
533
		checkData.Score += scores[metricData.State]
534
	}
535
	return checkData.Score
536
}
537

538
// MetricState represent metric state data for given timestamp
539
type MetricState struct {
540
	EventTimestamp int64    `json:"event_timestamp"`
541
	State          string   `json:"state"`
542
	Suppressed     bool     `json:"suppressed"`
543
	Timestamp      int64    `json:"timestamp"`
544
	Value          *float64 `json:"value,omitempty"`
545
	Maintenance    int64    `json:"maintenance,omitempty"`
546
	IsPending      bool     `json:"is_pending"`
547

548
	IsNoData bool `json:"is_no_data"`
549
	IsForced bool `json:"is_forced,omitempty"`
550
}
551

552
// GetCheckPoint gets check point for given MetricState
553
// CheckPoint is the timestamp from which to start checking the current state of the metric
554
func (metricState *MetricState) GetCheckPoint(checkPointGap int64) int64 {
555
	return MaxI64(metricState.Timestamp-checkPointGap, metricState.EventTimestamp)
556
}
557

558
// GetEventTimestamp gets event timestamp for given metric
559
func (metricState *MetricState) GetEventTimestamp() int64 {
560
	if metricState.EventTimestamp == 0 {
561
		return metricState.Timestamp
562
	}
563
	return metricState.EventTimestamp
564
}
565

566
// MetricEvent represent filter metric event
567
type MetricEvent struct {
568
	Metric  string `json:"metric"`
569
	Pattern string `json:"pattern"`
570
}
571

572
// maintenanceInterval is maintenance interval for some metric or for the whole trigger
573
type maintenanceInterval struct {
574
	From  int64 `json:"from"`
575
	Until int64 `json:"until"`
576
}
577

578
// Maintenance is history of maintenance intervals for each metric of the trigger
579
// key for the whole trigger maintenance is WildcardMetric
580
type Maintenance map[string][]maintenanceInterval
581

582
// NewMaintenance creates blank Maintenance instance
583
func NewMaintenance() Maintenance {
584
	return make(Maintenance)
585
}
586

587
// NewMaintenanceFromCheckData migrates CheckData maintenance to the new Maintenance instance
588
// only maintenanceInterval.Until values can be filled
589
func NewMaintenanceFromCheckData(data *CheckData) Maintenance {
590
	result := make(Maintenance, len(data.MaintenanceMetric)+1)
591
	if data.Maintenance > 0 {
592
		result[WildcardMetric] = []maintenanceInterval{{Until: data.Maintenance}}
593
	}
594
	for metric, maintenance := range data.MaintenanceMetric {
595
		result[metric] = []maintenanceInterval{{Until: maintenance}}
596
	}
597
	return result
598
}
599

600
// Add adds maintenance for the given metric
601
func (maintenance Maintenance) Add(metric string, until int64) {
602
	now := time.Now().Unix()
603
	history, ok := maintenance[metric]
604

605
	if ok {
606
		last := &history[len(history)-1]
607
		if last.Until > now {
608
			// last maintenance isn't over yet, extend it
609
			last.Until = until
610
		} else {
611
			// append new maintenance
612
			history = append(history, maintenanceInterval{
613
				From:  now,
614
				Until: until,
615
			})
616
			maintenance[metric] = history
617
		}
618
	} else {
619
		maintenance[metric] = []maintenanceInterval{{
620
			From:  now,
621
			Until: until,
622
		}}
623
	}
624
}
625

626
// Get returns maintenance state for the given metric at the given timestamp
627
func (maintenance Maintenance) Get(metric string, ts int64) (maintained bool, until int64) {
628
	// check the metric is present in the first place
629
	history, ok := maintenance[metric]
630
	if !ok {
631
		return false, 0
632
	}
633
	qty := len(history)
634

635
	// look for the interval where Until exceeds ts
636
	pos := sort.Search(qty, func(i int) bool {
637
		return history[i].Until >= ts
638
	})
639
	if pos == qty { // sort.Search returns collection's length if nothing has been found
640
		return false, 0
641
	}
642
	return true, history[pos].Until
643
}
644

645
// Del terminates maintenance for the given metric
646
// it does nothing if the metric doesn't exist
647
func (maintenance Maintenance) Del(metric string) {
648
	if history, ok := maintenance[metric]; ok {
649
		history[len(history)-1].Until = time.Now().Unix()
650
		maintenance[metric] = history
651
	}
652
}
653

654
// Clean removes all outdated maintenance intervals
655
func (maintenance Maintenance) Clean() {
656
	const housekeepingRange = 30 * 24 * 60 * 60 // 30 days
657
	margin := time.Now().Unix() - housekeepingRange
658

659
	for metric, history := range maintenance {
660
		// find first non-outdated interval
661
		qty := len(history)
662
		pos := sort.Search(qty, func(i int) bool {
663
			return history[i].From > margin
664
		})
665

666
		if pos == qty {
667
			// all intervals are outdated -- just delete metric entry
668
			delete(maintenance, metric)
669
		} else if pos > 0 {
670
			// truncate intervals
671
			maintenance[metric] = history[pos:]
672
		}
673
	}
674
}
675

676
// Snapshot returns map of metrics to their actual maintenance on the given timestamp
677
func (maintenance Maintenance) Snapshot(ts int64) map[string]int64 {
678
	result := make(map[string]int64, len(maintenance))
679
	for metric, history := range maintenance {
680
		qty := len(history)
681
		pos := sort.Search(qty, func(i int) bool {
682
			return history[i].Until >= ts
683
		})
684

685
		if pos < qty {
686
			result[metric] = history[pos].Until
687
		}
688
	}
689
	return result
690
}
691

692
func (maintenance Maintenance) SnapshotNow() map[string]int64 {
693
	return maintenance.Snapshot(time.Now().Unix())
694
}
695

696
// ScheduledEscalationEvent represent escalated notification event
697
type ScheduledEscalationEvent struct {
698
	Escalation   EscalationData    `json:"escalation"`
699
	Event        NotificationEvent `json:"event"`
700
	Trigger      TriggerData       `json:"trigger"`
701
	IsFinal      bool              `json:"is_final"`
702
	IsResolution bool              `json:"is_resolution"`
703
}
704

705
type NotificationsDisabledSettings struct {
706
	Author   string `json:"author"`
707
	Disabled bool   `json:"disabled"`
708
}
709

710
type GlobalSettings struct {
711
	Notifications NotificationsDisabledSettings `json:"notifications"`
712
}
713

714
type DutyItem struct {
715
	Login   string
716
	DutyEnd *time.Time `json:"duty_end"`
717
}
718

719
type DutyData struct {
720
	Duty      []DutyItem
721
	Timestamp time.Time
722
}
723

724
type RateLimit struct {
725
	AcceptRate float64
726
	ThreadsQty int
727
}
728

729
type SlackDelayedAction struct {
730
	Action      string          `json:"action"`
731
	EncodedArgs json.RawMessage `json:"encodedArgs"`
732
	FailCount   int             `json:"failCount"`
733
	ScheduledAt time.Time       `json:"scheduledAt"`
734

735
	// these fields are only used for logging
736
	Contact ContactData `json:"contact"`
737
}
738

739
// SlackUserGroup represents slack user group macro (user to mention several users at a time)
740
type SlackUserGroup struct {
741
	Id         string    `json:"id"`
742
	Handle     string    `json:"handle"` // macro
743
	Name       string    `json:"name"`   // human-readable name
744
	DateCreate time.Time `json:"date_create"`
745
	DateUpdate time.Time `json:"date_update"`
746
	UserIds    []string  `json:"user_ids"` // sadly, not names, but slack accepts it
747
}
748

749
// SlackUserGroupsCache maps SlackUserGroup.Handle to SlackUserGroup
750
type SlackUserGroupsCache map[string]SlackUserGroup
751

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.