Skip to content

Commit 7cd8f1d

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

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

pkg/support/dump_others.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
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

@@ -41,6 +45,9 @@ func (d *agentDumper) DumpHostNetworkInfo(basedir string) error {
4145
if err := d.dumpIPTables(basedir); err != nil {
4246
return err
4347
}
48+
if err := d.dumpNFTables(basedir); err != nil {
49+
return err
50+
}
4451
if err := d.dumpIPToolInfo(basedir); err != nil {
4552
return err
4653
}
@@ -59,6 +66,48 @@ func (d *agentDumper) dumpIPTables(basedir string) error {
5966
return writeFile(d.fs, filepath.Join(basedir, "iptables"), "iptables", data)
6067
}
6168

69+
func (d *agentDumper) dumpNFTables(basedir string) error {
70+
if _, err := nftclient.New(d.v4Enabled, d.v6Enabled); err != nil {
71+
klog.ErrorS(err, "Skipping nftables dump because it is not supported on this Node")
72+
return nil
73+
}
74+
75+
var data bytes.Buffer
76+
77+
if d.v4Enabled {
78+
output, err := d.executor.Command("nft", "list", "table", "ip", "antrea").CombinedOutput()
79+
if err != nil {
80+
return fmt.Errorf("failed to dump nftables table 'ip antrea': %w", err)
81+
}
82+
if len(output) > 0 {
83+
data.Write(output)
84+
data.WriteString("\n")
85+
}
86+
}
87+
88+
if d.v6Enabled {
89+
output, err := d.executor.Command("nft", "list", "table", "ip6", "antrea").CombinedOutput()
90+
if err != nil {
91+
return fmt.Errorf("failed to dump nftables table 'ip6 antrea': %w", err)
92+
}
93+
if len(output) > 0 {
94+
data.Write(output)
95+
data.WriteString("\n")
96+
}
97+
}
98+
99+
if data.Len() == 0 {
100+
return nil
101+
}
102+
103+
fileName := "nftables"
104+
if err := writeFile(d.fs, filepath.Join(basedir, fileName), fileName, data.Bytes()); err != nil {
105+
return fmt.Errorf("failed to write nftables file: %w", err)
106+
}
107+
108+
return nil
109+
}
110+
62111
func (d *agentDumper) dumpIPToolInfo(basedir string) error {
63112
dump := func(name string) error {
64113
output, err := d.executor.Command("ip", name).CombinedOutput()

pkg/support/dump_others_test.go

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

0 commit comments

Comments
 (0)