Skip to content

Commit 2947ede

Browse files
authored
feat(riseupvpn): include bootstrap in progress (#1364)
This diff modifies riseupvpn to include bootstrap in progress. We define as riseupvpn's bootstrap the process of fetching the CA and the various JSON files needed to find out the gateways to measure. I previously stopped including bootstrap in progress in #1363, which I did because the progress was not monotonic. This diff introduces helpers that make it very obvious to emit monotonic bootstrap through the ./internal/progress package. This work is part of ooni/probe#1432.
1 parent db70616 commit 2947ede

3 files changed

Lines changed: 141 additions & 10 deletions

File tree

internal/experiment/riseupvpn/riseupvpn.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/ooni/probe-cli/v3/internal/experiment/urlgetter"
1212
"github.com/ooni/probe-cli/v3/internal/model"
1313
"github.com/ooni/probe-cli/v3/internal/netxlite"
14+
"github.com/ooni/probe-cli/v3/internal/progress"
1415
)
1516

1617
const (
@@ -165,8 +166,8 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
165166
// TODO(https://github.com/ooni/probe/issues/2559): solve this problem by serving the
166167
// correct CA and the endpoints to probes using check-in v2 (aka richer input).
167168

168-
nullCallbacks := model.NewPrinterCallbacks(model.DiscardLogger)
169-
for entry := range multi.CollectOverall(ctx, inputs, 0, 20, "riseupvpn", nullCallbacks) {
169+
callbacksStage1 := progress.NewScaler(callbacks, 0, 0.25)
170+
for entry := range multi.Collect(ctx, inputs, "riseupvpn", callbacksStage1) {
170171
tk := entry.TestKeys
171172
testkeys.AddCACertFetchTestKeys(tk)
172173
if tk.Failure != nil {
@@ -203,7 +204,9 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
203204
FailOnHTTPError: true,
204205
}},
205206
}
206-
for entry := range multi.CollectOverall(ctx, inputs, 1, 20, "riseupvpn", nullCallbacks) {
207+
208+
callbacksStage2 := progress.NewScaler(callbacks, 0.25, 0.5)
209+
for entry := range multi.Collect(ctx, inputs, "riseupvpn", callbacksStage2) {
207210
testkeys.UpdateProviderAPITestKeys(entry)
208211
tk := entry.TestKeys
209212
if tk.Failure != nil {
@@ -216,22 +219,19 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
216219
gateways := parseGateways(testkeys)
217220
openvpnEndpoints := generateMultiInputs(gateways, "openvpn")
218221
obfs4Endpoints := generateMultiInputs(gateways, "obfs4")
219-
overallCount := 1 + len(inputs) + len(openvpnEndpoints) + len(obfs4Endpoints)
220-
startCount := 1 + len(inputs)
221222

222223
// measure openvpn in parallel
223-
for entry := range multi.CollectOverall(
224-
ctx, openvpnEndpoints, startCount, overallCount, "riseupvpn", callbacks) {
224+
callbacksStage3 := progress.NewScaler(callbacks, 0.5, 0.75)
225+
for entry := range multi.Collect(ctx, openvpnEndpoints, "riseupvpn", callbacksStage3) {
225226
testkeys.AddGatewayConnectTestKeys(entry, "openvpn")
226227
}
227228

228229
// measure obfs4 in parallel
229230
// TODO(bassosimone): when urlgetter is able to do obfs4 handshakes, here
230231
// can possibly also test for the obfs4 handshake.
231232
// See https://github.com/ooni/probe/issues/1463.
232-
startCount += len(openvpnEndpoints)
233-
for entry := range multi.CollectOverall(
234-
ctx, obfs4Endpoints, startCount, overallCount, "riseupvpn", callbacks) {
233+
callbacksStage4 := progress.NewScaler(callbacks, 0.75, 1)
234+
for entry := range multi.Collect(ctx, obfs4Endpoints, "riseupvpn", callbacksStage4) {
235235
testkeys.AddGatewayConnectTestKeys(entry, "obfs4")
236236
}
237237

internal/progress/progress.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Package progress contains utilities to emit progress.
2+
package progress
3+
4+
import (
5+
"github.com/ooni/probe-cli/v3/internal/model"
6+
"github.com/ooni/probe-cli/v3/internal/runtimex"
7+
)
8+
9+
// Scaler implements [model.ExperimentCallbacks] and scales progress
10+
// as instructed through the [NewScaler] constructor.
11+
//
12+
// The [*Scaler] is safe to use from multiple goroutine contexts.
13+
type Scaler struct {
14+
cbs model.ExperimentCallbacks
15+
offset float64
16+
total float64
17+
}
18+
19+
// NewScaler constructs a new [*Scaler] using the given offset and total
20+
// and emitting progress using the given [model.ExperimentCallbacks].
21+
//
22+
// The offset is added to each progress value we emit. The total is
23+
// used to scale the 100% to a suitable subset.
24+
//
25+
// For example, with offset equal to 0.1 and total equal to 0.5, the value
26+
// 0.5 corresponds to 0.3 and the value 1 (i.e., 100%) is 0.5.
27+
//
28+
// This func PANICS if offset<0, offset >= total, total<=0, total>1.
29+
func NewScaler(callbacks model.ExperimentCallbacks, offset, total float64) *Scaler {
30+
runtimex.Assert(offset >= 0.0 && offset < total, "NewScaler: offset must be >= 0 and < total")
31+
runtimex.Assert(total > 0.0 && total <= 1, "NewScaler: total must be > 0 and <= 1")
32+
return &Scaler{
33+
cbs: callbacks,
34+
offset: offset,
35+
total: total,
36+
}
37+
}
38+
39+
var _ model.ExperimentCallbacks = &Scaler{}
40+
41+
// OnProgress implements model.ExperimentCallbacks.
42+
func (s *Scaler) OnProgress(percentage float64, message string) {
43+
s.cbs.OnProgress(s.offset+percentage*(s.total-s.offset), message)
44+
}

internal/progress/progress_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package progress
2+
3+
import (
4+
"testing"
5+
6+
"github.com/google/go-cmp/cmp"
7+
"github.com/google/go-cmp/cmp/cmpopts"
8+
"github.com/ooni/probe-cli/v3/internal/model"
9+
)
10+
11+
type capturerCallbacks struct {
12+
value float64
13+
}
14+
15+
var _ model.ExperimentCallbacks = &capturerCallbacks{}
16+
17+
// OnProgress implements model.ExperimentCallbacks.
18+
func (v *capturerCallbacks) OnProgress(percentage float64, message string) {
19+
v.value = percentage
20+
}
21+
22+
func TestScaler(t *testing.T) {
23+
// testcase is a test case run by this function.
24+
type testcase struct {
25+
// name is the test case name.
26+
name string
27+
28+
// offset is the offset (>=0, <total)
29+
offset float64
30+
31+
// total is the total (>0, <=1)
32+
total float64
33+
34+
// emit is the list of progress values to emit.
35+
emit []float64
36+
37+
// expect is the list of progress values we expect in output.
38+
expect []float64
39+
}
40+
41+
cases := []testcase{{
42+
name: "with offset==0 and total=1",
43+
offset: 0,
44+
total: 1,
45+
emit: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
46+
expect: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
47+
}, {
48+
name: "with offset==0 and total=0.5",
49+
offset: 0,
50+
total: 0.5,
51+
emit: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
52+
expect: []float64{0, 0.1, 0.2, 0.3, 0.4, 0.5},
53+
}, {
54+
name: "with offset==0.5 and total=1",
55+
offset: 0.5,
56+
total: 1,
57+
emit: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
58+
expect: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 1},
59+
}, {
60+
name: "with offset==0.2 and total=0.7",
61+
offset: 0.2,
62+
total: 0.7,
63+
emit: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
64+
expect: []float64{0.2, 0.3, 0.4, 0.5, 0.6, 0.7},
65+
}, {
66+
name: "with offset=0.4 and total=0.5",
67+
offset: 0.4,
68+
total: 0.5,
69+
emit: []float64{0, 0.2, 0.4, 0.6, 0.8, 1},
70+
expect: []float64{0.4, 0.42, 0.44, 0.46, 0.48, 0.5},
71+
}}
72+
73+
for _, tc := range cases {
74+
t.Run(tc.name, func(t *testing.T) {
75+
var got []float64
76+
for _, v := range tc.emit {
77+
cc := &capturerCallbacks{}
78+
wrapper := NewScaler(cc, tc.offset, tc.total)
79+
wrapper.OnProgress(v, "")
80+
got = append(got, cc.value)
81+
}
82+
if diff := cmp.Diff(tc.expect, got, cmpopts.EquateApprox(0, 0.01)); diff != "" {
83+
t.Fatal(diff)
84+
}
85+
})
86+
}
87+
}

0 commit comments

Comments
 (0)