Pull request 2378: AGDNS-2750-find-client

Merge in DNS/adguard-home from AGDNS-2750-find-client to master

Squashed commit of the following:

commit 98f1a8ca4622b6f502a5092273b9724203fe0bd8
Merge: 9270222d8 4ccc2a213
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 23 17:53:20 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 9270222d8e9e03038e9434b54496cbb6164463cd
Merge: 6468ceec8 c7c62ad3b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:58 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6468ceec82d30084771a53ff6720a8c11c68bf2f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:52 2025 +0300

    home: imp docs

commit 3fd4735a0d6db4fdf2d46f3da9794a687fdcaa8b
Merge: 1311a5869 a8fdf1c55
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:43:36 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 1311a58695de00f20c9704378ee6e964a44d1c59
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:42:41 2025 +0300

    home: imp code

commit b1f2c4c883c9476c5135140abac31f8ae6609b4f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 16 16:47:59 2025 +0300

    home: imp code

commit d0a5abd66587c1ad602c2ccf6c8a45a3dfe39a5c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 15 14:58:31 2025 +0300

    client: imp naming

commit 5accdca325551237f003f1c416891b488fe5290b
Merge: 6a00232f7 4d258972d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:40:40 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6a00232f76a0fe5ce781aa01637b6e04ace7250d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:30:32 2025 +0300

    home: imp code

commit 8633886457c6aab75f5676494b1f49d9811e9ab9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 11 15:29:25 2025 +0300

    all: imp code

commit d6f16879e7b054a5ffac59131d2a6eff1da659c0
Merge: 58236fdec 6d282ae71
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:35:23 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 58236fdec5b64e83a44680ff8a89badc18ec81f1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:23:01 2025 +0300

    all: upd ci

commit 3c4d946d7970987677d4ac984394e18987a29f9a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:16:03 2025 +0300

    all: upd go

commit cc1c97734506a9ffbe70fd3c676284e58a21ba46
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 20:58:56 2025 +0300

    all: imp code

commit 8f061c933152481a4c80eef2af575efd4919d82b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 9 16:49:11 2025 +0300

    all: imp docs

commit 8d19355f1c519211a56cec3f23d527922d4f2ee0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 21:35:06 2025 +0300

    all: imp code

commit f1e853f57e5d54d13bedcdab4f8e21e112f3a356
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 2 14:57:40 2025 +0300

    all: imp code

commit 6a6ac7f899f29ddc90a583c80562233e646ba1d6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:51:56 2025 +0300

    client: imp tests

commit 52040ee7393d0483c682f2f37d7b70f12f9cf621
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:28:18 2025 +0300

    all: imp code

commit 1e09208dbd2d35c3f6b2ade169324e23d1a643a5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 26 15:33:02 2025 +0300

    all: imp code

... and 2 more commits
This commit is contained in:
Stanislav Chzhen
2025-04-23 18:10:52 +03:00
parent 4ccc2a2138
commit 61a1403e4e
16 changed files with 705 additions and 338 deletions

View File

@@ -15,6 +15,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -350,15 +351,15 @@ func TestClientsDHCP(t *testing.T) {
cliName1 = "one.dhcp"
cliIP2 = netip.MustParseAddr("2.2.2.2")
cliMAC2 = mustParseMAC("22:22:22:22:22:22")
cliMAC2 = errors.Must(net.ParseMAC("22:22:22:22:22:22"))
cliName2 = "two.dhcp"
cliIP3 = netip.MustParseAddr("3.3.3.3")
cliMAC3 = mustParseMAC("33:33:33:33:33:33")
cliMAC3 = errors.Must(net.ParseMAC("33:33:33:33:33:33"))
cliName3 = "three.dhcp"
prsCliIP = netip.MustParseAddr("4.3.2.1")
prsCliMAC = mustParseMAC("AA:AA:AA:AA:AA:AA")
prsCliMAC = errors.Must(net.ParseMAC("AA:AA:AA:AA:AA:AA"))
prsCliName = "persistent.dhcp"
otherARPCliName = "other.arp"
@@ -519,7 +520,11 @@ func TestClientsDHCP(t *testing.T) {
})
require.NoError(t, err)
prsCli, ok := storage.Find(prsCliIP.String())
params := &client.FindParams{}
err = params.Set(prsCliIP.String())
require.NoError(t, err)
prsCli, ok := storage.Find(params)
require.True(t, ok)
assert.Equal(t, prsCliName, prsCli.Name)
@@ -663,17 +668,6 @@ func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
return s
}
// mustParseMAC is wrapper around [net.ParseMAC] that panics if there is an
// error.
func mustParseMAC(s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return mac
}
func TestStorage_Add(t *testing.T) {
const (
existingName = "existing_name"
@@ -693,7 +687,7 @@ func TestStorage_Add(t *testing.T) {
Name: existingName,
IPs: []netip.Addr{existingIP},
Subnets: []netip.Prefix{existingSubnet},
ClientIDs: []string{existingClientID},
ClientIDs: []client.ClientID{existingClientID},
UID: existingClientUID,
}
@@ -761,7 +755,7 @@ func TestStorage_Add(t *testing.T) {
name: "duplicate_client_id",
cli: &client.Persistent{
Name: "duplicate_client_id",
ClientIDs: []string{existingClientID},
ClientIDs: []client.ClientID{existingClientID},
UID: client.MustNewUID(),
},
wantErrMsg: `adding client: another client "existing_name" ` +
@@ -898,12 +892,12 @@ func TestStorage_Find(t *testing.T) {
clientWithMAC = &client.Persistent{
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
MACs: []net.HardwareAddr{errors.Must(net.ParseMAC(cliMAC))},
}
clientWithID = &client.Persistent{
Name: "client_with_id",
ClientIDs: []string{cliID},
ClientIDs: []client.ClientID{cliID},
}
clientLinkLocal = &client.Persistent{
@@ -950,7 +944,11 @@ func TestStorage_Find(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := s.Find(id)
params := &client.FindParams{}
err := params.Set(id)
require.NoError(t, err)
c, ok := s.Find(params)
require.True(t, ok)
assert.Equal(t, tc.want, c)
@@ -959,7 +957,11 @@ func TestStorage_Find(t *testing.T) {
}
t.Run("not_found", func(t *testing.T) {
_, ok := s.Find(cliIPNone)
params := &client.FindParams{}
err := params.Set(cliIPNone)
require.NoError(t, err)
_, ok := s.Find(params)
assert.False(t, ok)
})
}
@@ -1025,127 +1027,6 @@ func TestStorage_FindLoose(t *testing.T) {
}
}
func TestStorage_FindByName(t *testing.T) {
const (
cliIP1 = "1.1.1.1"
cliIP2 = "2.2.2.2"
)
const (
clientExistingName = "client_existing"
clientAnotherExistingName = "client_another_existing"
nonExistingClientName = "client_non_existing"
)
var (
clientExisting = &client.Persistent{
Name: clientExistingName,
IPs: []netip.Addr{netip.MustParseAddr(cliIP1)},
}
clientAnotherExisting = &client.Persistent{
Name: clientAnotherExistingName,
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
}
)
clients := []*client.Persistent{
clientExisting,
clientAnotherExisting,
}
s := newStorage(t, clients)
testCases := []struct {
want *client.Persistent
name string
clientName string
}{{
name: "existing",
clientName: clientExistingName,
want: clientExisting,
}, {
name: "another_existing",
clientName: clientAnotherExistingName,
want: clientAnotherExisting,
}, {
name: "non_existing",
clientName: nonExistingClientName,
want: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c, ok := s.FindByName(tc.clientName)
if tc.want == nil {
assert.False(t, ok)
return
}
assert.True(t, ok)
assert.Equal(t, tc.want, c)
})
}
}
func TestStorage_FindByMAC(t *testing.T) {
var (
cliMAC = mustParseMAC("11:11:11:11:11:11")
cliAnotherMAC = mustParseMAC("22:22:22:22:22:22")
nonExistingClientMAC = mustParseMAC("33:33:33:33:33:33")
)
var (
clientExisting = &client.Persistent{
Name: "client",
MACs: []net.HardwareAddr{cliMAC},
}
clientAnotherExisting = &client.Persistent{
Name: "another_client",
MACs: []net.HardwareAddr{cliAnotherMAC},
}
)
clients := []*client.Persistent{
clientExisting,
clientAnotherExisting,
}
s := newStorage(t, clients)
testCases := []struct {
want *client.Persistent
name string
clientMAC net.HardwareAddr
}{{
name: "existing",
clientMAC: cliMAC,
want: clientExisting,
}, {
name: "another_existing",
clientMAC: cliAnotherMAC,
want: clientAnotherExisting,
}, {
name: "non_existing",
clientMAC: nonExistingClientMAC,
want: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c, ok := s.FindByMAC(tc.clientMAC)
if tc.want == nil {
assert.False(t, ok)
return
}
assert.True(t, ok)
assert.Equal(t, tc.want, c)
})
}
}
func TestStorage_Update(t *testing.T) {
const (
clientName = "client_name"
@@ -1162,7 +1043,7 @@ func TestStorage_Update(t *testing.T) {
Name: obstructingName,
IPs: []netip.Addr{obstructingIP},
Subnets: []netip.Prefix{obstructingSubnet},
ClientIDs: []string{obstructingClientID},
ClientIDs: []client.ClientID{obstructingClientID},
}
clientToUpdate := &client.Persistent{
@@ -1211,7 +1092,7 @@ func TestStorage_Update(t *testing.T) {
name: "duplicate_client_id",
cli: &client.Persistent{
Name: "duplicate_client_id",
ClientIDs: []string{obstructingClientID},
ClientIDs: []client.ClientID{obstructingClientID},
UID: client.MustNewUID(),
},
wantErrMsg: `updating client: another client "obstructing_name" ` +
@@ -1238,19 +1119,19 @@ func TestStorage_Update(t *testing.T) {
func TestStorage_RangeByName(t *testing.T) {
sortedClients := []*client.Persistent{{
Name: "clientA",
ClientIDs: []string{"A"},
ClientIDs: []client.ClientID{"A"},
}, {
Name: "clientB",
ClientIDs: []string{"B"},
ClientIDs: []client.ClientID{"B"},
}, {
Name: "clientC",
ClientIDs: []string{"C"},
ClientIDs: []client.ClientID{"C"},
}, {
Name: "clientD",
ClientIDs: []string{"D"},
ClientIDs: []client.ClientID{"D"},
}, {
Name: "clientE",
ClientIDs: []string{"E"},
ClientIDs: []client.ClientID{"E"},
}}
testCases := []struct {
@@ -1306,7 +1187,7 @@ func TestStorage_CustomUpstreamConfig(t *testing.T) {
existingClient := &client.Persistent{
Name: existingName,
IPs: []netip.Addr{existingIP},
ClientIDs: []string{existingClientID},
ClientIDs: []client.ClientID{existingClientID},
UID: existingClientUID,
Upstreams: []string{"192.0.2.0"},
}
@@ -1381,3 +1262,182 @@ func TestStorage_CustomUpstreamConfig(t *testing.T) {
assert.NotEqual(t, conf, updConf)
})
}
func BenchmarkFindParams_Set(b *testing.B) {
const (
testIPStr = "192.0.2.1"
testCIDRStr = "192.0.2.0/24"
testMACStr = "02:00:00:00:00:00"
testClientID = "clientid"
)
benchCases := []struct {
wantErr error
params *client.FindParams
name string
id string
}{{
wantErr: nil,
params: &client.FindParams{
ClientID: testClientID,
},
name: "client_id",
id: testClientID,
}, {
wantErr: nil,
params: &client.FindParams{
RemoteIP: netip.MustParseAddr(testIPStr),
},
name: "ip_address",
id: testIPStr,
}, {
wantErr: nil,
params: &client.FindParams{
Subnet: netip.MustParsePrefix(testCIDRStr),
},
name: "subnet",
id: testCIDRStr,
}, {
wantErr: nil,
params: &client.FindParams{
MAC: errors.Must(net.ParseMAC(testMACStr)),
},
name: "mac_address",
id: testMACStr,
}, {
wantErr: client.ErrBadIdentifier,
params: &client.FindParams{},
name: "bad_id",
id: "!@#$%^&*()_+",
}}
for _, bc := range benchCases {
b.Run(bc.name, func(b *testing.B) {
params := &client.FindParams{}
var err error
b.ReportAllocs()
for b.Loop() {
err = params.Set(bc.id)
}
assert.ErrorIs(b, err, bc.wantErr)
assert.Equal(b, bc.params, params)
})
}
// Most recent results:
//
// goos: linux
// goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardHome/internal/client
// cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
// BenchmarkFindParams_Set/client_id-8 49463488 24.27 ns/op 0 B/op 0 allocs/op
// BenchmarkFindParams_Set/ip_address-8 18740977 62.22 ns/op 0 B/op 0 allocs/op
// BenchmarkFindParams_Set/subnet-8 10848192 110.0 ns/op 0 B/op 0 allocs/op
// BenchmarkFindParams_Set/mac_address-8 8148494 133.2 ns/op 8 B/op 1 allocs/op
// BenchmarkFindParams_Set/bad_id-8 73894278 16.29 ns/op 0 B/op 0 allocs/op
}
func BenchmarkStorage_Find(b *testing.B) {
const (
cliID = "cid"
cliMAC = "02:00:00:00:00:00"
)
const (
cliNameWithID = "client_with_id"
cliNameWithIP = "client_with_ip"
cliNameWithCIDR = "client_with_cidr"
cliNameWithMAC = "client_with_mac"
)
var (
cliIP = netip.MustParseAddr("192.0.2.1")
cliCIDR = netip.MustParsePrefix("192.0.2.0/24")
)
var (
clientWithID = &client.Persistent{
Name: cliNameWithID,
ClientIDs: []client.ClientID{cliID},
}
clientWithIP = &client.Persistent{
Name: cliNameWithIP,
IPs: []netip.Addr{cliIP},
}
clientWithCIDR = &client.Persistent{
Name: cliNameWithCIDR,
Subnets: []netip.Prefix{cliCIDR},
}
clientWithMAC = &client.Persistent{
Name: cliNameWithMAC,
MACs: []net.HardwareAddr{errors.Must(net.ParseMAC(cliMAC))},
}
)
clients := []*client.Persistent{
clientWithID,
clientWithIP,
clientWithCIDR,
clientWithMAC,
}
s := newStorage(b, clients)
benchCases := []struct {
params *client.FindParams
name string
wantName string
}{{
params: &client.FindParams{
ClientID: cliID,
},
name: "client_id",
wantName: cliNameWithID,
}, {
params: &client.FindParams{
RemoteIP: cliIP,
},
name: "ip_address",
wantName: cliNameWithIP,
}, {
params: &client.FindParams{
Subnet: cliCIDR,
},
name: "subnet",
wantName: cliNameWithCIDR,
}, {
params: &client.FindParams{
MAC: errors.Must(net.ParseMAC(cliMAC)),
},
name: "mac_address",
wantName: cliNameWithMAC,
}}
for _, bc := range benchCases {
b.Run(bc.name, func(b *testing.B) {
var p *client.Persistent
var ok bool
b.ReportAllocs()
for b.Loop() {
p, ok = s.Find(bc.params)
}
assert.True(b, ok)
assert.NotNil(b, p)
assert.Equal(b, bc.wantName, p.Name)
})
}
// Most recent results:
//
// goos: linux
// goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardHome/internal/client
// cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
// BenchmarkStorage_Find/client_id-8 7070107 154.4 ns/op 240 B/op 2 allocs/op
// BenchmarkStorage_Find/ip_address-8 6831823 168.6 ns/op 248 B/op 2 allocs/op
// BenchmarkStorage_Find/subnet-8 7209050 167.5 ns/op 256 B/op 2 allocs/op
// BenchmarkStorage_Find/mac_address-8 5776131 199.7 ns/op 256 B/op 3 allocs/op
}