Repository navigation
Expand file tree
/
Copy pathconfig.go
More file actions
107 lines (99 loc) · 3.3 KB
/
Copy pathconfig.go
File metadata and controls
107 lines (99 loc) · 3.3 KB
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
package main
import (
"fmt"
"net"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// Program parameters come from the command line; everything an operator changes
// day to day — targets, the admin password — lives in SQLite and is edited in the
// web console. Nothing is read from a config file, so there is no file to find,
// no file to back up separately, and no file whose format can drift from the UI.
// licenceName is what the program calls its own licence, everywhere it says so.
// It is not translated: it has to match the LICENSE file well enough for a
// reader to check, and a test asserts that it does. This project shipped four
// places claiming MIT over an Apache 2.0 LICENSE once, so the claim and the
// file are pinned to each other rather than both to somebody's memory.
const licenceName = "Elastic License 2.0"
type Config struct {
Listen string
DataDir string
Probe ProbeCfg
HotDays int // full samples kept this long
RetentionDays int // downsampled data kept this long
}
func defaultConfig() *Config {
return &Config{
// Loopback by default; see defaultBind in settings.go for why that is a
// security decision and not a preference. The console changes it.
Listen: defaultBind + ":8518",
DataDir: "./data",
Probe: ProbeCfg{IntervalSec: 60, Packets: 20, GapMs: 50, TimeoutMs: 1000},
HotDays: 2, // raw samples: enough for the sub-day windows
RetentionDays: 300, // hourly rollups; --days overrides
}
}
type TargetCfg struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // "icmp" (default) | "tcp"
Host string `json:"host"`
Port int `json:"port"` // tcp only
Pace string `json:"pace"` // "fast"(15s) | ""/"normal"(60s) | "slow"(300s)
IntervalSec int `json:"interval_sec"` // explicit seconds, highest priority
}
type ProbeCfg struct {
IntervalSec int
Packets int
GapMs int
TimeoutMs int
}
// normalizeTarget is the single validation gate for the web API, so the DB never
// holds a target the prober can't run.
func normalizeTarget(t *TargetCfg) error {
t.Type = strings.TrimSpace(t.Type)
t.Host = strings.TrimSpace(t.Host)
t.Name = strings.TrimSpace(t.Name)
t.Pace = strings.TrimSpace(t.Pace)
if t.Type == "" {
t.Type = "icmp"
}
switch t.Type {
case "icmp":
t.Port = 0
case "tcp":
if t.Port < 1 || t.Port > 65535 {
return fmt.Errorf("tcp target needs a port 1-65535")
}
default:
return fmt.Errorf("unknown type %q (icmp|tcp)", t.Type)
}
if t.Host == "" || len(t.Host) > 253 || strings.ContainsFunc(t.Host, unicode.IsSpace) {
return fmt.Errorf("invalid host %q", t.Host)
}
switch t.Pace {
case "normal":
t.Pace = ""
case "", "fast", "slow":
default:
return fmt.Errorf("invalid pace %q (fast|normal|slow)", t.Pace)
}
if t.IntervalSec < 0 || t.IntervalSec > 86400 {
return fmt.Errorf("interval must be 1-86400 seconds (0 = use pace)")
}
if t.Name == "" {
t.Name = targetAddr(*t)
}
if utf8.RuneCountInString(t.Name) > 64 || strings.ContainsFunc(t.Name, unicode.IsControl) {
return fmt.Errorf("name must be at most 64 printable characters")
}
return nil
}
func targetAddr(t TargetCfg) string {
if t.Type == "tcp" {
return net.JoinHostPort(t.Host, strconv.Itoa(t.Port))
}
return t.Host
}