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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 6 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ jobs:
strategy:
fail-fast: false
matrix:
go-version: [1.20.x, 1.21.x]
go-version: [1.24.x, 1.25.x]
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v4
- run: make test
- run: |
# Disable apparmor restriction for unprivileged userns
# This is required for the integration tests to run in a new network namespace
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
make test-integration
verify:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ clean:
test:
./hack/test.sh

test-integration:
./hack/test-integration.sh

benchmark:
./hack/benchmark.sh

update:
./hack/update.sh

Expand Down
126 changes: 126 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Copyright The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package knftables_test

import (
"context"
"fmt"
"os"
"testing"

"sigs.k8s.io/knftables"
)

func setupBenchmarkChains(b *testing.B, nft knftables.Interface, tableName string, numChains int) {
b.Helper()
ctx := context.Background()

// Clean up any existing table
tx := nft.NewTransaction()
tx.Delete(&knftables.Table{
Name: tableName,
})
_ = nft.Run(ctx, tx)

// Create table
tx = nft.NewTransaction()
tx.Add(&knftables.Table{
Comment: knftables.PtrTo("benchmark table"),
})

for i := 0; i < numChains; i++ {
tx.Add(&knftables.Chain{
Name: fmt.Sprintf("chain-%d", i),
})
}

if err := nft.Run(ctx, tx); err != nil {
b.Fatalf("failed to setup chains: %v", err)
}
}

func runBenchmarkListChains(b *testing.B, useNetlink bool, numChains int) {
if os.Geteuid() != 0 {
b.Skip("skipping benchmark that requires root")
}

tableName := fmt.Sprintf("bench-chains-%d-%v", numChains, useNetlink)
family := knftables.IPv4Family

var opts []knftables.Option
if !useNetlink {
opts = append(opts, knftables.DisableNetlink)
}

nft, err := knftables.New(family, tableName, opts...)
if err != nil {
b.Fatalf("failed to create knftables client: %v", err)
}

setupBenchmarkChains(b, nft, tableName, numChains)
defer func() {
// cleanup
tx := nft.NewTransaction()
tx.Delete(&knftables.Table{Name: tableName})
_ = nft.Run(context.Background(), tx)
}()

b.ResetTimer()
ctx := context.Background()
for i := 0; i < b.N; i++ {
out, err := nft.List(ctx, "chains")
if err != nil {
b.Fatalf("List chains failed: %v", err)
}
if len(out) != numChains {
b.Fatalf("List chains failed: expected %d chains, got %d", numChains, len(out))
}
}
b.StopTimer()
}

func BenchmarkListChains_NFT_10(b *testing.B) {
runBenchmarkListChains(b, false, 10)
}

func BenchmarkListChains_NFT_100(b *testing.B) {
runBenchmarkListChains(b, false, 100)
}

func BenchmarkListChains_NFT_1000(b *testing.B) {
runBenchmarkListChains(b, false, 1000)
}

func BenchmarkListChains_NFT_10000(b *testing.B) {
runBenchmarkListChains(b, false, 10000)
}

func BenchmarkListChains_Netlink_10(b *testing.B) {
runBenchmarkListChains(b, true, 10)
}

func BenchmarkListChains_Netlink_100(b *testing.B) {
runBenchmarkListChains(b, true, 100)
}

func BenchmarkListChains_Netlink_1000(b *testing.B) {
runBenchmarkListChains(b, true, 1000)
}

func BenchmarkListChains_Netlink_10000(b *testing.B) {
runBenchmarkListChains(b, true, 10000)
}
15 changes: 13 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
module sigs.k8s.io/knftables

go 1.20
go 1.21

toolchain go1.24.13

require (
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.6.0
github.com/google/nftables v0.3.0
github.com/lithammer/dedent v1.1.0
)

require (
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.28.0 // indirect
)
18 changes: 16 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/nftables v0.3.0 h1:bkyZ0cbpVeMHXOrtlFc8ISmfVqq5gPJukoYieyVmITg=
github.com/google/nftables v0.3.0/go.mod h1:BCp9FsrbF1Fn/Yu6CLUc9GGZFw/+hsxfluNXXmxBfRM=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
43 changes: 43 additions & 0 deletions hack/benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash

# Copyright The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

# This script runs the benchmarks.
# It requires `unshare` to create a new network namespace.

if ! command -v unshare >/dev/null 2>&1; then
echo "unshare is required but not found." >&2
exit 1
fi

echo "Running benchmarks in a new network namespace..."

# Compile the test binary
# We use -c to compile the test binary, and then execute it in its own network namespace.
TEST_BINARY="./benchmark.test"
go test -race -c -o "${TEST_BINARY}" .

cleanup() {
rm -f "${TEST_BINARY}"
}
trap cleanup EXIT

# Run the test binary in a new network namespace
# We filter for Benchmark functions
unshare -rn "${TEST_BINARY}" -test.v -test.bench . -test.benchmem -test.run ^$
43 changes: 43 additions & 0 deletions hack/test-integration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash

# Copyright The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

# This script runs the integration tests.
# It requires `unshare` to create a new network namespace.

if ! command -v unshare >/dev/null 2>&1; then
echo "unshare is required but not found." >&2
exit 1
fi

echo "Running integration tests in a new network namespace..."

# Compile the test binary
# We use -c to compile the test binary, and then execute it in its own network namespace.
TEST_BINARY="./integration.test"
go test -race -c -o "${TEST_BINARY}" .

cleanup() {
rm -f "${TEST_BINARY}"
}
trap cleanup EXIT

# Run the test binary in a new network namespace
TEST_PATTERN="${1:-.}"
unshare -rn "${TEST_BINARY}" -test.v -test.run "${TEST_PATTERN}"
1 change: 0 additions & 1 deletion hack/tools/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ linters:

# additional lints
- errorlint
- exportloopref
- gochecknoinits
- gofmt
- misspell
Expand Down
Loading
Loading