Skip to content

Commit e8ae137

Browse files
authored
Merge pull request containerd#3508 from apostasie/chore-clean-network-walker
Fix raft of issues related to network listing
2 parents 24b97cb + 1fe34f8 commit e8ae137

File tree

10 files changed

+307
-224
lines changed

10 files changed

+307
-224
lines changed

cmd/nerdctl/completion/completion.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,13 @@ func NetworkNames(cmd *cobra.Command, exclude []string) ([]string, cobra.ShellCo
124124
if err != nil {
125125
return nil, cobra.ShellCompDirectiveError
126126
}
127-
for netName := range netConfigs {
127+
for netName, network := range netConfigs {
128128
if _, ok := excludeMap[netName]; !ok {
129129
candidates = append(candidates, netName)
130+
if network.NerdctlID != nil {
131+
candidates = append(candidates, *network.NerdctlID)
132+
candidates = append(candidates, (*network.NerdctlID)[0:12])
133+
}
130134
}
131135
}
132136
for _, s := range []string{"host", "none"} {

cmd/nerdctl/network/network_inspect_test.go

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package network
1919
import (
2020
"encoding/json"
2121
"errors"
22+
"strings"
2223
"testing"
2324

2425
"gotest.tools/v3/assert"
@@ -38,6 +39,128 @@ func TestNetworkInspect(t *testing.T) {
3839
)
3940

4041
testGroup := &test.Group{
42+
{
43+
Description: "non existent network",
44+
Command: test.RunCommand("network", "inspect", "nonexistent"),
45+
// FIXME: where is this error even comin from?
46+
Expected: test.Expects(1, []error{errors.New("no network found matching")}, nil),
47+
},
48+
{
49+
Description: "invalid name network",
50+
Command: test.RunCommand("network", "inspect", "∞"),
51+
// FIXME: this is not even a valid identifier
52+
Expected: test.Expects(1, []error{errors.New("no network found matching")}, nil),
53+
},
54+
{
55+
Description: "none",
56+
Require: nerdtest.NerdctlNeedsFixing,
57+
Command: test.RunCommand("network", "inspect", "none"),
58+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
59+
var dc []dockercompat.Network
60+
err := json.Unmarshal([]byte(stdout), &dc)
61+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
62+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
63+
assert.Equal(t, dc[0].Name, "none")
64+
}),
65+
},
66+
{
67+
Description: "host",
68+
Require: nerdtest.NerdctlNeedsFixing,
69+
Command: test.RunCommand("network", "inspect", "host"),
70+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
71+
var dc []dockercompat.Network
72+
err := json.Unmarshal([]byte(stdout), &dc)
73+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
74+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
75+
assert.Equal(t, dc[0].Name, "host")
76+
}),
77+
},
78+
{
79+
Description: "bridge",
80+
Require: test.Not(test.Windows),
81+
Command: test.RunCommand("network", "inspect", "bridge"),
82+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
83+
var dc []dockercompat.Network
84+
err := json.Unmarshal([]byte(stdout), &dc)
85+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
86+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
87+
assert.Equal(t, dc[0].Name, "bridge")
88+
}),
89+
},
90+
{
91+
Description: "custom",
92+
Setup: func(data test.Data, helpers test.Helpers) {
93+
helpers.Ensure("network", "create", "custom")
94+
},
95+
Cleanup: func(data test.Data, helpers test.Helpers) {
96+
helpers.Anyhow("network", "remove", "custom")
97+
},
98+
Command: test.RunCommand("network", "inspect", "custom"),
99+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
100+
var dc []dockercompat.Network
101+
err := json.Unmarshal([]byte(stdout), &dc)
102+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
103+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
104+
assert.Equal(t, dc[0].Name, "custom")
105+
}),
106+
},
107+
{
108+
Description: "match exact id",
109+
Require: test.Not(test.Windows),
110+
Command: func(data test.Data, helpers test.Helpers) test.Command {
111+
id := strings.TrimSpace(helpers.Capture("network", "inspect", "bridge", "--format", "{{ .Id }}"))
112+
return helpers.Command("network", "inspect", id)
113+
},
114+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
115+
var dc []dockercompat.Network
116+
err := json.Unmarshal([]byte(stdout), &dc)
117+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
118+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
119+
assert.Equal(t, dc[0].Name, "bridge")
120+
}),
121+
},
122+
{
123+
Description: "match part of id",
124+
Require: test.Not(test.Windows),
125+
Command: func(data test.Data, helpers test.Helpers) test.Command {
126+
id := strings.TrimSpace(helpers.Capture("network", "inspect", "bridge", "--format", "{{ .Id }}"))
127+
return helpers.Command("network", "inspect", id[0:25])
128+
},
129+
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
130+
var dc []dockercompat.Network
131+
err := json.Unmarshal([]byte(stdout), &dc)
132+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
133+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
134+
assert.Equal(t, dc[0].Name, "bridge")
135+
}),
136+
},
137+
{
138+
Description: "using another net short id",
139+
Require: test.Not(test.Windows),
140+
Setup: func(data test.Data, helpers test.Helpers) {
141+
id := strings.TrimSpace(helpers.Capture("network", "inspect", "bridge", "--format", "{{ .Id }}"))
142+
helpers.Ensure("network", "create", id[0:12])
143+
data.Set("netname", id[0:12])
144+
},
145+
Cleanup: func(data test.Data, helpers test.Helpers) {
146+
id := strings.TrimSpace(helpers.Capture("network", "inspect", "bridge", "--format", "{{ .Id }}"))
147+
helpers.Anyhow("network", "remove", id[0:12])
148+
},
149+
Command: func(data test.Data, helpers test.Helpers) test.Command {
150+
return helpers.Command("network", "inspect", data.Get("netname"))
151+
},
152+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
153+
return &test.Expected{
154+
Output: func(stdout string, info string, t *testing.T) {
155+
var dc []dockercompat.Network
156+
err := json.Unmarshal([]byte(stdout), &dc)
157+
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
158+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
159+
assert.Equal(t, dc[0].Name, data.Get("netname"))
160+
},
161+
}
162+
},
163+
},
41164
{
42165
Description: "Test network inspect",
43166
// IPAMConfig is not implemented on Windows yet
@@ -88,16 +211,16 @@ func TestNetworkInspect(t *testing.T) {
88211
return &test.Expected{
89212
ExitCode: 0,
90213
Output: func(stdout string, info string, t *testing.T) {
91-
cmd := helpers.Command().Clear().WithBinary("nerdctl").WithArgs("--namespace", data.Identifier())
214+
cmd := helpers.CustomCommand("nerdctl", "--namespace", data.Identifier())
92215

93216
cmd.Clone().WithArgs("network", "inspect", data.Identifier()).Run(&test.Expected{
94217
ExitCode: 1,
95-
Errors: []error{errors.New("no such network")},
218+
Errors: []error{errors.New("no network found")},
96219
})
97220

98221
cmd.Clone().WithArgs("network", "remove", data.Identifier()).Run(&test.Expected{
99222
ExitCode: 1,
100-
Errors: []error{errors.New("no such network")},
223+
Errors: []error{errors.New("no network found")},
101224
})
102225

103226
cmd.Clone().WithArgs("network", "ls").Run(&test.Expected{

pkg/cmd/container/kill.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,12 @@ func cleanupNetwork(ctx context.Context, container containerd.Container, globalO
161161
cniOpts := []gocni.Opt{
162162
gocni.WithPluginDir([]string{globalOpts.CNIPath}),
163163
}
164-
netMap, err := e.NetworkMap()
165-
if err != nil {
166-
return err
167-
}
164+
var netw *netutil.NetworkConfig
168165
for _, netstr := range networks {
169-
net, ok := netMap[netstr]
170-
if !ok {
171-
return fmt.Errorf("no such network: %q", netstr)
166+
if netw, err = e.NetworkByNameOrID(netstr); err != nil {
167+
return err
172168
}
173-
cniOpts = append(cniOpts, gocni.WithConfListBytes(net.Bytes))
169+
cniOpts = append(cniOpts, gocni.WithConfListBytes(netw.Bytes))
174170
}
175171
cni, err := gocni.New(cniOpts...)
176172
if err != nil {

pkg/cmd/network/inspect.go

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,62 +19,71 @@ package network
1919
import (
2020
"context"
2121
"encoding/json"
22+
"errors"
2223
"fmt"
2324

2425
"github.com/containerd/log"
2526

2627
"github.com/containerd/nerdctl/v2/pkg/api/types"
2728
"github.com/containerd/nerdctl/v2/pkg/formatter"
28-
"github.com/containerd/nerdctl/v2/pkg/idutil/netwalker"
2929
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
3030
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
3131
"github.com/containerd/nerdctl/v2/pkg/netutil"
3232
)
3333

3434
func Inspect(ctx context.Context, options types.NetworkInspectOptions) error {
35-
globalOptions := options.GOptions
36-
e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
35+
if options.Mode != "native" && options.Mode != "dockercompat" {
36+
return fmt.Errorf("unknown mode %q", options.Mode)
37+
}
3738

39+
cniEnv, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
3840
if err != nil {
3941
return err
4042
}
41-
if options.Mode != "native" && options.Mode != "dockercompat" {
42-
return fmt.Errorf("unknown mode %q", options.Mode)
43-
}
4443

4544
var result []interface{}
46-
walker := netwalker.NetworkWalker{
47-
Client: e,
48-
OnFound: func(ctx context.Context, found netwalker.Found) error {
49-
if found.MatchCount > 1 {
50-
return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req)
51-
}
52-
r := &native.Network{
53-
CNI: json.RawMessage(found.Network.Bytes),
54-
NerdctlID: found.Network.NerdctlID,
55-
NerdctlLabels: found.Network.NerdctlLabels,
56-
File: found.Network.File,
57-
}
58-
switch options.Mode {
59-
case "native":
60-
result = append(result, r)
61-
case "dockercompat":
62-
compat, err := dockercompat.NetworkFromNative(r)
63-
if err != nil {
64-
return err
65-
}
66-
result = append(result, compat)
45+
netLists, errs := cniEnv.ListNetworksMatch(options.Networks, true)
46+
47+
for req, netList := range netLists {
48+
if len(netList) > 1 {
49+
errs = append(errs, fmt.Errorf("multiple IDs found with provided prefix: %s", req))
50+
continue
51+
}
52+
if len(netList) == 0 {
53+
errs = append(errs, fmt.Errorf("no network found matching: %s", req))
54+
continue
55+
}
56+
network := netList[0]
57+
r := &native.Network{
58+
CNI: json.RawMessage(network.Bytes),
59+
NerdctlID: network.NerdctlID,
60+
NerdctlLabels: network.NerdctlLabels,
61+
File: network.File,
62+
}
63+
switch options.Mode {
64+
case "native":
65+
result = append(result, r)
66+
case "dockercompat":
67+
compat, err := dockercompat.NetworkFromNative(r)
68+
if err != nil {
69+
return err
6770
}
68-
return nil
69-
},
71+
result = append(result, compat)
72+
}
7073
}
7174

72-
// `network inspect` doesn't support pseudo network.
73-
err = walker.WalkAll(ctx, options.Networks, true, false)
7475
if len(result) > 0 {
7576
if formatErr := formatter.FormatSlice(options.Format, options.Stdout, result); formatErr != nil {
7677
log.G(ctx).Error(formatErr)
7778
}
79+
err = nil
80+
} else {
81+
err = errors.New("unable to find any network matching the provided request")
82+
}
83+
84+
for _, unErr := range errs {
85+
log.G(ctx).Error(unErr)
7886
}
87+
7988
return err
8089
}

pkg/cmd/network/remove.go

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ package network
1818

1919
import (
2020
"context"
21+
"errors"
2122
"fmt"
2223

2324
containerd "github.com/containerd/containerd/v2/client"
25+
"github.com/containerd/log"
2426

2527
"github.com/containerd/nerdctl/v2/pkg/api/types"
26-
"github.com/containerd/nerdctl/v2/pkg/idutil/netwalker"
2728
"github.com/containerd/nerdctl/v2/pkg/netutil"
2829
)
2930

3031
func Remove(ctx context.Context, client *containerd.Client, options types.NetworkRemoveOptions) error {
31-
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
32+
cniEnv, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
3233
if err != nil {
3334
return err
3435
}
@@ -38,28 +39,52 @@ func Remove(ctx context.Context, client *containerd.Client, options types.Networ
3839
return err
3940
}
4041

41-
walker := netwalker.NetworkWalker{
42-
Client: e,
43-
OnFound: func(ctx context.Context, found netwalker.Found) error {
44-
if found.MatchCount > 1 {
45-
return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req)
46-
}
47-
if value, ok := usedNetworkInfo[found.Network.Name]; ok {
48-
return fmt.Errorf("network %q is in use by container %q", found.Req, value)
49-
}
50-
if found.Network.NerdctlID == nil {
51-
return fmt.Errorf("%s is managed outside nerdctl and cannot be removed", found.Req)
52-
}
53-
if found.Network.File == "" {
54-
return fmt.Errorf("%s is a pre-defined network and cannot be removed", found.Req)
55-
}
56-
if err := e.RemoveNetwork(found.Network); err != nil {
57-
return err
58-
}
59-
fmt.Fprintln(options.Stdout, found.Req)
60-
return nil
61-
},
42+
var result []string
43+
netLists, errs := cniEnv.ListNetworksMatch(options.Networks, false)
44+
45+
for req, netList := range netLists {
46+
if len(netList) > 1 {
47+
errs = append(errs, fmt.Errorf("multiple IDs found with provided prefix: %s", req))
48+
continue
49+
}
50+
if len(netList) == 0 {
51+
errs = append(errs, fmt.Errorf("no network found matching: %s", req))
52+
continue
53+
}
54+
network := netList[0]
55+
if value, ok := usedNetworkInfo[network.Name]; ok {
56+
errs = append(errs, fmt.Errorf("network %q is in use by container %q", req, value))
57+
continue
58+
}
59+
if network.Name == "bridge" {
60+
errs = append(errs, errors.New("cannot remove pre-defined network bridge"))
61+
continue
62+
}
63+
if network.File == "" {
64+
errs = append(errs, fmt.Errorf("%s is a pre-defined network and cannot be removed", req))
65+
continue
66+
}
67+
if network.NerdctlID == nil {
68+
errs = append(errs, fmt.Errorf("%s is managed outside nerdctl and cannot be removed", req))
69+
continue
70+
}
71+
if err := cniEnv.RemoveNetwork(network); err != nil {
72+
errs = append(errs, err)
73+
} else {
74+
result = append(result, req)
75+
}
76+
}
77+
for _, unErr := range errs {
78+
log.G(ctx).Error(unErr)
79+
}
80+
if len(result) > 0 {
81+
for _, id := range result {
82+
fmt.Fprintln(options.Stdout, id)
83+
}
84+
err = nil
85+
} else {
86+
err = errors.New("no network could be removed")
6287
}
6388

64-
return walker.WalkAll(ctx, options.Networks, true, false)
89+
return err
6590
}

0 commit comments

Comments
 (0)