Pull request: 1992 configurable timeouts
Merge in DNS/adguard-home from 1992-conf-timeouts to master
Updates #1992.
Squashed commit of the following:
commit 1050c54fb407bec0617728690763b0392b3440b0
Merge: 05f71c3e 59544160
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 27 20:04:25 2021 +0300
Merge branch 'master' into 1992-conf-timeouts
commit 05f71c3e5397909d943e69d49a247bf4f67619b0
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 27 20:03:06 2021 +0300
home: use const
commit d0861792b42e6d066aa3ffdb3937e29477235ee0
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 27 19:16:26 2021 +0300
home: fix default timeout
commit ba26fcdcf66366350c89d5a82c4da91ade45838f
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 27 16:50:33 2021 +0300
all: mk ping timeout configurable
This commit is contained in:
@@ -8,12 +8,14 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||
"github.com/AdguardTeam/dnsproxy/fastip"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/google/renameio/maybe"
|
||||
@@ -106,9 +108,9 @@ type dnsConfig struct {
|
||||
QueryLogEnabled bool `yaml:"querylog_enabled"` // if true, query log is enabled
|
||||
QueryLogFileEnabled bool `yaml:"querylog_file_enabled"` // if true, query log will be written to a file
|
||||
// QueryLogInterval is the interval for query log's files rotation.
|
||||
QueryLogInterval Duration `yaml:"querylog_interval"`
|
||||
QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk
|
||||
AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
|
||||
QueryLogInterval aghtime.Duration `yaml:"querylog_interval"`
|
||||
QueryLogMemSize uint32 `yaml:"querylog_size_memory"` // number of entries kept in memory before they are flushed to disk
|
||||
AnonymizeClientIP bool `yaml:"anonymize_client_ip"` // anonymize clients' IP addresses in logs and stats
|
||||
|
||||
dnsforward.FilteringConfig `yaml:",inline"`
|
||||
|
||||
@@ -117,7 +119,7 @@ type dnsConfig struct {
|
||||
DnsfilterConf filtering.Config `yaml:",inline"`
|
||||
|
||||
// UpstreamTimeout is the timeout for querying upstream servers.
|
||||
UpstreamTimeout Duration `yaml:"upstream_timeout"`
|
||||
UpstreamTimeout aghtime.Duration `yaml:"upstream_timeout"`
|
||||
|
||||
// LocalDomainName is the domain name used for known internal hosts.
|
||||
// For example, a machine called "myhost" can be addressed as
|
||||
@@ -178,6 +180,9 @@ var config = configuration{
|
||||
Ratelimit: 20,
|
||||
RefuseAny: true,
|
||||
AllServers: false,
|
||||
FastestTimeout: aghtime.Duration{
|
||||
Duration: fastip.DefaultPingWaitTimeout,
|
||||
},
|
||||
|
||||
TrustedProxies: []string{"127.0.0.0/8", "::1/128"},
|
||||
|
||||
@@ -189,7 +194,7 @@ var config = configuration{
|
||||
},
|
||||
FilteringEnabled: true, // whether or not use filter lists
|
||||
FiltersUpdateIntervalHours: 24,
|
||||
UpstreamTimeout: Duration{Duration: dnsforward.DefaultTimeout},
|
||||
UpstreamTimeout: aghtime.Duration{Duration: dnsforward.DefaultTimeout},
|
||||
LocalDomainName: "lan",
|
||||
ResolveClients: true,
|
||||
UsePrivateRDNS: true,
|
||||
@@ -216,7 +221,7 @@ func initConfig() {
|
||||
|
||||
config.DNS.QueryLogEnabled = true
|
||||
config.DNS.QueryLogFileEnabled = true
|
||||
config.DNS.QueryLogInterval = Duration{Duration: 90 * 24 * time.Hour}
|
||||
config.DNS.QueryLogInterval = aghtime.Duration{Duration: 90 * 24 * time.Hour}
|
||||
config.DNS.QueryLogMemSize = 1000
|
||||
|
||||
config.DNS.CacheSize = 4 * 1024 * 1024
|
||||
@@ -285,7 +290,7 @@ func parseConfig() error {
|
||||
}
|
||||
|
||||
if config.DNS.UpstreamTimeout.Duration == 0 {
|
||||
config.DNS.UpstreamTimeout = Duration{Duration: dnsforward.DefaultTimeout}
|
||||
config.DNS.UpstreamTimeout = aghtime.Duration{Duration: dnsforward.DefaultTimeout}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -332,7 +337,7 @@ func (c *configuration) write() error {
|
||||
Context.queryLog.WriteDiskConfig(&dc)
|
||||
config.DNS.QueryLogEnabled = dc.Enabled
|
||||
config.DNS.QueryLogFileEnabled = dc.FileEnabled
|
||||
config.DNS.QueryLogInterval = Duration{Duration: dc.RotationIvl}
|
||||
config.DNS.QueryLogInterval = aghtime.Duration{Duration: dc.RotationIvl}
|
||||
config.DNS.QueryLogMemSize = dc.MemSize
|
||||
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// Duration is a wrapper for time.Duration providing functionality for encoding.
|
||||
type Duration struct {
|
||||
// time.Duration is embedded here to avoid implementing all the methods.
|
||||
time.Duration
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface for Duration. It wraps
|
||||
// time.Duration.String method and additionally cuts off non-leading zero values
|
||||
// of minutes and seconds. Some values which are differ between the
|
||||
// implementations:
|
||||
//
|
||||
// Duration: "1m", time.Duration: "1m0s"
|
||||
// Duration: "1h", time.Duration: "1h0m0s"
|
||||
// Duration: "1h1m", time.Duration: "1h1m0s"
|
||||
//
|
||||
func (d Duration) String() (str string) {
|
||||
str = d.Duration.String()
|
||||
|
||||
const (
|
||||
tailMin = len(`0s`)
|
||||
tailMinSec = len(`0m0s`)
|
||||
|
||||
secsInHour = time.Hour / time.Second
|
||||
minsInHour = time.Hour / time.Minute
|
||||
)
|
||||
|
||||
switch rounded := d.Duration / time.Second; {
|
||||
case
|
||||
rounded == 0,
|
||||
rounded*time.Second != d.Duration,
|
||||
rounded%60 != 0:
|
||||
// Return the uncutted value if it's either equal to zero or has
|
||||
// fractions of a second or even whole seconds in it.
|
||||
return str
|
||||
|
||||
case (rounded%secsInHour)/minsInHour != 0:
|
||||
return str[:len(str)-tailMin]
|
||||
|
||||
default:
|
||||
return str[:len(str)-tailMinSec]
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface for Duration.
|
||||
func (d Duration) MarshalText() (text []byte, err error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface for
|
||||
// *Duration.
|
||||
//
|
||||
// TODO(e.burkov): Make it able to parse larger units like days.
|
||||
func (d *Duration) UnmarshalText(b []byte) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "unmarshalling duration: %w") }()
|
||||
|
||||
d.Duration, err = time.ParseDuration(string(b))
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestDuration_String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
val time.Duration
|
||||
}{{
|
||||
name: "1s",
|
||||
val: time.Second,
|
||||
}, {
|
||||
name: "1m",
|
||||
val: time.Minute,
|
||||
}, {
|
||||
name: "1h",
|
||||
val: time.Hour,
|
||||
}, {
|
||||
name: "1m1s",
|
||||
val: time.Minute + time.Second,
|
||||
}, {
|
||||
name: "1h1m",
|
||||
val: time.Hour + time.Minute,
|
||||
}, {
|
||||
name: "1h0m1s",
|
||||
val: time.Hour + time.Second,
|
||||
}, {
|
||||
name: "1ms",
|
||||
val: time.Millisecond,
|
||||
}, {
|
||||
name: "1h0m0.001s",
|
||||
val: time.Hour + time.Millisecond,
|
||||
}, {
|
||||
name: "1.001s",
|
||||
val: time.Second + time.Millisecond,
|
||||
}, {
|
||||
name: "1m1.001s",
|
||||
val: time.Minute + time.Second + time.Millisecond,
|
||||
}, {
|
||||
name: "0s",
|
||||
val: 0,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := Duration{Duration: tc.val}
|
||||
assert.Equal(t, tc.name, d.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// durationEncodingTester is a helper struct to simplify testing different
|
||||
// Duration marshalling and unmarshalling cases.
|
||||
type durationEncodingTester struct {
|
||||
PtrMap map[string]*Duration `json:"ptr_map" yaml:"ptr_map"`
|
||||
PtrSlice []*Duration `json:"ptr_slice" yaml:"ptr_slice"`
|
||||
PtrValue *Duration `json:"ptr_value" yaml:"ptr_value"`
|
||||
PtrArray [1]*Duration `json:"ptr_array" yaml:"ptr_array"`
|
||||
Map map[string]Duration `json:"map" yaml:"map"`
|
||||
Slice []Duration `json:"slice" yaml:"slice"`
|
||||
Value Duration `json:"value" yaml:"value"`
|
||||
Array [1]Duration `json:"array" yaml:"array"`
|
||||
}
|
||||
|
||||
const nl = "\n"
|
||||
const (
|
||||
jsonStr = `{` +
|
||||
`"ptr_map":{"dur":"1ms"},` +
|
||||
`"ptr_slice":["1ms"],` +
|
||||
`"ptr_value":"1ms",` +
|
||||
`"ptr_array":["1ms"],` +
|
||||
`"map":{"dur":"1ms"},` +
|
||||
`"slice":["1ms"],` +
|
||||
`"value":"1ms",` +
|
||||
`"array":["1ms"]` +
|
||||
`}`
|
||||
yamlStr = `ptr_map:` + nl +
|
||||
` dur: 1ms` + nl +
|
||||
`ptr_slice:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`ptr_value: 1ms` + nl +
|
||||
`ptr_array:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`map:` + nl +
|
||||
` dur: 1ms` + nl +
|
||||
`slice:` + nl +
|
||||
`- 1ms` + nl +
|
||||
`value: 1ms` + nl +
|
||||
`array:` + nl +
|
||||
`- 1ms`
|
||||
)
|
||||
|
||||
// defaultTestDur is the default time.Duration value to be used throughout the tests of
|
||||
// Duration.
|
||||
const defaultTestDur = time.Millisecond
|
||||
|
||||
// checkFields verifies m's fields. It expects the m to be unmarshalled from
|
||||
// one of the constant strings above.
|
||||
func (m *durationEncodingTester) checkFields(t *testing.T, d Duration) {
|
||||
t.Run("pointers_map", func(t *testing.T) {
|
||||
require.NotNil(t, m.PtrMap)
|
||||
|
||||
fromPtrMap, ok := m.PtrMap["dur"]
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, fromPtrMap)
|
||||
|
||||
assert.Equal(t, d, *fromPtrMap)
|
||||
})
|
||||
|
||||
t.Run("pointers_slice", func(t *testing.T) {
|
||||
require.Len(t, m.PtrSlice, 1)
|
||||
|
||||
fromPtrSlice := m.PtrSlice[0]
|
||||
require.NotNil(t, fromPtrSlice)
|
||||
|
||||
assert.Equal(t, d, *fromPtrSlice)
|
||||
})
|
||||
|
||||
t.Run("pointers_array", func(t *testing.T) {
|
||||
fromPtrArray := m.PtrArray[0]
|
||||
require.NotNil(t, fromPtrArray)
|
||||
|
||||
assert.Equal(t, d, *fromPtrArray)
|
||||
})
|
||||
|
||||
t.Run("pointer_value", func(t *testing.T) {
|
||||
require.NotNil(t, m.PtrValue)
|
||||
|
||||
assert.Equal(t, d, *m.PtrValue)
|
||||
})
|
||||
|
||||
t.Run("map", func(t *testing.T) {
|
||||
fromMap, ok := m.Map["dur"]
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, d, fromMap)
|
||||
})
|
||||
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
require.Len(t, m.Slice, 1)
|
||||
|
||||
assert.Equal(t, d, m.Slice[0])
|
||||
})
|
||||
|
||||
t.Run("array", func(t *testing.T) {
|
||||
assert.Equal(t, d, m.Array[0])
|
||||
})
|
||||
|
||||
t.Run("value", func(t *testing.T) {
|
||||
assert.Equal(t, d, m.Value)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDuration_MarshalText(t *testing.T) {
|
||||
d := Duration{defaultTestDur}
|
||||
dPtr := &d
|
||||
|
||||
v := durationEncodingTester{
|
||||
PtrMap: map[string]*Duration{"dur": dPtr},
|
||||
PtrSlice: []*Duration{dPtr},
|
||||
PtrValue: dPtr,
|
||||
PtrArray: [1]*Duration{dPtr},
|
||||
Map: map[string]Duration{"dur": d},
|
||||
Slice: []Duration{d},
|
||||
Value: d,
|
||||
Array: [1]Duration{d},
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
t.Run("json", func(t *testing.T) {
|
||||
t.Cleanup(b.Reset)
|
||||
err := json.NewEncoder(b).Encode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, jsonStr, b.String())
|
||||
})
|
||||
|
||||
t.Run("yaml", func(t *testing.T) {
|
||||
t.Cleanup(b.Reset)
|
||||
err := yaml.NewEncoder(b).Encode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.YAMLEq(t, yamlStr, b.String(), b.String())
|
||||
})
|
||||
|
||||
t.Run("direct", func(t *testing.T) {
|
||||
data, err := d.MarshalText()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, []byte(defaultTestDur.String()), data)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDuration_UnmarshalText(t *testing.T) {
|
||||
d := Duration{defaultTestDur}
|
||||
var v *durationEncodingTester
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
v = &durationEncodingTester{}
|
||||
|
||||
r := strings.NewReader(jsonStr)
|
||||
err := json.NewDecoder(r).Decode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
v.checkFields(t, d)
|
||||
})
|
||||
|
||||
t.Run("yaml", func(t *testing.T) {
|
||||
v = &durationEncodingTester{}
|
||||
|
||||
r := strings.NewReader(yamlStr)
|
||||
err := yaml.NewDecoder(r).Decode(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
v.checkFields(t, d)
|
||||
})
|
||||
|
||||
t.Run("direct", func(t *testing.T) {
|
||||
dd := &Duration{}
|
||||
|
||||
err := dd.UnmarshalText([]byte(d.String()))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, d, *dd)
|
||||
})
|
||||
|
||||
t.Run("bad_data", func(t *testing.T) {
|
||||
assert.Error(t, (&Duration{}).UnmarshalText([]byte(`abc`)))
|
||||
})
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
@@ -684,7 +685,7 @@ func upgradeSchema11to12(diskConf yobj) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
dns[field] = Duration{Duration: time.Duration(qlogIvl) * 24 * time.Hour}
|
||||
dns[field] = aghtime.Duration{Duration: time.Duration(qlogIvl) * 24 * time.Hour}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtime"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -425,7 +426,7 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
name string
|
||||
}{{
|
||||
ivl: 1,
|
||||
want: Duration{Duration: 24 * time.Hour},
|
||||
want: aghtime.Duration{Duration: 24 * time.Hour},
|
||||
wantErr: "",
|
||||
name: "success",
|
||||
}, {
|
||||
@@ -462,8 +463,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
newDNSConf, ok = dnsVal.(yobj)
|
||||
require.True(t, ok)
|
||||
|
||||
var newIvl Duration
|
||||
newIvl, ok = newDNSConf["querylog_interval"].(Duration)
|
||||
var newIvl aghtime.Duration
|
||||
newIvl, ok = newDNSConf["querylog_interval"].(aghtime.Duration)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, tc.want, newIvl)
|
||||
@@ -504,8 +505,8 @@ func TestUpgradeSchema11to12(t *testing.T) {
|
||||
ivl, ok = dnsVal["querylog_interval"]
|
||||
require.True(t, ok)
|
||||
|
||||
var ivlVal Duration
|
||||
ivlVal, ok = ivl.(Duration)
|
||||
var ivlVal aghtime.Duration
|
||||
ivlVal, ok = ivl.(aghtime.Duration)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, 90*24*time.Hour, ivlVal.Duration)
|
||||
|
||||
Reference in New Issue
Block a user