Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ To configure log verbosity, set the `VERBOSITY` environment variable to one of t
| `PING_INTERVAL` | ping interval in seconds | `"60"` |
| `PING_REQUESTS` | number of requests each test | `"600"` |
| `REALTIME` | enable real-time features if on paid plan | `"true"` |
| `SPEED_TEST_DAILY_TOTAL` | approximate number of speed test to run per day | `"6"` |
| `VERBOSITY` | controls log level. must be one of `debug`, `info`, `warn`, `error` | `"info"` |

## Flags
Expand Down Expand Up @@ -180,6 +181,8 @@ Usage: imup
api endpoint for imup realtime reloadable configuration, default is https://api.imup.io/v1/realtime/config
-should-run-speed-test-address string
api endpoint for imup realtime speed tests, default is https://api.imup.io/v1/realtime/shouldClientRunSpeedTest
-speed-test-daily-total string
configure the total number of speed tests to run in a day, [1-32]
-speed-test-results-address string
api endpoint for imup realtime speed test results, default is https://api.imup.io/v1/realtime/speedTestResults
-speed-test-status-update-address string
Expand Down
24 changes: 21 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var (
shouldRunSpeedTestAddress *string
speedTestResultsAddress *string
speedTestStatusUpdateAddress *string
speedTestTotal *string
verbosity *string

insecureSpeedTest *bool
Expand Down Expand Up @@ -104,6 +105,7 @@ type Reloadable interface {
PingRequestsCount() int
ConnRequestsCount() int
IMUPDataLen() int
SpeedTestDaily() int
}

// cfg is intentionally declared in the global space, but un-exported
Expand Down Expand Up @@ -136,6 +138,7 @@ type config struct {
PingDelay int
PingInterval int
PingRequests int
SpeedTestTotal int

PingAddressesExternal []string

Expand Down Expand Up @@ -189,6 +192,7 @@ func New() (Reloadable, error) {
shouldRunSpeedTestAddress = flag.String("should-run-speed-test-address", "", fmt.Sprintf("api endpoint for imup realtime speed tests, default is %s/v1/realtime/shouldClientRunSpeedTest", ImUpAPIHost))
speedTestResultsAddress = flag.String("speed-test-results-address", "", fmt.Sprintf("api endpoint for imup realtime speed test results, default is %s/v1/realtime/speedTestResults", ImUpAPIHost))
speedTestStatusUpdateAddress = flag.String("speed-test-status-update-address", "", fmt.Sprintf("api endpoint for imup real-time speed test status updates, default is %s/v1/realtime/speedTestStatusUpdate", ImUpAPIHost))
speedTestTotal = flag.String("speed-test-daily-total", "", "configure the total number of speed tests to run in a day, [1-32]")
verbosity = flag.String("verbosity", "", "verbosity for log output [debug, info, warn, error], default is info")

insecureSpeedTest = flag.Bool("insecure", false, "run insecure speed tests (ws:// and not wss://), default is false")
Expand All @@ -214,15 +218,13 @@ func New() (Reloadable, error) {
cfg.BlocklistedIPs = strings.Split(util.ValueOr(blocklistedIPs, "BLOCKLISTED_IPS", ""), ",")
cfg.ConfigVersion = util.ValueOr(configVersion, "CONFIG_VERSION", "dev-preview") //todo: placeholder for reloadable configs
cfg.Group = util.ValueOr(groupID, "GROUP_ID", "")

cfg.PingAddressInternal = util.ValueOr(pingAddressInternal, "PING_ADDRESS_INTERNAL", cfg.discoverGateway())
cfg.LivenessCheckInAddress = util.ValueOr(livenessCheckInAddress, "IMUP_LIVENESS_CHECKIN_ADDRESS", fmt.Sprintf("%s/v1/realtime/livenesscheckin", ImUpAPIHost))
cfg.RealtimeAuthorized = util.ValueOr(realtimeAuthorized, "IMUP_REALTIME_AUTHORIZED", fmt.Sprintf("%s/v1/auth/realtimeAuthorized", ImUpAPIHost))
cfg.RealtimeConfig = util.ValueOr(realtimeConfig, "IMUP_REALTIME_CONFIG", fmt.Sprintf("%s/v1/realtime/config", ImUpAPIHost))
cfg.ShouldRunSpeedTestAddress = util.ValueOr(shouldRunSpeedTestAddress, "IMUP_SHOULD_RUN_SPEEDTEST_ADDRESS", fmt.Sprintf("%s/v1/realtime/shouldClientRunSpeedTest", ImUpAPIHost))
cfg.SpeedTestResultsAddress = util.ValueOr(speedTestResultsAddress, "IMUP_SPEED_TEST_RESULTS_ADDRESS", fmt.Sprintf("%s/v1/realtime/speedTestResults", ImUpAPIHost))
cfg.SpeedTestStatusUpdateAddress = util.ValueOr(speedTestStatusUpdateAddress, "IMUP_SPEED_TEST_STATUS_ADDRESS", fmt.Sprintf("%s/v1/realtime/speedTestStatusUpdate", ImUpAPIHost))

cfg.PingAddressInternal = util.ValueOr(pingAddressInternal, "PING_ADDRESS_INTERNAL", "")
cfg.PingAddressesExternal = strings.Split(util.ValueOr(pingAddressesExternal, "PING_ADDRESS", "1.1.1.1/32,1.0.0.1/32,8.8.8.8/32,8.8.4.4/32"), ",")

var err error
Expand Down Expand Up @@ -268,6 +270,12 @@ func New() (Reloadable, error) {
panic(err)
}

SpeedTestTotalStr := util.ValueOr(speedTestTotal, "SPEED_TEST_DAILY_TOTAL", "6")
cfg.SpeedTestTotal, err = strconv.Atoi(SpeedTestTotalStr)
if err != nil {
panic(err)
}

logFilePathStr := util.ValueOr(logFile, "LOG_FILE", "")
cfg.InsecureSpeedTest = util.BooleanValueOr(insecureSpeedTest, "INSECURE_SPEED_TEST", "false")
cfg.FileLogger = util.BooleanValueOr(logToFile, "LOG_TO_FILE", "false")
Expand All @@ -279,6 +287,10 @@ func New() (Reloadable, error) {

cfg.logLevel = util.LevelMap(verbosity, "VERBOSITY", "info")

if !cfg.NoDiscoverGateway && cfg.PingAddressInternal == "" {
cfg.PingAddressInternal = cfg.discoverGateway()
}

var w io.Writer
if logFilePathStr != "" {
w = logToThisFile(logFilePathStr)
Expand Down Expand Up @@ -565,3 +577,9 @@ func (c *config) IMUPDataLen() int {
defer mu.RUnlock()
return cfg.IMUPDataLength
}

func (c *config) SpeedTestDaily() int {
mu.RLock()
defer mu.RUnlock()
return cfg.SpeedTestTotal
}
18 changes: 12 additions & 6 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@ import (
"gonum.org/v1/gonum/stat/distuv"
)

// timePeriodMinutes is the desired interval between tests
// the default (fixed) configuration is one tests every four hours
const timePeriodMinutes = 4 * 60

// speedTestInterval takes advantage of a poisson distribution
// to generate pseudo random speed tests
// this distribution helps guarantee a consistent number of speed tests
// with a smaller chance of frequent speed tests consuming large amounts of data
// or saturating a network
func speedTestInterval() time.Duration {
//
// numTests is the desired number of speed tests to run, within a 24 hour period
func speedTestInterval(numTests int) time.Duration {
if numTests < 1 {
numTests = 1
}

if numTests > 32 {
numTests = 32
}

t := distuv.Poisson{
Lambda: timePeriodMinutes,
Lambda: float64(1440 / numTests),
}.Rand()

return time.Duration(t * float64(time.Minute))
Expand Down
54 changes: 48 additions & 6 deletions helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,65 @@ import (

func Test_SpeedTestFrequency(t *testing.T) {
is := is.New(t)
hours := 24
maxTests := 8
minutesInDay := 1440
maxTests := 6

//
tf := func() int {
numTests := 0
for i := 1; i <= hours; {
t1 := speedTestInterval()
i += int(t1.Hours())
for i := 1; i < minutesInDay; {
t1 := speedTestInterval(maxTests)
i += int(t1.Minutes())
numTests++
}

return numTests
}

results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()}
is.True(max(results) <= maxTests)
is.True(max(results) <= maxTests+1)
is.True(min(results) > maxTests/2)
}

func Test_MaxSpeedTestFrequency(t *testing.T) {
is := is.New(t)
minutesInDay := 1440
maxTests := 32

//
tf := func() int {
numTests := 0
for i := 1; i < minutesInDay; {
t1 := speedTestInterval(maxTests)
i += int(t1.Minutes())
numTests++
}

return numTests
}

results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()}
is.True(max(results) <= maxTests+2)
is.True(min(results) > maxTests/2)
}

func Test_MinSpeedTestFrequency(t *testing.T) {
is := is.New(t)
minutesInDay := 1440
minTests := 1

//
tf := func() int {
numTests := 0
for i := 1; i < minutesInDay; {
t1 := speedTestInterval(minTests)
i += int(t1.Minutes())
numTests++
}

return numTests
}

results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()}
is.True(max(results) <= minTests+1)
}
2 changes: 1 addition & 1 deletion run.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func run(ctx context.Context, shutdown chan os.Signal) error {
// collects speed test data using the ndt7 protocol
// data is collected pseudo randomly, every 4 hours
go func() {
ticker := time.NewTicker(speedTestInterval())
ticker := time.NewTicker(speedTestInterval(imup.cfg.SpeedTestDaily()))
defer ticker.Stop()
for {
if imup.cfg.SpeedTests() {
Expand Down