Skip to content

Commit 63d257d

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

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

pkg/support/dump_others.go

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

2020
import (
21+
"bytes"
2122
"fmt"
2223
"path"
2324
"path/filepath"
2425
"strings"
2526

27+
"k8s.io/klog/v2"
28+
2629
"antrea.io/antrea/pkg/agent/util/iptables"
30+
nftclient "antrea.io/antrea/pkg/agent/util/nftables"
2731
"antrea.io/antrea/pkg/util/logdir"
2832
)
2933

34+
// newNFTablesClient is defined as a variable to allow mocking in unit tests.
35+
var newNFTablesClient = func(v4Enabled, v6Enabled bool) (interface{}, error) {
36+
return nftclient.New(v4Enabled, v6Enabled)
37+
}
38+
3039
func (d *agentDumper) DumpLog(basedir string) error {
3140
logDir := logdir.GetLogDir()
3241
timeFilter := timestampFilter(d.since)
@@ -41,6 +50,9 @@ func (d *agentDumper) DumpHostNetworkInfo(basedir string) error {
4150
if err := d.dumpIPTables(basedir); err != nil {
4251
return err
4352
}
53+
if err := d.dumpNFTables(basedir); err != nil {
54+
return err
55+
}
4456
if err := d.dumpIPToolInfo(basedir); err != nil {
4557
return err
4658
}
@@ -59,6 +71,50 @@ func (d *agentDumper) dumpIPTables(basedir string) error {
5971
return writeFile(d.fs, filepath.Join(basedir, "iptables"), "iptables", data)
6072
}
6173

74+
func (d *agentDumper) dumpNFTables(basedir string) error {
75+
// We only care about the error here to check if nftables is supported by the kernel.
76+
// The returned client is not used.
77+
if _, err := newNFTablesClient(d.v4Enabled, d.v6Enabled); err != nil {
78+
klog.ErrorS(err, "Skipping nftables dump because it is not supported on this Node")
79+
return nil
80+
}
81+
82+
var data bytes.Buffer
83+
84+
if d.v4Enabled {
85+
output, err := d.executor.Command("nft", "list", "table", "ip", "antrea").CombinedOutput()
86+
if err != nil {
87+
return fmt.Errorf("failed to dump nftables table 'ip antrea': %w", err)
88+
}
89+
if len(output) > 0 {
90+
data.Write(output)
91+
data.WriteString("\n")
92+
}
93+
}
94+
95+
if d.v6Enabled {
96+
output, err := d.executor.Command("nft", "list", "table", "ip6", "antrea").CombinedOutput()
97+
if err != nil {
98+
return fmt.Errorf("failed to dump nftables table 'ip6 antrea': %w", err)
99+
}
100+
if len(output) > 0 {
101+
data.Write(output)
102+
data.WriteString("\n")
103+
}
104+
}
105+
106+
if data.Len() == 0 {
107+
return nil
108+
}
109+
110+
fileName := "nftables"
111+
if err := writeFile(d.fs, filepath.Join(basedir, fileName), fileName, data.Bytes()); err != nil {
112+
return fmt.Errorf("failed to write nftables file: %w", err)
113+
}
114+
115+
return nil
116+
}
117+
62118
func (d *agentDumper) dumpIPToolInfo(basedir string) error {
63119
dump := func(name string) error {
64120
output, err := d.executor.Command("ip", name).CombinedOutput()

pkg/support/dump_others_test.go

Lines changed: 153 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,153 @@ 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+
originalNewFunc := newNFTablesClient
74+
defer func() { newNFTablesClient = originalNewFunc }()
75+
76+
newNFTablesClient = func(v4, v6 bool) (interface{}, error) {
77+
return nil, nil
78+
}
79+
80+
tests := []struct {
81+
name string
82+
v4Enabled bool
83+
v6Enabled bool
84+
commandActions []testingexec.FakeCommandAction
85+
expectedContent string
86+
expectFile bool
87+
expectErr bool
88+
}{
89+
{
90+
name: "v4 enabled only",
91+
v4Enabled: true,
92+
v6Enabled: false,
93+
commandActions: []testingexec.FakeCommandAction{
94+
func(cmd string, args ...string) exec.Cmd {
95+
return &testingexec.FakeCmd{
96+
CombinedOutputScript: []testingexec.FakeAction{v4SuccessAction},
97+
}
98+
},
99+
},
100+
expectedContent: nftV4Output + "\n",
101+
expectFile: true,
102+
},
103+
{
104+
name: "v6 enabled only",
105+
v4Enabled: false,
106+
v6Enabled: true,
107+
commandActions: []testingexec.FakeCommandAction{
108+
func(cmd string, args ...string) exec.Cmd {
109+
return &testingexec.FakeCmd{
110+
CombinedOutputScript: []testingexec.FakeAction{v6SuccessAction},
111+
}
112+
},
113+
},
114+
expectedContent: nftV6Output + "\n",
115+
expectFile: true,
116+
},
117+
{
118+
name: "v4 and v6 enabled",
119+
v4Enabled: true,
120+
v6Enabled: true,
121+
commandActions: []testingexec.FakeCommandAction{
122+
func(cmd string, args ...string) exec.Cmd {
123+
return &testingexec.FakeCmd{
124+
CombinedOutputScript: []testingexec.FakeAction{v4SuccessAction},
125+
}
126+
},
127+
func(cmd string, args ...string) exec.Cmd {
128+
return &testingexec.FakeCmd{
129+
CombinedOutputScript: []testingexec.FakeAction{v6SuccessAction},
130+
}
131+
},
132+
},
133+
expectedContent: nftV4Output + "\n" + nftV6Output + "\n",
134+
expectFile: true,
135+
},
136+
{
137+
name: "v4 command error",
138+
v4Enabled: true,
139+
v6Enabled: true,
140+
commandActions: []testingexec.FakeCommandAction{
141+
func(cmd string, args ...string) exec.Cmd {
142+
return &testingexec.FakeCmd{
143+
CombinedOutputScript: []testingexec.FakeAction{v4ErrorAction},
144+
}
145+
},
146+
},
147+
expectFile: false,
148+
expectErr: true,
149+
},
150+
{
151+
name: "no rules found (empty output)",
152+
v4Enabled: true,
153+
v6Enabled: true,
154+
commandActions: []testingexec.FakeCommandAction{
155+
func(cmd string, args ...string) exec.Cmd {
156+
return &testingexec.FakeCmd{CombinedOutputScript: []testingexec.FakeAction{emptySuccessAction}}
157+
},
158+
func(cmd string, args ...string) exec.Cmd {
159+
return &testingexec.FakeCmd{CombinedOutputScript: []testingexec.FakeAction{emptySuccessAction}}
160+
},
161+
},
162+
expectFile: false,
163+
expectErr: false,
164+
},
165+
}
166+
167+
for _, tc := range tests {
168+
t.Run(tc.name, func(t *testing.T) {
169+
fs := afero.NewMemMapFs()
170+
fs.MkdirAll(baseDir, os.ModePerm)
171+
172+
fakeExecutor := &testingexec.FakeExec{}
173+
fakeExecutor.CommandScript = tc.commandActions
174+
175+
dumper := &agentDumper{
176+
fs: fs,
177+
executor: fakeExecutor,
178+
v4Enabled: tc.v4Enabled,
179+
v6Enabled: tc.v6Enabled,
180+
}
181+
182+
err := dumper.dumpNFTables(baseDir)
183+
184+
if tc.expectErr {
185+
require.Error(t, err)
186+
return
187+
}
188+
189+
require.NoError(t, err)
190+
191+
filePath := filepath.Join(baseDir, "nftables")
192+
193+
ok, err := afero.Exists(fs, filePath)
194+
require.NoError(t, err)
195+
assert.Equal(t, tc.expectFile, ok, "Expected nftables file existence to be %t", tc.expectFile)
196+
197+
if tc.expectFile {
198+
content, err := afero.ReadFile(fs, filePath)
199+
require.NoError(t, err)
200+
assert.Equal(t, tc.expectedContent, string(content), "File content does not match")
201+
}
202+
})
203+
}
204+
}

0 commit comments

Comments
 (0)