-
Notifications
You must be signed in to change notification settings - Fork 3
/
app.go
126 lines (113 loc) · 3.12 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package shimesaba
import (
"context"
"errors"
"fmt"
"log"
"sort"
"github.com/Songmu/flextime"
mackerel "github.com/mackerelio/mackerel-client-go"
)
//App manages life cycle
type App struct {
repo *Repository
SLODefinitions []*Definition
}
//New creates an app
func New(apikey string, cfg *Config) (*App, error) {
client := mackerel.NewClient(apikey)
return NewWithMackerelClient(client, cfg)
}
//NewWithMackerelClient is there to accept mock clients.
func NewWithMackerelClient(client MackerelClient, cfg *Config) (*App, error) {
slo := make([]*Definition, 0, len(cfg.SLO))
for _, c := range cfg.SLO {
d, err := NewDefinition(c)
if err != nil {
return nil, err
}
slo = append(slo, d)
}
app := &App{
repo: NewRepository(client),
SLODefinitions: slo,
}
return app, nil
}
type Options struct {
dryRun bool
backfill int
dumpReports bool
}
//DryRunOption is an option to output the calculated error budget as standard without posting it to Mackerel.
func DryRunOption(dryRun bool) func(*Options) {
return func(opt *Options) {
opt.dryRun = dryRun
}
}
//BackfillOption specifies how many points of data to calculate retroactively from the current time.
func BackfillOption(count int) func(*Options) {
return func(opt *Options) {
opt.backfill = count
}
}
func DumpReportsOption(dump bool) func(*Options) {
return func(opt *Options) {
opt.dumpReports = dump
}
}
//Run performs the calculation of the error bar calculation
func (app *App) Run(ctx context.Context, optFns ...func(*Options)) error {
orgName, err := app.repo.GetOrgName(ctx)
if err != nil {
return err
}
log.Printf("[info] start run in the `%s` organization.", orgName)
opts := &Options{
backfill: 3,
dryRun: false,
}
for _, optFn := range optFns {
optFn(opts)
}
repo := app.repo
if opts.dryRun {
log.Println("[notice] **with dry run**")
repo = repo.WithDryRun()
}
if opts.backfill <= 0 {
return errors.New("backfill must over 0")
}
now := flextime.Now()
for _, d := range app.SLODefinitions {
log.Printf("[info] service level objective[id=%s]: start create reports \n", d.ID())
reports, err := d.CreateReports(ctx, repo, now, opts.backfill)
if err != nil {
return fmt.Errorf("service level objective[id=%s]: create report faileds: %w", d.ID(), err)
}
if len(reports) > opts.backfill {
sort.Slice(reports, func(i, j int) bool {
return reports[i].DataPoint.Before(reports[j].DataPoint)
})
n := len(reports) - opts.backfill
if n < 0 {
n = 0
}
reports = reports[n:]
}
log.Printf("[info] service level objective[id=%s]: finish create reports \n", d.ID())
if opts.dumpReports {
for _, report := range reports {
log.Printf("[info] %s", report)
}
}
log.Printf("[info] service level objective[id=%s]: start save reports \n", d.ID())
if err := repo.SaveReports(ctx, reports); err != nil {
return fmt.Errorf("objective[%s] save report failed: %w", d.ID(), err)
}
log.Printf("[info] service level objective[id=%s]: finish save reports \n", d.ID())
}
runTime := flextime.Now().Sub(now)
log.Printf("[info] run successes. run time:%s\n", runTime)
return nil
}