Run CI tests with nftables

Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
Rob Murray
2025-04-23 17:13:11 +01:00
parent d82900dfde
commit 178416334f
8 changed files with 44 additions and 38 deletions

View File

@@ -145,6 +145,7 @@ jobs:
];
if ("${{ inputs.storage }}" == "snapshotter") {
includes.push({ os: 'ubuntu-24.04', mode: 'firewalld' });
includes.push({ os: 'ubuntu-24.04', mode: 'nftables' });
}
await core.group(`Set matrix`, async () => {
core.info(`matrix: ${JSON.stringify(includes)}`);
@@ -190,6 +191,9 @@ jobs:
echo "FIREWALLD=true" >> $GITHUB_ENV
CACHE_DEV_SCOPE="${CACHE_DEV_SCOPE}firewalld"
fi
if [[ "${{ matrix.mode }}" == *"nftables"* ]]; then
echo "DOCKER_FIREWALL_BACKEND=nftables" >> $GITHUB_ENV
fi
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
-
name: Set up Docker Buildx
@@ -328,7 +332,7 @@ jobs:
// 'include' with other matrix variables that aren't part of the
// include items.
// Moreover, since the goal is to run only relevant tests with
// firewalld enabled to minimize the number of CI jobs, we
// firewalld/nftables enabled to minimize the number of CI jobs, we
// statically define the list of test suites that we want to run.
if ("${{ inputs.storage }}" == "snapshotter") {
matrix.include.push({
@@ -343,6 +347,18 @@ jobs:
'mode': 'firewalld',
'test': 'DockerNetworkSuite'
});
matrix.include.push({
'mode': 'nftables',
'test': 'DockerCLINetworkSuite|DockerCLIPortSuite|DockerDaemonSuite'
});
matrix.include.push({
'mode': 'nftables',
'test': 'DockerSwarmSuite'
});
matrix.include.push({
'mode': 'nftables',
'test': 'DockerNetworkSuite'
});
}
await core.group(`Set matrix`, async () => {
core.info(`matrix: ${JSON.stringify(matrix)}`);
@@ -380,6 +396,9 @@ jobs:
echo "FIREWALLD=true" >> $GITHUB_ENV
CACHE_DEV_SCOPE="${CACHE_DEV_SCOPE}firewalld"
fi
if [[ "${{ matrix.mode }}" == *"nftables"* ]]; then
echo "DOCKER_FIREWALL_BACKEND=nftables" >> $GITHUB_ENV
fi
echo "CACHE_DEV_SCOPE=${CACHE_DEV_SCOPE}" >> $GITHUB_ENV
-
name: Set up Docker Buildx
@@ -437,7 +456,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-integration-cli-${{ inputs.storage }}-${{ env.TESTREPORTS_NAME }}
name: test-reports-integration-cli-${{ inputs.storage }}-${{ matrix.mode }}-${{ env.TESTREPORTS_NAME }}
path: /tmp/reports/*
retention-days: 1
@@ -460,7 +479,7 @@ jobs:
uses: actions/download-artifact@v4
with:
path: /tmp/reports
pattern: test-reports-integration-cli-${{ inputs.storage }}-*
pattern: test-reports-integration-cli-${{ inputs.storage }}-${{ matrix.mode }}-*
merge-multiple: true
-
name: Install teststat

View File

@@ -38,6 +38,7 @@ DOCKER_ENVS := \
-e DOCKERCLI_INTEGRATION_REPOSITORY \
-e DOCKER_DEBUG \
-e DOCKER_EXPERIMENTAL \
-e DOCKER_FIREWALL_BACKEND \
-e DOCKER_GITCOMMIT \
-e DOCKER_GRAPHDRIVER \
-e DOCKER_LDFLAGS \

View File

@@ -1,6 +1,7 @@
package networking
import (
"os"
"testing"
"github.com/docker/docker/client"
@@ -10,12 +11,16 @@ import (
is "gotest.tools/v3/assert/cmp"
)
const defaultFirewallBackend = "iptables"
func TestInfoFirewallBackend(t *testing.T) {
ctx := setupTest(t)
c := testEnv.APIClient()
expDriver := "iptables"
if !testEnv.IsRootless() && networking.FirewalldRunning() {
expDriver := defaultFirewallBackend
if val := os.Getenv("DOCKER_FIREWALL_BACKEND"); val != "" {
expDriver = val
} else if !testEnv.IsRootless() && networking.FirewalldRunning() {
expDriver = "iptables+firewalld"
}
info, err := c.Info(ctx)

View File

@@ -20,11 +20,7 @@ func (c *Controller) FirewallBackend() *system.FirewallInfo {
if nftables.Enabled() {
return &system.FirewallInfo{Driver: "nftables"}
}
usingFirewalld, err := iptables.UsingFirewalld()
if err != nil {
return nil
}
if usingFirewalld {
if iptables.UsingFirewalld() {
info := &system.FirewallInfo{Driver: "iptables+firewalld"}
reloadedAt := iptables.FirewalldReloadedAt()
if !reloadedAt.IsZero() {

View File

@@ -21,16 +21,8 @@ import (
"gotest.tools/v3/skip"
)
func usingFirewalld() bool {
// Check for existence of a dummy rule to make sure iptables is initialised - then can
// check whether firewalld is running.
_ = iptables.GetIptable(iptables.IPv4).Exists(iptables.Filter, "FORWARD", "-j", "DROP")
fw, _ := iptables.UsingFirewalld()
return fw
}
func TestCleanupIptableRules(t *testing.T) {
skip.If(t, usingFirewalld(), "firewalld is running in the host netns, it can't modify rules in the test's netns")
skip.If(t, iptables.UsingFirewalld(), "firewalld is running in the host netns, it can't modify rules in the test's netns")
defer netnsutils.SetupTestOSContext(t)()
bridgeChains := []struct {
@@ -86,7 +78,7 @@ func TestCleanupIptableRules(t *testing.T) {
// TestIptabler tests combinations of firewaller options against golden results.
func TestIptabler(t *testing.T) {
skip.If(t, usingFirewalld(), "firewalld is running in the host netns, it can't modify rules in the test's netns")
skip.If(t, iptables.UsingFirewalld(), "firewalld is running in the host netns, it can't modify rules in the test's netns")
const (
ipv4 int64 = iota

View File

@@ -14,6 +14,10 @@ import (
const userChain = "DOCKER-USER"
func (c *Controller) selectFirewallBackend() {
// Don't try to enable nftables if firewalld is running.
if iptables.UsingFirewalld() {
return
}
// Only try to use nftables if explicitly enabled by env-var.
// TODO(robmry) - command line options?
if os.Getenv("DOCKER_FIREWALL_BACKEND") == "nftables" {

View File

@@ -11,7 +11,6 @@ import (
"time"
"github.com/containerd/log"
"github.com/docker/docker/pkg/rootless"
dbus "github.com/godbus/dbus/v5"
"github.com/pkg/errors"
)
@@ -35,8 +34,7 @@ type Conn struct {
var (
connection *Conn
firewalldInitCalled bool
firewalldRunning bool // is Firewalld service running
firewalldRunning bool // is Firewalld service running
// Time of the last firewalld reload.
firewalldReloadedAt atomic.Value
// Mutex to serialise firewalld reload callbacks.
@@ -45,16 +43,10 @@ var (
)
// UsingFirewalld returns true if iptables rules will be applied via firewalld's
// passthrough interface. The error return is non-nil if the status cannot be
// determined because the initialisation function has not been called.
func UsingFirewalld() (bool, error) {
// If called before startup has completed, the firewall backend is unknown.
// But, if running rootless, the init function is not called because
// firewalld will be running in the host's netns, not in rootlesskit's.
if !firewalldInitCalled && !rootless.RunningWithRootlessKit() {
return false, errors.New("iptables.firewalld is not initialised")
}
return firewalldRunning, nil
// passthrough interface.
func UsingFirewalld() bool {
_ = initCheck()
return firewalldRunning
}
// FirewalldReloadedAt returns the time at which the daemon last completed a
@@ -72,7 +64,6 @@ func FirewalldReloadedAt() time.Time {
func firewalldInit() error {
var err error
firewalldInitCalled = true
if connection, err = newConnection(); err != nil {
return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/docker/docker/internal/testutils/netnsutils"
"golang.org/x/sync/errgroup"
"gotest.tools/v3/assert"
"gotest.tools/v3/skip"
)
const (
@@ -258,10 +259,7 @@ func TestExistsRaw(t *testing.T) {
}
func TestRule(t *testing.T) {
_ = firewalldInit()
if res, _ := UsingFirewalld(); res {
t.Skip("firewalld in host netns cannot create rules in the test's netns")
}
skip.If(t, UsingFirewalld(), "firewalld is running in the host netns, it can't modify rules in the test's netns")
defer netnsutils.SetupTestOSContext(t)()
assert.NilError(t, exec.Command("iptables", "-N", "TESTCHAIN").Run())