@@ -61,6 +61,46 @@ func (s *Storage) UpdateExploits(ctx context.Context, exs []*epb.ExploitState) b
61
61
return true
62
62
}
63
63
64
+ func (s * Storage ) ScaleTimeouts (workers , teams int , target float64 ) {
65
+ // Alpha is a worker usage coefficient.
66
+ // For example, an exploit with timeout 10s and run every 20s
67
+ // Uses half of the worker's time for each team, so if teams = 4,
68
+ // exploit will use 2 full workers.
69
+ // Alpha in the case above will be 10/20 = 0.5 after the loop,
70
+ // if workers = 2 its final value will be 0.5 * 4 / 2 = 1,
71
+ // which means full worker utilization.
72
+ // If it's smaller, we could increase the timeouts, if larger --
73
+ // decrease them proportionally to their original values.
74
+ // Target allows to specify the desired Alpha value,
75
+ // as in most cases exploits finish before timeout,
76
+ // and "safe" case with target = 1 leads to
77
+ // suboptimal worker utilization.
78
+ // NB 1: endless exploits are not scaled.
79
+ // NB 2: timeouts are rounded down to nearest second.
80
+ alpha := 0.0
81
+
82
+ for _ , ex := range s .cache .Exploits () {
83
+ if ex .Endless {
84
+ continue
85
+ }
86
+ alpha += ex .Timeout .Seconds () / ex .RunEvery .Seconds ()
87
+ }
88
+ alpha = alpha * float64 (teams ) / float64 (workers )
89
+ logrus .Infof ("Scaling timeouts: alpha = %.2f, target = %.2f" , alpha , target )
90
+ for _ , ex := range s .cache .Exploits () {
91
+ if ex .Endless {
92
+ continue
93
+ }
94
+ newTimeout := time .Duration (float64 (ex .Timeout ) * target / alpha )
95
+
96
+ // Round down to nearest second.
97
+ newTimeout -= newTimeout % time .Second
98
+
99
+ logrus .Infof ("Scaling timeout for exploit %s: %s -> %s" , ex .ID , ex .ScaledTimeout , newTimeout )
100
+ ex .ScaledTimeout = newTimeout
101
+ }
102
+ }
103
+
64
104
func (s * Storage ) updateExploit (ctx context.Context , exploitID string ) (* State , error ) {
65
105
// Download the current exploit state.
66
106
resp , err := s .client .Exploit (ctx , exploitID )
@@ -115,14 +155,15 @@ func (s *Storage) updateExploit(ctx context.Context, exploitID string) (*State,
115
155
}
116
156
117
157
res := & State {
118
- ID : state .ExploitId ,
119
- Version : state .Version ,
120
- Dir : "" ,
121
- Path : entryPath ,
122
- Disabled : state .Config .Disabled ,
123
- Endless : state .Config .Endless ,
124
- RunEvery : state .Config .RunEvery .AsDuration (),
125
- Timeout : state .Config .Timeout .AsDuration (),
158
+ ID : state .ExploitId ,
159
+ Version : state .Version ,
160
+ Dir : "" ,
161
+ Path : entryPath ,
162
+ Disabled : state .Config .Disabled ,
163
+ Endless : state .Config .Endless ,
164
+ RunEvery : state .Config .RunEvery .AsDuration (),
165
+ ScaledTimeout : state .Config .Timeout .AsDuration (),
166
+ Timeout : state .Config .Timeout .AsDuration (),
126
167
}
127
168
if state .Config .IsArchive {
128
169
res .Dir = oPath
0 commit comments