Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29ae26153f | ||
|
|
8109bb3fa0 | ||
|
|
27605997c1 | ||
|
|
6c455764e1 | ||
|
|
31a3d4948b |
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"Interval": "6h0m0s",
|
"Interval": {
|
||||||
|
"OK": "6h0m0s",
|
||||||
|
"Error": "6h",
|
||||||
|
"Email": "15m"
|
||||||
|
},
|
||||||
"States": [
|
"States": [
|
||||||
"GA"
|
"GA"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Interval Duration
|
Interval struct {
|
||||||
|
Email Duration
|
||||||
|
OK Duration
|
||||||
|
Error Duration
|
||||||
|
}
|
||||||
States []State
|
States []State
|
||||||
Storage []string
|
Storage []string
|
||||||
Client string
|
Client string
|
||||||
|
|||||||
@@ -2,17 +2,29 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Duration time.Duration
|
type Duration struct {
|
||||||
|
least time.Duration
|
||||||
|
most time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
func (d Duration) Get() time.Duration {
|
func (d Duration) Get() time.Duration {
|
||||||
return time.Duration(d)
|
jitter := d.most - d.least
|
||||||
|
if jitter >= time.Second {
|
||||||
|
jitter = time.Second * time.Duration(rand.Int()%int(jitter.Seconds()))
|
||||||
|
} else {
|
||||||
|
jitter = 0
|
||||||
|
}
|
||||||
|
return time.Duration(d.least + jitter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(d.Get().String())
|
return json.Marshal(d.least.String() + ".." + d.most.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
@@ -20,10 +32,25 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
|||||||
if err := json.Unmarshal(b, &s); err != nil {
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d2, err := time.ParseDuration(s)
|
if !strings.Contains(s, "..") {
|
||||||
|
s = s + ".." + s
|
||||||
|
}
|
||||||
|
splits := strings.Split(s, "..")
|
||||||
|
if len(splits) != 2 {
|
||||||
|
return errors.New("unexpected ..")
|
||||||
|
}
|
||||||
|
least, err := time.ParseDuration(splits[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*d = Duration(d2)
|
most, err := time.ParseDuration(splits[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if least > most {
|
||||||
|
most, least = least, most
|
||||||
|
}
|
||||||
|
d.most = most
|
||||||
|
d.least = least
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
105
config/duration_test.go
Normal file
105
config/duration_test.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDurationMarshal(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
in string
|
||||||
|
want Duration
|
||||||
|
wantJS string
|
||||||
|
}{
|
||||||
|
"empty string": {
|
||||||
|
wantJS: `"0s..0s"`,
|
||||||
|
},
|
||||||
|
"random in": {
|
||||||
|
in: "abc",
|
||||||
|
want: Duration{},
|
||||||
|
wantJS: `"0s..0s"`,
|
||||||
|
},
|
||||||
|
"1 dur": {
|
||||||
|
in: "1m",
|
||||||
|
want: Duration{
|
||||||
|
least: time.Minute,
|
||||||
|
most: time.Minute,
|
||||||
|
},
|
||||||
|
wantJS: `"1m0s..1m0s"`,
|
||||||
|
},
|
||||||
|
"descending": {
|
||||||
|
in: "2m..1m",
|
||||||
|
want: Duration{
|
||||||
|
least: time.Minute,
|
||||||
|
most: 2 * time.Minute,
|
||||||
|
},
|
||||||
|
wantJS: `"1m0s..2m0s"`,
|
||||||
|
},
|
||||||
|
"happy span": {
|
||||||
|
in: "1m..2m",
|
||||||
|
want: Duration{
|
||||||
|
least: time.Minute,
|
||||||
|
most: 2 * time.Minute,
|
||||||
|
},
|
||||||
|
wantJS: `"1m0s..2m0s"`,
|
||||||
|
},
|
||||||
|
"multi unit descending": {
|
||||||
|
in: "1h1m..2m3ms",
|
||||||
|
want: Duration{
|
||||||
|
least: 2*time.Minute + 3*time.Millisecond,
|
||||||
|
most: time.Hour + time.Minute,
|
||||||
|
},
|
||||||
|
wantJS: `"2m0.003s..1h1m0s"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
var got Duration
|
||||||
|
err := json.Unmarshal([]byte(`"`+c.in+`"`), &got)
|
||||||
|
if got != c.want {
|
||||||
|
t.Fatalf("err %v: want %+v, got %+v", err, c.want, got)
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(got)
|
||||||
|
if string(js) != c.wantJS {
|
||||||
|
t.Fatalf("want marshalled %s, got %s", c.wantJS, js)
|
||||||
|
}
|
||||||
|
var got2 Duration
|
||||||
|
json.Unmarshal(js, &got2)
|
||||||
|
if got != got2 {
|
||||||
|
t.Fatalf("want unmarshal-marshal-unmarshal %+v, got %+v", got, got2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDurationGet(t *testing.T) {
|
||||||
|
t.Run("same", func(t *testing.T) {
|
||||||
|
d := Duration{least: time.Second, most: time.Second}
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
if got := d.Get(); got != time.Second {
|
||||||
|
t.Error(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("1ms span", func(t *testing.T) {
|
||||||
|
d := Duration{least: time.Second, most: time.Second + time.Millisecond}
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
if got := d.Get(); got != time.Second {
|
||||||
|
t.Error(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("5s span", func(t *testing.T) {
|
||||||
|
d := Duration{least: time.Second, most: 6 * time.Second}
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
if got := d.Get(); got > time.Second*6 || got < time.Second {
|
||||||
|
t.Error(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
33
main.go
33
main.go
@@ -21,17 +21,16 @@ var stateFinder = regexp.MustCompile(`[A-Za-z]+`)
|
|||||||
func main() {
|
func main() {
|
||||||
lock := &sync.Mutex{}
|
lock := &sync.Mutex{}
|
||||||
go func() {
|
go func() {
|
||||||
c := time.NewTicker(time.Minute)
|
for {
|
||||||
for range c.C {
|
time.Sleep(config.Get().Interval.Email.Get())
|
||||||
if !config.Get().EmailerEnabled {
|
if config.Get().EmailerEnabled {
|
||||||
continue
|
|
||||||
}
|
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
if err := email(); err != nil {
|
if err := email(); err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
}
|
}
|
||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
if err := _main(); err != nil {
|
if err := _main(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -94,17 +93,31 @@ func parseOutStates(b []byte) []config.State {
|
|||||||
|
|
||||||
func _main() error {
|
func _main() error {
|
||||||
for {
|
for {
|
||||||
|
err := _mainOne()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if config.Get().Once {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(config.Get().Interval.Error.Get())
|
||||||
|
} else {
|
||||||
|
time.Sleep(config.Get().Interval.OK.Get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _mainOne() error {
|
||||||
|
log.Println("config.refreshing...")
|
||||||
if err := config.Refresh(); err != nil {
|
if err := config.Refresh(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Println("once...")
|
||||||
if err := once(); err != nil {
|
if err := once(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.Get().Once {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(config.Get().Interval.Get())
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ todo:
|
|||||||
- banlist criteria like vendors, brokers, metadata
|
- banlist criteria like vendors, brokers, metadata
|
||||||
- quiet hours
|
- quiet hours
|
||||||
- setup pa on element
|
- setup pa on element
|
||||||
|
- accept states via element for one system
|
||||||
done:
|
done:
|
||||||
|
- configurable email interval
|
||||||
|
- jitter on intervals, including dedicated err span
|
||||||
- email doesnt get all matches
|
- email doesnt get all matches
|
||||||
- send jobs
|
- send jobs
|
||||||
- read jobs
|
- read jobs
|
||||||
|
|||||||
Reference in New Issue
Block a user