Skip to content

Commit c1e1cda

Browse files
committed
Include nftables information in Agent supportbundle
Signed-off-by: molegit9 <[email protected]>
1 parent 3c81b27 commit c1e1cda

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

pkg/support/dump_others.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,38 @@
1818
package support
1919

2020
import (
21+
"bytes"
2122
"fmt"
2223
"path"
2324
"path/filepath"
2425
"strings"
26+
"sync"
27+
28+
"k8s.io/klog/v2"
2529

2630
"antrea.io/antrea/pkg/agent/util/iptables"
31+
nftclient "antrea.io/antrea/pkg/agent/util/nftables"
2732
"antrea.io/antrea/pkg/util/logdir"
2833
)
2934

35+
// nftablesIPv4Supported and nftablesIPv6Supported check if the kernel supports nftables.
36+
// They initialize the client once to verify support, but the returned clients are not used.
37+
var nftablesIPv4Supported = sync.OnceValue(func() bool {
38+
if _, err := nftclient.New(true, false); err != nil {
39+
klog.ErrorS(err, "nftables IPv4 not supported on this Node")
40+
return false
41+
}
42+
return true
43+
})
44+
45+
var nftablesIPv6Supported = sync.OnceValue(func() bool {
46+
if _, err := nftclient.New(false, true); err != nil {
47+
klog.ErrorS(err, "nftables IPv6 not supported on this Node")
48+
return false
49+
}
50+
return true
51+
})
52+
3053
func (d *agentDumper) DumpLog(basedir string) error {
3154
logDir := logdir.GetLogDir()
3255
timeFilter := timestampFilter(d.since)
@@ -41,6 +64,9 @@ func (d *agentDumper) DumpHostNetworkInfo(basedir string) error {
4164
if err := d.dumpIPTables(basedir); err != nil {
4265
return err
4366
}
67+
if err := d.dumpNFTables(basedir); err != nil {
68+
return err
69+
}
4470
if err := d.dumpIPToolInfo(basedir); err != nil {
4571
return err
4672
}
@@ -59,6 +85,46 @@ func (d *agentDumper) dumpIPTables(basedir string) error {
5985
return writeFile(d.fs, filepath.Join(basedir, "iptables"), "iptables", data)
6086
}
6187

88+
func (d *agentDumper) dumpNFTables(basedir string) error {
89+
isV4Supported := d.v4Enabled && nftablesIPv4Supported()
90+
isV6Supported := d.v6Enabled && nftablesIPv6Supported()
91+
92+
var data bytes.Buffer
93+
94+
if isV4Supported {
95+
output, err := d.executor.Command("nft", "list", "table", "ip", "antrea").CombinedOutput()
96+
if err != nil {
97+
return fmt.Errorf("failed to dump nftables table 'ip antrea': %w", err)
98+
}
99+
if len(output) > 0 {
100+
data.Write(output)
101+
data.WriteString("\n")
102+
}
103+
}
104+
105+
if isV6Supported {
106+
output, err := d.executor.Command("nft", "list", "table", "ip6", "antrea").CombinedOutput()
107+
if err != nil {
108+
return fmt.Errorf("failed to dump nftables table 'ip6 antrea': %w", err)
109+
}
110+
if len(output) > 0 {
111+
data.Write(output)
112+
data.WriteString("\n")
113+
}
114+
}
115+
116+
if data.Len() == 0 {
117+
return nil
118+
}
119+
120+
fileName := "nftables"
121+
if err := writeFile(d.fs, filepath.Join(basedir, fileName), fileName, data.Bytes()); err != nil {
122+
return fmt.Errorf("failed to write nftables file: %w", err)
123+
}
124+
125+
return nil
126+
}
127+
62128
func (d *agentDumper) dumpIPToolInfo(basedir string) error {
63129
dump := func(name string) error {
64130
output, err := d.executor.Command("ip", name).CombinedOutput()

pkg/support/dump_others_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package support
1919

2020
import (
21+
"fmt"
2122
"os"
2223
"path/filepath"
2324
"testing"
@@ -27,6 +28,8 @@ import (
2728
"github.com/spf13/afero"
2829
"github.com/stretchr/testify/assert"
2930
"github.com/stretchr/testify/require"
31+
"k8s.io/utils/exec"
32+
testingexec "k8s.io/utils/exec/testing"
3033
)
3134

3235
func TestDumpLog(t *testing.T) {
@@ -49,3 +52,156 @@ func TestDumpLog(t *testing.T) {
4952
require.NoError(t, err)
5053
assert.True(t, ok)
5154
}
55+
56+
func TestDumpNFTables(t *testing.T) {
57+
const nftV4Output = "table ip antrea { chain antrea-chain { type filter hook input priority 0; } }"
58+
const nftV6Output = "table ip6 antrea { chain antrea-chain6 { type filter hook input priority 0; } }"
59+
60+
v4ErrorAction := func() ([]byte, []byte, error) {
61+
return nil, nil, fmt.Errorf("v4 error")
62+
}
63+
v4SuccessAction := func() ([]byte, []byte, error) {
64+
return []byte(nftV4Output), nil, nil
65+
}
66+
v6SuccessAction := func() ([]byte, []byte, error) {
67+
return []byte(nftV6Output), nil, nil
68+
}
69+
emptySuccessAction := func() ([]byte, []byte, error) {
70+
return []byte(""), nil, nil
71+
}
72+
73+
originalV4Check := nftablesIPv4Supported
74+
originalV6Check := nftablesIPv6Supported
75+
defer func() {
76+
nftablesIPv4Supported = originalV4Check
77+
nftablesIPv6Supported = originalV6Check
78+
}()
79+
80+
nftablesIPv4Supported = func() bool { return true }
81+
nftablesIPv6Supported = func() bool { return true }
82+
83+
tests := []struct {
84+
name string
85+
v4Enabled bool
86+
v6Enabled bool
87+
commandActions []testingexec.FakeCommandAction
88+
expectedContent string
89+
expectFile bool
90+
expectErr bool
91+
}{
92+
{
93+
name: "v4 enabled only",
94+
v4Enabled: true,
95+
v6Enabled: false,
96+
commandActions: []testingexec.FakeCommandAction{
97+
func(cmd string, args ...string) exec.Cmd {
98+
return &testingexec.FakeCmd{
99+
CombinedOutputScript: []testingexec.FakeAction{v4SuccessAction},
100+
}
101+
},
102+
},
103+
expectedContent: nftV4Output + "\n",
104+
expectFile: true,
105+
},
106+
{
107+
name: "v6 enabled only",
108+
v4Enabled: false,
109+
v6Enabled: true,
110+
commandActions: []testingexec.FakeCommandAction{
111+
func(cmd string, args ...string) exec.Cmd {
112+
return &testingexec.FakeCmd{
113+
CombinedOutputScript: []testingexec.FakeAction{v6SuccessAction},
114+
}
115+
},
116+
},
117+
expectedContent: nftV6Output + "\n",
118+
expectFile: true,
119+
},
120+
{
121+
name: "v4 and v6 enabled",
122+
v4Enabled: true,
123+
v6Enabled: true,
124+
commandActions: []testingexec.FakeCommandAction{
125+
func(cmd string, args ...string) exec.Cmd {
126+
return &testingexec.FakeCmd{
127+
CombinedOutputScript: []testingexec.FakeAction{v4SuccessAction},
128+
}
129+
},
130+
func(cmd string, args ...string) exec.Cmd {
131+
return &testingexec.FakeCmd{
132+
CombinedOutputScript: []testingexec.FakeAction{v6SuccessAction},
133+
}
134+
},
135+
},
136+
expectedContent: nftV4Output + "\n" + nftV6Output + "\n",
137+
expectFile: true,
138+
},
139+
{
140+
name: "v4 command error",
141+
v4Enabled: true,
142+
v6Enabled: true,
143+
commandActions: []testingexec.FakeCommandAction{
144+
func(cmd string, args ...string) exec.Cmd {
145+
return &testingexec.FakeCmd{
146+
CombinedOutputScript: []testingexec.FakeAction{v4ErrorAction},
147+
}
148+
},
149+
},
150+
expectFile: false,
151+
expectErr: true,
152+
},
153+
{
154+
name: "no rules found (empty output)",
155+
v4Enabled: true,
156+
v6Enabled: true,
157+
commandActions: []testingexec.FakeCommandAction{
158+
func(cmd string, args ...string) exec.Cmd {
159+
return &testingexec.FakeCmd{CombinedOutputScript: []testingexec.FakeAction{emptySuccessAction}}
160+
},
161+
func(cmd string, args ...string) exec.Cmd {
162+
return &testingexec.FakeCmd{CombinedOutputScript: []testingexec.FakeAction{emptySuccessAction}}
163+
},
164+
},
165+
expectFile: false,
166+
expectErr: false,
167+
},
168+
}
169+
170+
for _, tc := range tests {
171+
t.Run(tc.name, func(t *testing.T) {
172+
fs := afero.NewMemMapFs()
173+
fs.MkdirAll(baseDir, os.ModePerm)
174+
175+
fakeExecutor := &testingexec.FakeExec{}
176+
fakeExecutor.CommandScript = tc.commandActions
177+
178+
dumper := &agentDumper{
179+
fs: fs,
180+
executor: fakeExecutor,
181+
v4Enabled: tc.v4Enabled,
182+
v6Enabled: tc.v6Enabled,
183+
}
184+
185+
err := dumper.dumpNFTables(baseDir)
186+
187+
if tc.expectErr {
188+
require.Error(t, err)
189+
return
190+
}
191+
192+
require.NoError(t, err)
193+
194+
filePath := filepath.Join(baseDir, "nftables")
195+
196+
ok, err := afero.Exists(fs, filePath)
197+
require.NoError(t, err)
198+
assert.Equal(t, tc.expectFile, ok, "Expected nftables file existence to be %t", tc.expectFile)
199+
200+
if tc.expectFile {
201+
content, err := afero.ReadFile(fs, filePath)
202+
require.NoError(t, err)
203+
assert.Equal(t, tc.expectedContent, string(content), "File content does not match")
204+
}
205+
})
206+
}
207+
}

0 commit comments

Comments
 (0)