mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-16 01:58:16 +02:00
[management] optimize test execution (#3204)
This commit is contained in:
150
.github/workflows/golang-test-linux.yml
vendored
150
.github/workflows/golang-test-linux.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Test Code Linux
|
name: Linux
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -12,6 +12,7 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-cache:
|
build-cache:
|
||||||
|
name: "Build Cache"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
outputs:
|
||||||
management: ${{ steps.filter.outputs.management }}
|
management: ${{ steps.filter.outputs.management }}
|
||||||
@ -47,7 +48,6 @@ jobs:
|
|||||||
key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
||||||
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
@ -98,6 +98,7 @@ jobs:
|
|||||||
run: CGO_ENABLED=1 GOARCH=386 go build -o relay-386 .
|
run: CGO_ENABLED=1 GOARCH=386 go build -o relay-386 .
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
name: "Client / Unit"
|
||||||
needs: [build-cache]
|
needs: [build-cache]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -143,9 +144,116 @@ jobs:
|
|||||||
run: git --no-pager diff --exit-code
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} CI=true go test -tags devcert -exec 'sudo' -timeout 10m -p 1 $(go list ./... | grep -v /management)
|
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} CI=true go test -tags devcert -exec 'sudo' -timeout 10m -p 1 $(go list ./... | grep -v -e /management -e /signal -e /relay)
|
||||||
|
|
||||||
|
test_relay:
|
||||||
|
name: "Relay / Unit"
|
||||||
|
needs: [build-cache]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch: [ '386','amd64' ]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23.x"
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get Go environment
|
||||||
|
run: |
|
||||||
|
echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
|
||||||
|
echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Cache Go modules
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
${{ env.cache }}
|
||||||
|
${{ env.modcache }}
|
||||||
|
key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gotest-cache-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-multilib libpcap-dev
|
||||||
|
|
||||||
|
- name: Install 32-bit libpcap
|
||||||
|
if: matrix.arch == '386'
|
||||||
|
run: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt-get install -y libpcap0.8-dev:i386
|
||||||
|
|
||||||
|
- name: Install modules
|
||||||
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
go test \
|
||||||
|
-exec 'sudo' \
|
||||||
|
-timeout 10m ./signal/...
|
||||||
|
|
||||||
|
test_signal:
|
||||||
|
name: "Signal / Unit"
|
||||||
|
needs: [build-cache]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch: [ '386','amd64' ]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23.x"
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get Go environment
|
||||||
|
run: |
|
||||||
|
echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
|
||||||
|
echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Cache Go modules
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
${{ env.cache }}
|
||||||
|
${{ env.modcache }}
|
||||||
|
key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gotest-cache-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-multilib libpcap-dev
|
||||||
|
|
||||||
|
- name: Install 32-bit libpcap
|
||||||
|
if: matrix.arch == '386'
|
||||||
|
run: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt-get install -y libpcap0.8-dev:i386
|
||||||
|
|
||||||
|
- name: Install modules
|
||||||
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
go test \
|
||||||
|
-exec 'sudo' \
|
||||||
|
-timeout 10m ./signal/...
|
||||||
|
|
||||||
test_management:
|
test_management:
|
||||||
|
name: "Management / Unit"
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -203,9 +311,15 @@ jobs:
|
|||||||
run: docker pull mlsmaycon/warmed-mysql:8
|
run: docker pull mlsmaycon/warmed-mysql:8
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -tags=devcert -p 1 -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 10m $(go list ./... | grep /management)
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
NETBIRD_STORE_ENGINE=${{ matrix.store }} \
|
||||||
|
go test -tags=devcert \
|
||||||
|
-exec "sudo --preserve-env=CI,NETBIRD_STORE_ENGINE" \
|
||||||
|
-timeout 10m ./management/...
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
|
name: "Management / Benchmark"
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
||||||
strategy:
|
strategy:
|
||||||
@ -264,9 +378,15 @@ jobs:
|
|||||||
run: docker pull mlsmaycon/warmed-mysql:8
|
run: docker pull mlsmaycon/warmed-mysql:8
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -tags devcert -run=^$ -bench=. -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 20m ./...
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true \
|
||||||
|
go test -tags devcert -run=^$ -bench=. \
|
||||||
|
-exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
|
||||||
|
-timeout 20m ./...
|
||||||
|
|
||||||
api_benchmark:
|
api_benchmark:
|
||||||
|
name: "Management / Benchmark (API)"
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
||||||
strategy:
|
strategy:
|
||||||
@ -323,11 +443,19 @@ jobs:
|
|||||||
- name: download mysql image
|
- name: download mysql image
|
||||||
if: matrix.store == 'mysql'
|
if: matrix.store == 'mysql'
|
||||||
run: docker pull mlsmaycon/warmed-mysql:8
|
run: docker pull mlsmaycon/warmed-mysql:8
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -run=^$ -tags=benchmark -bench=. -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 30m $(go list -tags=benchmark ./... | grep /management)
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true \
|
||||||
|
go test -tags=benchmark \
|
||||||
|
-run=^$ \
|
||||||
|
-bench=. \
|
||||||
|
-exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
|
||||||
|
-timeout 20m ./management/...
|
||||||
|
|
||||||
api_integration_test:
|
api_integration_test:
|
||||||
|
name: "Management / Integration"
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
|
||||||
strategy:
|
strategy:
|
||||||
@ -375,9 +503,15 @@ jobs:
|
|||||||
run: git --no-pager diff --exit-code
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -tags=integration -p 1 -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 30m $(go list -tags=integration ./... | grep /management)
|
run: |
|
||||||
|
CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
|
||||||
|
NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true \
|
||||||
|
go test -tags=integration \
|
||||||
|
-exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
|
||||||
|
-timeout 10m ./management/...
|
||||||
|
|
||||||
test_client_on_docker:
|
test_client_on_docker:
|
||||||
|
name: "Client (Docker) / Unit"
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
@ -103,7 +103,7 @@ linters:
|
|||||||
- predeclared # predeclared finds code that shadows one of Go's predeclared identifiers
|
- predeclared # predeclared finds code that shadows one of Go's predeclared identifiers
|
||||||
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
|
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
|
||||||
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
||||||
- thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers.
|
# - thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers.
|
||||||
- wastedassign # wastedassign finds wasted assignment statements
|
- wastedassign # wastedassign finds wasted assignment statements
|
||||||
issues:
|
issues:
|
||||||
# Maximum count of issues with the same text.
|
# Maximum count of issues with the same text.
|
||||||
|
@ -258,8 +258,11 @@ func TestClient_Sync(t *testing.T) {
|
|||||||
|
|
||||||
ch := make(chan *mgmtProto.SyncResponse, 1)
|
ch := make(chan *mgmtProto.SyncResponse, 1)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err = client.Sync(context.Background(), info, func(msg *mgmtProto.SyncResponse) error {
|
err = client.Sync(ctx, info, func(msg *mgmtProto.SyncResponse) error {
|
||||||
ch <- msg
|
ch <- msg
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -42,7 +42,7 @@ func TestGetDNSSettings(t *testing.T) {
|
|||||||
|
|
||||||
account, err := initTestDNSAccount(t, am)
|
account, err := initTestDNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsSettings, err := am.GetDNSSettings(context.Background(), account.Id, dnsAdminUserID)
|
dnsSettings, err := am.GetDNSSettings(context.Background(), account.Id, dnsAdminUserID)
|
||||||
@ -124,12 +124,12 @@ func TestSaveDNSSettings(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
am, err := createDNSManager(t)
|
am, err := createDNSManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to create account manager")
|
t.Fatalf("failed to create account manager")
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := initTestDNSAccount(t, am)
|
account, err := initTestDNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = am.SaveDNSSettings(context.Background(), account.Id, testCase.userID, testCase.inputSettings)
|
err = am.SaveDNSSettings(context.Background(), account.Id, testCase.userID, testCase.inputSettings)
|
||||||
@ -156,22 +156,22 @@ func TestGetNetworkMap_DNSConfigSync(t *testing.T) {
|
|||||||
|
|
||||||
am, err := createDNSManager(t)
|
am, err := createDNSManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to create account manager")
|
t.Fatalf("failed to create account manager: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := initTestDNSAccount(t, am)
|
account, err := initTestDNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peer1, err := account.FindPeerByPubKey(dnsPeer1Key)
|
peer1, err := account.FindPeerByPubKey(dnsPeer1Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peer2, err := account.FindPeerByPubKey(dnsPeer2Key)
|
peer2, err := account.FindPeerByPubKey(dnsPeer2Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newAccountDNSConfig, err := am.GetNetworkMap(context.Background(), peer1.ID)
|
newAccountDNSConfig, err := am.GetNetworkMap(context.Background(), peer1.ID)
|
||||||
|
@ -29,7 +29,7 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
|||||||
|
|
||||||
_, account, err := initTestGroupAccount(am)
|
_, account, err := initTestGroupAccount(am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
for _, group := range account.Groups {
|
for _, group := range account.Groups {
|
||||||
group.Issued = types.GroupIssuedIntegration
|
group.Issued = types.GroupIssuedIntegration
|
||||||
@ -59,12 +59,12 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
|||||||
func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
|
func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
|
||||||
am, err := createManager(t)
|
am, err := createManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to create account manager")
|
t.Fatalf("failed to create account manager: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, account, err := initTestGroupAccount(am)
|
_, account, err := initTestGroupAccount(am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package server_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestManagement(t *testing.T) {
|
|
||||||
RegisterFailHandler(Fail)
|
|
||||||
RunSpecs(t, "Management Service Suite")
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -379,12 +379,12 @@ func TestCreateNameServerGroup(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
am, err := createNSManager(t)
|
am, err := createNSManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to create account manager")
|
t.Fatalf("failed to create account manager: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := initTestNSAccount(t, am)
|
account, err := initTestNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
outNSGroup, err := am.CreateNameServerGroup(
|
outNSGroup, err := am.CreateNameServerGroup(
|
||||||
@ -607,12 +607,12 @@ func TestSaveNameServerGroup(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
am, err := createNSManager(t)
|
am, err := createNSManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to create account manager")
|
t.Fatalf("failed to create account manager: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := initTestNSAccount(t, am)
|
account, err := initTestNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account.NameServerGroups[testCase.existingNSGroup.ID] = testCase.existingNSGroup
|
account.NameServerGroups[testCase.existingNSGroup.ID] = testCase.existingNSGroup
|
||||||
@ -706,7 +706,7 @@ func TestDeleteNameServerGroup(t *testing.T) {
|
|||||||
|
|
||||||
account, err := initTestNSAccount(t, am)
|
account, err := initTestNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account.NameServerGroups[testingNSGroup.ID] = testingNSGroup
|
account.NameServerGroups[testingNSGroup.ID] = testingNSGroup
|
||||||
@ -741,7 +741,7 @@ func TestGetNameServerGroup(t *testing.T) {
|
|||||||
|
|
||||||
account, err := initTestNSAccount(t, am)
|
account, err := initTestNSAccount(t, am)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to init testing account")
|
t.Fatalf("failed to init testing account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
foundGroup, err := am.GetNameServerGroup(context.Background(), account.Id, testUserID, existingNSGroupID)
|
foundGroup, err := am.GetNameServerGroup(context.Background(), account.Id, testUserID, existingNSGroupID)
|
||||||
@ -761,6 +761,7 @@ func TestGetNameServerGroup(t *testing.T) {
|
|||||||
|
|
||||||
func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
store, err := createNSStore(t)
|
store, err := createNSStore(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -13,12 +13,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/domain"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/domain"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
@ -37,40 +37,44 @@ import (
|
|||||||
nbroute "github.com/netbirdio/netbird/route"
|
nbroute "github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSqlite_NewStore(t *testing.T) {
|
func runTestForAllEngines(t *testing.T, testDataFile string, f func(t *testing.T, store Store)) {
|
||||||
|
t.Helper()
|
||||||
|
for _, engine := range supportedEngines {
|
||||||
|
if os.Getenv("NETBIRD_STORE_ENGINE") != "" && os.Getenv("NETBIRD_STORE_ENGINE") != string(engine) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Setenv("NETBIRD_STORE_ENGINE", string(engine))
|
||||||
|
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), testDataFile, t.TempDir())
|
||||||
|
t.Cleanup(cleanUp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Run(string(engine), func(t *testing.T) {
|
||||||
|
f(t, store)
|
||||||
|
})
|
||||||
|
os.Unsetenv("NETBIRD_STORE_ENGINE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_NewStore(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
runTestForAllEngines(t, "", func(t *testing.T, store Store) {
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
if store == nil {
|
||||||
t.Cleanup(cleanUp)
|
t.Errorf("expected to create a new Store")
|
||||||
assert.NoError(t, err)
|
}
|
||||||
|
if len(store.GetAllAccounts(context.Background())) != 0 {
|
||||||
if len(store.GetAllAccounts(context.Background())) != 0 {
|
t.Errorf("expected to create a new empty Accounts map when creating a new FileStore")
|
||||||
t.Errorf("expected to create a new empty Accounts map when creating a new FileStore")
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_SaveAccount_Large(t *testing.T) {
|
func Test_SaveAccount_Large(t *testing.T) {
|
||||||
if (os.Getenv("CI") == "true" && runtime.GOOS == "darwin") || runtime.GOOS == "windows" {
|
if (os.Getenv("CI") == "true" && runtime.GOOS == "darwin") || runtime.GOOS == "windows" {
|
||||||
t.Skip("skip CI tests on darwin and windows")
|
t.Skip("skip CI tests on darwin and windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("SQLite", func(t *testing.T) {
|
runTestForAllEngines(t, "", func(t *testing.T, store Store) {
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
|
||||||
t.Cleanup(cleanUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
runLargeTest(t, store)
|
|
||||||
})
|
|
||||||
|
|
||||||
// create store outside to have a better time counter for the test
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
|
||||||
t.Cleanup(cleanUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
t.Run("PostgreSQL", func(t *testing.T) {
|
|
||||||
runLargeTest(t, store)
|
runLargeTest(t, store)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -215,77 +219,74 @@ func randomIPv4() net.IP {
|
|||||||
return net.IP(b)
|
return net.IP(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_SaveAccount(t *testing.T) {
|
func Test_SaveAccount(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
runTestForAllEngines(t, "", func(t *testing.T, store Store) {
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "", t.TempDir())
|
account := newAccountWithId(context.Background(), "account_id", "testuser", "")
|
||||||
t.Cleanup(cleanUp)
|
setupKey, _ := types.GenerateDefaultSetupKey()
|
||||||
assert.NoError(t, err)
|
account.SetupKeys[setupKey.Key] = setupKey
|
||||||
|
account.Peers["testpeer"] = &nbpeer.Peer{
|
||||||
|
Key: "peerkey",
|
||||||
|
IP: net.IP{127, 0, 0, 1},
|
||||||
|
Meta: nbpeer.PeerSystemMeta{},
|
||||||
|
Name: "peer name",
|
||||||
|
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||||
|
}
|
||||||
|
|
||||||
account := newAccountWithId(context.Background(), "account_id", "testuser", "")
|
err := store.SaveAccount(context.Background(), account)
|
||||||
setupKey, _ := types.GenerateDefaultSetupKey()
|
require.NoError(t, err)
|
||||||
account.SetupKeys[setupKey.Key] = setupKey
|
|
||||||
account.Peers["testpeer"] = &nbpeer.Peer{
|
|
||||||
Key: "peerkey",
|
|
||||||
IP: net.IP{127, 0, 0, 1},
|
|
||||||
Meta: nbpeer.PeerSystemMeta{},
|
|
||||||
Name: "peer name",
|
|
||||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
|
||||||
}
|
|
||||||
|
|
||||||
err = store.SaveAccount(context.Background(), account)
|
account2 := newAccountWithId(context.Background(), "account_id2", "testuser2", "")
|
||||||
require.NoError(t, err)
|
setupKey, _ = types.GenerateDefaultSetupKey()
|
||||||
|
account2.SetupKeys[setupKey.Key] = setupKey
|
||||||
|
account2.Peers["testpeer2"] = &nbpeer.Peer{
|
||||||
|
Key: "peerkey2",
|
||||||
|
IP: net.IP{127, 0, 0, 2},
|
||||||
|
Meta: nbpeer.PeerSystemMeta{},
|
||||||
|
Name: "peer name 2",
|
||||||
|
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||||
|
}
|
||||||
|
|
||||||
account2 := newAccountWithId(context.Background(), "account_id2", "testuser2", "")
|
err = store.SaveAccount(context.Background(), account2)
|
||||||
setupKey, _ = types.GenerateDefaultSetupKey()
|
require.NoError(t, err)
|
||||||
account2.SetupKeys[setupKey.Key] = setupKey
|
|
||||||
account2.Peers["testpeer2"] = &nbpeer.Peer{
|
|
||||||
Key: "peerkey2",
|
|
||||||
IP: net.IP{127, 0, 0, 2},
|
|
||||||
Meta: nbpeer.PeerSystemMeta{},
|
|
||||||
Name: "peer name 2",
|
|
||||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
|
||||||
}
|
|
||||||
|
|
||||||
err = store.SaveAccount(context.Background(), account2)
|
if len(store.GetAllAccounts(context.Background())) != 2 {
|
||||||
require.NoError(t, err)
|
t.Errorf("expecting 2 Accounts to be stored after SaveAccount()")
|
||||||
|
}
|
||||||
|
|
||||||
if len(store.GetAllAccounts(context.Background())) != 2 {
|
a, err := store.GetAccount(context.Background(), account.Id)
|
||||||
t.Errorf("expecting 2 Accounts to be stored after SaveAccount()")
|
if a == nil {
|
||||||
}
|
t.Errorf("expecting Account to be stored after SaveAccount(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
a, err := store.GetAccount(context.Background(), account.Id)
|
if a != nil && len(a.Policies) != 1 {
|
||||||
if a == nil {
|
t.Errorf("expecting Account to have one policy stored after SaveAccount(), got %d", len(a.Policies))
|
||||||
t.Errorf("expecting Account to be stored after SaveAccount(): %v", err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if a != nil && len(a.Policies) != 1 {
|
if a != nil && len(a.Policies[0].Rules) != 1 {
|
||||||
t.Errorf("expecting Account to have one policy stored after SaveAccount(), got %d", len(a.Policies))
|
t.Errorf("expecting Account to have one policy rule stored after SaveAccount(), got %d", len(a.Policies[0].Rules))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if a != nil && len(a.Policies[0].Rules) != 1 {
|
if a, err := store.GetAccountByPeerPubKey(context.Background(), "peerkey"); a == nil {
|
||||||
t.Errorf("expecting Account to have one policy rule stored after SaveAccount(), got %d", len(a.Policies[0].Rules))
|
t.Errorf("expecting PeerKeyID2AccountID index updated after SaveAccount(): %v", err)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if a, err := store.GetAccountByPeerPubKey(context.Background(), "peerkey"); a == nil {
|
if a, err := store.GetAccountByUser(context.Background(), "testuser"); a == nil {
|
||||||
t.Errorf("expecting PeerKeyID2AccountID index updated after SaveAccount(): %v", err)
|
t.Errorf("expecting UserID2AccountID index updated after SaveAccount(): %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, err := store.GetAccountByUser(context.Background(), "testuser"); a == nil {
|
if a, err := store.GetAccountByPeerID(context.Background(), "testpeer"); a == nil {
|
||||||
t.Errorf("expecting UserID2AccountID index updated after SaveAccount(): %v", err)
|
t.Errorf("expecting PeerID2AccountID index updated after SaveAccount(): %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, err := store.GetAccountByPeerID(context.Background(), "testpeer"); a == nil {
|
if a, err := store.GetAccountBySetupKey(context.Background(), setupKey.Key); a == nil {
|
||||||
t.Errorf("expecting PeerID2AccountID index updated after SaveAccount(): %v", err)
|
t.Errorf("expecting SetupKeyID2AccountID index updated after SaveAccount(): %v", err)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if a, err := store.GetAccountBySetupKey(context.Background(), setupKey.Key); a == nil {
|
|
||||||
t.Errorf("expecting SetupKeyID2AccountID index updated after SaveAccount(): %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_DeleteAccount(t *testing.T) {
|
func TestSqlite_DeleteAccount(t *testing.T) {
|
||||||
@ -402,27 +403,24 @@ func TestSqlite_DeleteAccount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_GetAccount(t *testing.T) {
|
func Test_GetAccount(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
runTestForAllEngines(t, "../testdata/store.sql", func(t *testing.T, store Store) {
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir())
|
id := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||||
t.Cleanup(cleanUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
id := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
account, err := store.GetAccount(context.Background(), id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, id, account.Id, "account id should match")
|
||||||
|
|
||||||
account, err := store.GetAccount(context.Background(), id)
|
_, err = store.GetAccount(context.Background(), "non-existing-account")
|
||||||
require.NoError(t, err)
|
assert.Error(t, err)
|
||||||
require.Equal(t, id, account.Id, "account id should match")
|
parsedErr, ok := status.FromError(err)
|
||||||
|
require.True(t, ok)
|
||||||
_, err = store.GetAccount(context.Background(), "non-existing-account")
|
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
||||||
assert.Error(t, err)
|
})
|
||||||
parsedErr, ok := status.FromError(err)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlStore_SavePeer(t *testing.T) {
|
func TestSqlStore_SavePeer(t *testing.T) {
|
||||||
@ -580,51 +578,45 @@ func TestSqlStore_SavePeerLocation(t *testing.T) {
|
|||||||
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_TestGetAccountByPrivateDomain(t *testing.T) {
|
func Test_TestGetAccountByPrivateDomain(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
runTestForAllEngines(t, "../testdata/store.sql", func(t *testing.T, store Store) {
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir())
|
existingDomain := "test.com"
|
||||||
t.Cleanup(cleanUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
existingDomain := "test.com"
|
account, err := store.GetAccountByPrivateDomain(context.Background(), existingDomain)
|
||||||
|
require.NoError(t, err, "should found account")
|
||||||
|
require.Equal(t, existingDomain, account.Domain, "domains should match")
|
||||||
|
|
||||||
account, err := store.GetAccountByPrivateDomain(context.Background(), existingDomain)
|
_, err = store.GetAccountByPrivateDomain(context.Background(), "missing-domain.com")
|
||||||
require.NoError(t, err, "should found account")
|
require.Error(t, err, "should return error on domain lookup")
|
||||||
require.Equal(t, existingDomain, account.Domain, "domains should match")
|
parsedErr, ok := status.FromError(err)
|
||||||
|
require.True(t, ok)
|
||||||
_, err = store.GetAccountByPrivateDomain(context.Background(), "missing-domain.com")
|
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
||||||
require.Error(t, err, "should return error on domain lookup")
|
})
|
||||||
parsedErr, ok := status.FromError(err)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlite_GetTokenIDByHashedToken(t *testing.T) {
|
func Test_GetTokenIDByHashedToken(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("NETBIRD_STORE_ENGINE", string(SqliteStoreEngine))
|
runTestForAllEngines(t, "../testdata/store.sql", func(t *testing.T, store Store) {
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir())
|
hashed := "SoMeHaShEdToKeN"
|
||||||
t.Cleanup(cleanUp)
|
id := "9dj38s35-63fb-11ec-90d6-0242ac120003"
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
hashed := "SoMeHaShEdToKeN"
|
token, err := store.GetTokenIDByHashedToken(context.Background(), hashed)
|
||||||
id := "9dj38s35-63fb-11ec-90d6-0242ac120003"
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, id, token)
|
||||||
|
|
||||||
token, err := store.GetTokenIDByHashedToken(context.Background(), hashed)
|
_, err = store.GetTokenIDByHashedToken(context.Background(), "non-existing-hash")
|
||||||
require.NoError(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, id, token)
|
parsedErr, ok := status.FromError(err)
|
||||||
|
require.True(t, ok)
|
||||||
_, err = store.GetTokenIDByHashedToken(context.Background(), "non-existing-hash")
|
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
||||||
require.Error(t, err)
|
})
|
||||||
parsedErr, ok := status.FromError(err)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMigrate(t *testing.T) {
|
func TestMigrate(t *testing.T) {
|
||||||
|
@ -9,11 +9,16 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
@ -193,6 +198,8 @@ const (
|
|||||||
mysqlDsnEnv = "NETBIRD_STORE_ENGINE_MYSQL_DSN"
|
mysqlDsnEnv = "NETBIRD_STORE_ENGINE_MYSQL_DSN"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var supportedEngines = []Engine{SqliteStoreEngine, PostgresStoreEngine, MysqlStoreEngine}
|
||||||
|
|
||||||
func getStoreEngineFromEnv() Engine {
|
func getStoreEngineFromEnv() Engine {
|
||||||
// NETBIRD_STORE_ENGINE supposed to be used in tests. Otherwise, rely on the config file.
|
// NETBIRD_STORE_ENGINE supposed to be used in tests. Otherwise, rely on the config file.
|
||||||
kind, ok := os.LookupEnv("NETBIRD_STORE_ENGINE")
|
kind, ok := os.LookupEnv("NETBIRD_STORE_ENGINE")
|
||||||
@ -201,7 +208,7 @@ func getStoreEngineFromEnv() Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
value := Engine(strings.ToLower(kind))
|
value := Engine(strings.ToLower(kind))
|
||||||
if value == SqliteStoreEngine || value == PostgresStoreEngine || value == MysqlStoreEngine {
|
if slices.Contains(supportedEngines, value) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,51 +356,126 @@ func NewTestStoreFromSQL(ctx context.Context, filename string, dataDir string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getSqlStoreEngine(ctx context.Context, store *SqlStore, kind Engine) (Store, func(), error) {
|
func getSqlStoreEngine(ctx context.Context, store *SqlStore, kind Engine) (Store, func(), error) {
|
||||||
if kind == PostgresStoreEngine {
|
var cleanup func()
|
||||||
cleanUp, err := testutil.CreatePostgresTestContainer()
|
var err error
|
||||||
if err != nil {
|
switch kind {
|
||||||
return nil, nil, err
|
case PostgresStoreEngine:
|
||||||
|
store, cleanup, err = newReusedPostgresStore(ctx, store, kind)
|
||||||
|
case MysqlStoreEngine:
|
||||||
|
store, cleanup, err = newReusedMysqlStore(ctx, store, kind)
|
||||||
|
default:
|
||||||
|
cleanup = func() {
|
||||||
|
// sqlite doesn't need to be cleaned up
|
||||||
}
|
}
|
||||||
|
|
||||||
dsn, ok := os.LookupEnv(postgresDsnEnv)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("%s is not set", postgresDsnEnv)
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err = NewPostgresqlStoreFromSqlStore(ctx, store, dsn, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return store, cleanUp, nil
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
if kind == MysqlStoreEngine {
|
return nil, cleanup, fmt.Errorf("failed to create test store: %v", err)
|
||||||
cleanUp, err := testutil.CreateMysqlTestContainer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dsn, ok := os.LookupEnv(mysqlDsnEnv)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("%s is not set", mysqlDsnEnv)
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err = NewMysqlStoreFromSqlStore(ctx, store, dsn, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return store, cleanUp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeConnection := func() {
|
closeConnection := func() {
|
||||||
|
cleanup()
|
||||||
store.Close(ctx)
|
store.Close(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return store, closeConnection, nil
|
return store, closeConnection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newReusedPostgresStore(ctx context.Context, store *SqlStore, kind Engine) (*SqlStore, func(), error) {
|
||||||
|
if envDsn, ok := os.LookupEnv(postgresDsnEnv); !ok || envDsn == "" {
|
||||||
|
var err error
|
||||||
|
_, err = testutil.CreatePostgresTestContainer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, ok := os.LookupEnv(postgresDsnEnv)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("%s is not set", postgresDsnEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to open postgres connection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, cleanup, err := createRandomDB(dsn, db, kind)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cleanup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err = NewPostgresqlStoreFromSqlStore(ctx, store, dsn, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cleanup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return store, cleanup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReusedMysqlStore(ctx context.Context, store *SqlStore, kind Engine) (*SqlStore, func(), error) {
|
||||||
|
if envDsn, ok := os.LookupEnv(mysqlDsnEnv); !ok || envDsn == "" {
|
||||||
|
var err error
|
||||||
|
_, err = testutil.CreateMysqlTestContainer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, ok := os.LookupEnv(mysqlDsnEnv)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("%s is not set", mysqlDsnEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := gorm.Open(mysql.Open(dsn+"?charset=utf8&parseTime=True&loc=Local"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to open mysql connection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, cleanup, err := createRandomDB(dsn, db, kind)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cleanup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err = NewMysqlStoreFromSqlStore(ctx, store, dsn, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return store, cleanup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRandomDB(dsn string, db *gorm.DB, engine Engine) (string, func(), error) {
|
||||||
|
dbName := fmt.Sprintf("test_db_%s", strings.ReplaceAll(uuid.New().String(), "-", "_"))
|
||||||
|
|
||||||
|
if err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbName)).Error; err != nil {
|
||||||
|
return "", nil, fmt.Errorf("failed to create database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
cleanup := func() {
|
||||||
|
switch engine {
|
||||||
|
case PostgresStoreEngine:
|
||||||
|
err = db.Exec(fmt.Sprintf("DROP DATABASE %s WITH (FORCE)", dbName)).Error
|
||||||
|
case MysqlStoreEngine:
|
||||||
|
// err = killMySQLConnections(dsn, dbName)
|
||||||
|
err = db.Exec(fmt.Sprintf("DROP DATABASE %s", dbName)).Error
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to drop database %s: %v", dbName, err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sqlDB, _ := db.DB()
|
||||||
|
_ = sqlDB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaceDBName(dsn, dbName), cleanup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceDBName(dsn, newDBName string) string {
|
||||||
|
re := regexp.MustCompile(`(?P<pre>[:/@])(?P<dbname>[^/?]+)(?P<post>\?|$)`)
|
||||||
|
return re.ReplaceAllString(dsn, `${pre}`+newDBName+`${post}`)
|
||||||
|
}
|
||||||
|
|
||||||
func loadSQL(db *gorm.DB, filepath string) error {
|
func loadSQL(db *gorm.DB, filepath string) error {
|
||||||
sqlContent, err := os.ReadFile(filepath)
|
sqlContent, err := os.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -22,7 +22,7 @@ func CreateMysqlTestContainer() (func(), error) {
|
|||||||
myContainer, err := mysql.RunContainer(ctx,
|
myContainer, err := mysql.RunContainer(ctx,
|
||||||
testcontainers.WithImage("mlsmaycon/warmed-mysql:8"),
|
testcontainers.WithImage("mlsmaycon/warmed-mysql:8"),
|
||||||
mysql.WithDatabase("testing"),
|
mysql.WithDatabase("testing"),
|
||||||
mysql.WithUsername("testing"),
|
mysql.WithUsername("root"),
|
||||||
mysql.WithPassword("testing"),
|
mysql.WithPassword("testing"),
|
||||||
testcontainers.WithWaitStrategy(
|
testcontainers.WithWaitStrategy(
|
||||||
wait.ForLog("/usr/sbin/mysqld: ready for connections").
|
wait.ForLog("/usr/sbin/mysqld: ready for connections").
|
||||||
@ -34,6 +34,7 @@ func CreateMysqlTestContainer() (func(), error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
|
os.Unsetenv("NETBIRD_STORE_ENGINE_MYSQL_DSN")
|
||||||
timeoutCtx, cancelFunc := context.WithTimeout(ctx, 1*time.Second)
|
timeoutCtx, cancelFunc := context.WithTimeout(ctx, 1*time.Second)
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
if err = myContainer.Terminate(timeoutCtx); err != nil {
|
if err = myContainer.Terminate(timeoutCtx); err != nil {
|
||||||
@ -68,6 +69,7 @@ func CreatePostgresTestContainer() (func(), error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
|
os.Unsetenv("NETBIRD_STORE_ENGINE_POSTGRES_DSN")
|
||||||
timeoutCtx, cancelFunc := context.WithTimeout(ctx, 1*time.Second)
|
timeoutCtx, cancelFunc := context.WithTimeout(ctx, 1*time.Second)
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
if err = pgContainer.Terminate(timeoutCtx); err != nil {
|
if err = pgContainer.Terminate(timeoutCtx); err != nil {
|
||||||
|
@ -80,7 +80,7 @@ type User struct {
|
|||||||
// AutoGroups is a list of Group IDs to auto-assign to peers registered by this user
|
// AutoGroups is a list of Group IDs to auto-assign to peers registered by this user
|
||||||
AutoGroups []string `gorm:"serializer:json"`
|
AutoGroups []string `gorm:"serializer:json"`
|
||||||
PATs map[string]*PersonalAccessToken `gorm:"-"`
|
PATs map[string]*PersonalAccessToken `gorm:"-"`
|
||||||
PATsG []PersonalAccessToken `json:"-" gorm:"foreignKey:UserID;references:id"`
|
PATsG []PersonalAccessToken `json:"-" gorm:"foreignKey:UserID;references:id;constraint:OnDelete:CASCADE;"`
|
||||||
// Blocked indicates whether the user is blocked. Blocked users can't use the system.
|
// Blocked indicates whether the user is blocked. Blocked users can't use the system.
|
||||||
Blocked bool
|
Blocked bool
|
||||||
// LastLogin is the last time the user logged in to IdP
|
// LastLogin is the last time the user logged in to IdP
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/coder/websocket"
|
"github.com/coder/websocket"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
||||||
"github.com/netbirdio/netbird/util/embeddedroots"
|
"github.com/netbirdio/netbird/util/embeddedroots"
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/coder/websocket"
|
"github.com/coder/websocket"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/coder/websocket"
|
"github.com/coder/websocket"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// URLPath is the path for the websocket connection.
|
// URLPath is the path for the websocket connection.
|
||||||
|
Reference in New Issue
Block a user