|
7830697…
|
lmata
|
1 |
package registry |
|
7830697…
|
lmata
|
2 |
|
|
7830697…
|
lmata
|
3 |
import ( |
|
7830697…
|
lmata
|
4 |
"fmt" |
|
7830697…
|
lmata
|
5 |
"strings" |
|
7830697…
|
lmata
|
6 |
) |
|
7830697…
|
lmata
|
7 |
|
|
7830697…
|
lmata
|
8 |
// EngagementConfig is the rules-of-engagement configuration for a registered agent. |
|
7830697…
|
lmata
|
9 |
// Passed to Register() at registration time; signed into the payload returned to the agent. |
|
7830697…
|
lmata
|
10 |
type EngagementConfig struct { |
|
7830697…
|
lmata
|
11 |
// Channels is the list of IRC channels the agent should join. |
|
7830697…
|
lmata
|
12 |
Channels []string `json:"channels,omitempty"` |
|
7830697…
|
lmata
|
13 |
|
|
7830697…
|
lmata
|
14 |
// OpsChannels is a subset of Channels where the agent is granted +o (operator). |
|
7830697…
|
lmata
|
15 |
// Only meaningful for orchestrator-type agents. |
|
7830697…
|
lmata
|
16 |
OpsChannels []string `json:"ops_channels,omitempty"` |
|
7830697…
|
lmata
|
17 |
|
|
7830697…
|
lmata
|
18 |
// Permissions is the list of allowed action types (e.g. "task.create"). |
|
7830697…
|
lmata
|
19 |
// Empty means no explicit restrictions. |
|
7830697…
|
lmata
|
20 |
Permissions []string `json:"permissions,omitempty"` |
|
7830697…
|
lmata
|
21 |
|
|
7830697…
|
lmata
|
22 |
// RateLimit controls message throughput for this agent. |
|
7830697…
|
lmata
|
23 |
RateLimit RateLimitConfig `json:"rate_limit,omitempty"` |
|
7830697…
|
lmata
|
24 |
|
|
7830697…
|
lmata
|
25 |
// Rules defines engagement behaviour rules for this agent. |
|
7830697…
|
lmata
|
26 |
Rules EngagementRules `json:"engagement,omitempty"` |
|
7830697…
|
lmata
|
27 |
} |
|
7830697…
|
lmata
|
28 |
|
|
7830697…
|
lmata
|
29 |
// RateLimitConfig controls message throughput. |
|
7830697…
|
lmata
|
30 |
type RateLimitConfig struct { |
|
7830697…
|
lmata
|
31 |
// MessagesPerSecond is the sustained send rate allowed. 0 means no limit. |
|
7830697…
|
lmata
|
32 |
MessagesPerSecond float64 `json:"messages_per_second,omitempty"` |
|
7830697…
|
lmata
|
33 |
|
|
7830697…
|
lmata
|
34 |
// Burst is the maximum burst above MessagesPerSecond. 0 means no burst. |
|
7830697…
|
lmata
|
35 |
Burst int `json:"burst,omitempty"` |
|
7830697…
|
lmata
|
36 |
} |
|
7830697…
|
lmata
|
37 |
|
|
7830697…
|
lmata
|
38 |
// EngagementRules defines what message types and peers the agent should engage with. |
|
7830697…
|
lmata
|
39 |
type EngagementRules struct { |
|
7830697…
|
lmata
|
40 |
// RespondToTypes restricts which message types trigger handler callbacks. |
|
7830697…
|
lmata
|
41 |
// Empty means respond to all types. |
|
7830697…
|
lmata
|
42 |
RespondToTypes []string `json:"respond_to_types,omitempty"` |
|
7830697…
|
lmata
|
43 |
|
|
7830697…
|
lmata
|
44 |
// IgnoreNicks is a list of IRC nicks whose messages are always ignored. |
|
7830697…
|
lmata
|
45 |
IgnoreNicks []string `json:"ignore_nicks,omitempty"` |
|
7830697…
|
lmata
|
46 |
} |
|
7830697…
|
lmata
|
47 |
|
|
7830697…
|
lmata
|
48 |
// Validate checks the EngagementConfig for obvious errors. |
|
7830697…
|
lmata
|
49 |
// Returns a descriptive error for the first problem found. |
|
7830697…
|
lmata
|
50 |
func (c EngagementConfig) Validate() error { |
|
7830697…
|
lmata
|
51 |
for _, ch := range c.Channels { |
|
7830697…
|
lmata
|
52 |
if !strings.HasPrefix(ch, "#") { |
|
7830697…
|
lmata
|
53 |
return fmt.Errorf("engagement: channel %q must start with #", ch) |
|
7830697…
|
lmata
|
54 |
} |
|
7830697…
|
lmata
|
55 |
if strings.ContainsAny(ch, " \t\r\n,") { |
|
7830697…
|
lmata
|
56 |
return fmt.Errorf("engagement: channel %q contains invalid characters", ch) |
|
7830697…
|
lmata
|
57 |
} |
|
7830697…
|
lmata
|
58 |
if len(ch) < 2 { |
|
7830697…
|
lmata
|
59 |
return fmt.Errorf("engagement: channel %q is too short", ch) |
|
7830697…
|
lmata
|
60 |
} |
|
7830697…
|
lmata
|
61 |
} |
|
7830697…
|
lmata
|
62 |
|
|
7830697…
|
lmata
|
63 |
// OpsChannels must be a subset of Channels. |
|
7830697…
|
lmata
|
64 |
joinSet := make(map[string]struct{}, len(c.Channels)) |
|
7830697…
|
lmata
|
65 |
for _, ch := range c.Channels { |
|
7830697…
|
lmata
|
66 |
joinSet[ch] = struct{}{} |
|
7830697…
|
lmata
|
67 |
} |
|
7830697…
|
lmata
|
68 |
for _, ch := range c.OpsChannels { |
|
7830697…
|
lmata
|
69 |
if _, ok := joinSet[ch]; !ok { |
|
7830697…
|
lmata
|
70 |
return fmt.Errorf("engagement: ops_channel %q is not in channels list", ch) |
|
7830697…
|
lmata
|
71 |
} |
|
7830697…
|
lmata
|
72 |
} |
|
7830697…
|
lmata
|
73 |
|
|
7830697…
|
lmata
|
74 |
if c.RateLimit.MessagesPerSecond < 0 { |
|
7830697…
|
lmata
|
75 |
return fmt.Errorf("engagement: rate_limit.messages_per_second must be >= 0") |
|
7830697…
|
lmata
|
76 |
} |
|
7830697…
|
lmata
|
77 |
if c.RateLimit.Burst < 0 { |
|
7830697…
|
lmata
|
78 |
return fmt.Errorf("engagement: rate_limit.burst must be >= 0") |
|
7830697…
|
lmata
|
79 |
} |
|
7830697…
|
lmata
|
80 |
|
|
7830697…
|
lmata
|
81 |
for _, t := range c.Rules.RespondToTypes { |
|
7830697…
|
lmata
|
82 |
if t == "" { |
|
7830697…
|
lmata
|
83 |
return fmt.Errorf("engagement: respond_to_types contains empty string") |
|
7830697…
|
lmata
|
84 |
} |
|
7830697…
|
lmata
|
85 |
} |
|
7830697…
|
lmata
|
86 |
|
|
7830697…
|
lmata
|
87 |
return nil |
|
7830697…
|
lmata
|
88 |
} |