1package nettests
2
3import (
4	"context"
5	"sync"
6	"time"
7
8	"github.com/apex/log"
9	"github.com/ooni/probe-cli/v3/cmd/ooniprobe/internal/database"
10	"github.com/ooni/probe-cli/v3/cmd/ooniprobe/internal/ooni"
11	"github.com/pkg/errors"
12)
13
14// RunGroupConfig contains the settings for running a nettest group.
15type RunGroupConfig struct {
16	GroupName  string
17	InputFiles []string
18	Inputs     []string
19	Probe      *ooni.Probe
20	RunType    string // hint for check-in API
21}
22
23const websitesURLLimitRemoved = `WARNING: CONFIGURATION CHANGE REQUIRED:
24
25* Since ooniprobe 3.9.0, websites_url_limit has been replaced
26  by websites_max_runtime in the configuration
27
28* To silence this warning either set websites_url_limit to zero or
29  replace it with websites_max_runtime
30
31* For the rest of 2021, we will automatically convert websites_url_limit
32  to websites_max_runtime (if the latter is not already set)
33
34* We will consider that each URL in websites_url_limit takes five
35  seconds to run and thus calculate websites_max_runtime
36
37* Since 2022, we will start silently ignoring websites_url_limit
38`
39
40var deprecationWarningOnce sync.Once
41
42// RunGroup runs a group of nettests according to the specified config.
43func RunGroup(config RunGroupConfig) error {
44	if config.Probe.Config().Nettests.WebsitesURLLimit > 0 {
45		if config.Probe.Config().Nettests.WebsitesMaxRuntime <= 0 {
46			limit := config.Probe.Config().Nettests.WebsitesURLLimit
47			maxRuntime := 5 * limit
48			config.Probe.Config().Nettests.WebsitesMaxRuntime = maxRuntime
49		}
50		deprecationWarningOnce.Do(func() {
51			log.Warn(websitesURLLimitRemoved)
52			time.Sleep(30 * time.Second)
53		})
54	}
55
56	if config.Probe.IsTerminated() {
57		log.Debugf("context is terminated, stopping runNettestGroup early")
58		return nil
59	}
60
61	sess, err := config.Probe.NewSession(context.Background())
62	if err != nil {
63		log.WithError(err).Error("Failed to create a measurement session")
64		return err
65	}
66	defer sess.Close()
67
68	err = sess.MaybeLookupLocation()
69	if err != nil {
70		log.WithError(err).Error("Failed to lookup the location of the probe")
71		return err
72	}
73	network, err := database.CreateNetwork(config.Probe.DB(), sess)
74	if err != nil {
75		log.WithError(err).Error("Failed to create the network row")
76		return err
77	}
78	if err := sess.MaybeLookupBackends(); err != nil {
79		log.WithError(err).Warn("Failed to discover OONI backends")
80		return err
81	}
82
83	group, ok := All[config.GroupName]
84	if !ok {
85		log.Errorf("No test group named %s", config.GroupName)
86		return errors.New("invalid test group name")
87	}
88	log.Debugf("Running test group %s", group.Label)
89
90	result, err := database.CreateResult(
91		config.Probe.DB(), config.Probe.Home(), config.GroupName, network.ID)
92	if err != nil {
93		log.Errorf("DB result error: %s", err)
94		return err
95	}
96
97	config.Probe.ListenForSignals()
98	config.Probe.MaybeListenForStdinClosed()
99	for i, nt := range group.Nettests {
100		if config.Probe.IsTerminated() {
101			log.Debugf("context is terminated, stopping group.Nettests early")
102			break
103		}
104		log.Debugf("Running test %T", nt)
105		ctl := NewController(nt, config.Probe, result, sess)
106		ctl.InputFiles = config.InputFiles
107		ctl.Inputs = config.Inputs
108		ctl.RunType = config.RunType
109		ctl.SetNettestIndex(i, len(group.Nettests))
110		if err = nt.Run(ctl); err != nil {
111			log.WithError(err).Errorf("Failed to run %s", group.Label)
112		}
113	}
114
115	if err = result.Finished(config.Probe.DB()); err != nil {
116		return err
117	}
118	return nil
119}
120