@@ -23,6 +23,7 @@ import (
23
23
"github.com/containernetworking/cni/pkg/version"
24
24
"github.com/containernetworking/plugins/pkg/ns"
25
25
"github.com/containernetworking/plugins/pkg/utils/buildversion"
26
+ "github.com/hashicorp/go-multierror"
26
27
"github.com/pkg/errors"
27
28
28
29
"github.com/firecracker-microvm/firecracker-go-sdk/cni/internal"
@@ -42,17 +43,20 @@ func add(args *skel.CmdArgs) error {
42
43
return err
43
44
}
44
45
45
- currentResult , err := getCurrentResult (args )
46
- if err != nil {
47
- return err
46
+ if p .netNS == nil {
47
+ return errors .Errorf ("netns path %q does not exist" , args .Netns )
48
48
}
49
49
50
- err = p .add (currentResult )
50
+ if p .currentResult == nil {
51
+ return & NoPreviousResultError {}
52
+ }
53
+
54
+ err = p .add ()
51
55
if err != nil {
52
56
return err
53
57
}
54
58
55
- return types .PrintResult (currentResult , currentResult .CNIVersion )
59
+ return types .PrintResult (p . currentResult , p . currentResult .CNIVersion )
56
60
}
57
61
58
62
func del (args * skel.CmdArgs ) error {
@@ -61,6 +65,12 @@ func del(args *skel.CmdArgs) error {
61
65
return err
62
66
}
63
67
68
+ if p .netNS == nil {
69
+ // the network namespace is already gone and everything we create
70
+ // is inside the netns, so nothing to do here.
71
+ return nil
72
+ }
73
+
64
74
return p .del ()
65
75
}
66
76
@@ -70,42 +80,42 @@ func check(args *skel.CmdArgs) error {
70
80
return err
71
81
}
72
82
73
- return p .check ()
74
- }
75
-
76
- func getCurrentResult (args * skel.CmdArgs ) (* current.Result , error ) {
77
- // parse the previous CNI result (or throw an error if there wasn't one)
78
- cniConf := types.NetConf {}
79
- err := json .Unmarshal (args .StdinData , & cniConf )
80
- if err != nil {
81
- return nil , errors .Wrap (err , "failure checking for previous result output" )
82
- }
83
-
84
- err = version .ParsePrevResult (& cniConf )
85
- if err != nil {
86
- return nil , errors .Wrap (err , "failed to parse previous CNI result" )
87
- }
88
-
89
- if cniConf .PrevResult == nil {
90
- return nil , errors .New ("no previous result was found, was this plugin chained with a previous one?" )
83
+ if p .netNS == nil {
84
+ return errors .Errorf ("netns path %q does not exist" , args .Netns )
91
85
}
92
86
93
- currentResult , err := current .NewResultFromResult (cniConf .PrevResult )
94
- if err != nil {
95
- return nil , errors .Wrap (err , "failed to generate current result from previous CNI result" )
87
+ if p .currentResult == nil {
88
+ return & NoPreviousResultError {}
96
89
}
97
90
98
- return currentResult , nil
91
+ return p . check ()
99
92
}
100
93
101
94
func newPlugin (args * skel.CmdArgs ) (* plugin , error ) {
95
+ if args .IfName == "" {
96
+ return nil , errors .New ("no device to redirect with was found, was IfName specified?" )
97
+ }
98
+
102
99
netNS , err := ns .GetNS (args .Netns )
103
100
if err != nil {
104
- return nil , errors .Wrapf (err , "failed to open netns at path %q" , args .Netns )
101
+ // It's valid for the netns to no longer exist during DEL commands (in which case DEL is
102
+ // a noop). Thus, we leave validating that netNS is not nil to command implementations.
103
+ switch err .(type ) {
104
+ case * ns.NSPathNotExistErr :
105
+ netNS = nil
106
+ default :
107
+ return nil , errors .Wrapf (err , "failed to open netns at path %q" , args .Netns )
108
+ }
105
109
}
106
110
107
- if args .IfName == "" {
108
- return nil , errors .New ("no device to redirect with was found, was IfName specified?" )
111
+ currentResult , err := getCurrentResult (args )
112
+ if err != nil {
113
+ switch err .(type ) {
114
+ case * NoPreviousResultError :
115
+ currentResult = nil
116
+ default :
117
+ return nil , errors .Wrapf (err , "failure parsing previous CNI result" )
118
+ }
109
119
}
110
120
111
121
return & plugin {
@@ -123,9 +133,37 @@ func newPlugin(args *skel.CmdArgs) (*plugin, error) {
123
133
redirectInterfaceName : args .IfName ,
124
134
125
135
netNS : netNS ,
136
+
137
+ currentResult : currentResult ,
126
138
}, nil
127
139
}
128
140
141
+ func getCurrentResult (args * skel.CmdArgs ) (* current.Result , error ) {
142
+ // parse the previous CNI result (or throw an error if there wasn't one)
143
+ cniConf := types.NetConf {}
144
+ err := json .Unmarshal (args .StdinData , & cniConf )
145
+ if err != nil {
146
+ return nil , errors .Wrap (err , "failure checking for previous result output" )
147
+ }
148
+
149
+ err = version .ParsePrevResult (& cniConf )
150
+ if err != nil {
151
+ return nil , errors .Wrap (err , "failed to parse previous CNI result" )
152
+ }
153
+
154
+ if cniConf .PrevResult == nil {
155
+ return nil , & NoPreviousResultError {}
156
+ }
157
+
158
+ currentResult , err := current .NewResultFromResult (cniConf .PrevResult )
159
+ if err != nil {
160
+ return nil , errors .Wrap (err ,
161
+ "failed to generate current result from previous CNI result" )
162
+ }
163
+
164
+ return currentResult , nil
165
+ }
166
+
129
167
type plugin struct {
130
168
internal.NetlinkOps
131
169
@@ -152,18 +190,25 @@ type plugin struct {
152
190
// netNS is the network namespace in which the redirectIface exists and thus in which
153
191
// the tap device will be created too
154
192
netNS ns.NetNS
193
+
194
+ // currentResult is the CNI result object, initialized to the previous CNI
195
+ // result if there was any or to nil if there was no previous result provided
196
+ currentResult * current.Result
155
197
}
156
198
157
- func (p plugin ) add (currentResult * current. Result ) error {
199
+ func (p plugin ) add () error {
158
200
return p .netNS .Do (func (_ ns.NetNS ) error {
159
201
redirectLink , err := p .GetLink (p .redirectInterfaceName )
160
202
if err != nil {
161
- return errors .Wrapf (err , "failed to find redirect interface %q" , p .redirectInterfaceName )
203
+ return errors .Wrapf (err ,
204
+ "failed to find redirect interface %q" , p .redirectInterfaceName )
162
205
}
163
206
164
- redirectIPs := internal .InterfaceIPs (currentResult , redirectLink .Attrs ().Name , p .netNS .Path ())
207
+ redirectIPs := internal .InterfaceIPs (
208
+ p .currentResult , redirectLink .Attrs ().Name , p .netNS .Path ())
165
209
if len (redirectIPs ) != 1 {
166
- return errors .Errorf ("expected to find 1 IP on redirect interface %q, but instead found %+v" ,
210
+ return errors .Errorf (
211
+ "expected to find 1 IP on redirect interface %q, but instead found %+v" ,
167
212
redirectLink .Attrs ().Name , redirectIPs )
168
213
}
169
214
redirectIP := redirectIPs [0 ]
@@ -194,7 +239,7 @@ func (p plugin) add(currentResult *current.Result) error {
194
239
}
195
240
196
241
// Add the tap device to our results
197
- currentResult .Interfaces = append (currentResult .Interfaces , & current.Interface {
242
+ p . currentResult .Interfaces = append (p . currentResult .Interfaces , & current.Interface {
198
243
Name : tapLink .Attrs ().Name ,
199
244
Sandbox : p .netNS .Path (),
200
245
Mac : tapLink .Attrs ().HardwareAddr .String (),
@@ -208,16 +253,16 @@ func (p plugin) add(currentResult *current.Result) error {
208
253
// differentiate from the tap and associate it with the VM.
209
254
//
210
255
// See the `vmconf` package's docstring for the definition of this interface
211
- currentResult .Interfaces = append (currentResult .Interfaces , & current.Interface {
256
+ p . currentResult .Interfaces = append (p . currentResult .Interfaces , & current.Interface {
212
257
Name : tapLink .Attrs ().Name ,
213
258
Sandbox : p .vmID ,
214
259
Mac : redirectLink .Attrs ().HardwareAddr .String (),
215
260
})
216
- vmIfaceIndex := len (currentResult .Interfaces ) - 1
261
+ vmIfaceIndex := len (p . currentResult .Interfaces ) - 1
217
262
218
263
// Add the IP configuration that should be applied to the VM internally by
219
264
// associating the IPConfig with the vmIface. We use the redirectIface's IP.
220
- currentResult .IPs = append (currentResult .IPs , & current.IPConfig {
265
+ p . currentResult .IPs = append (p . currentResult .IPs , & current.IPConfig {
221
266
Version : redirectIP .Version ,
222
267
Address : redirectIP .Address ,
223
268
Gateway : redirectIP .Gateway ,
@@ -229,9 +274,105 @@ func (p plugin) add(currentResult *current.Result) error {
229
274
}
230
275
231
276
func (p plugin ) del () error {
232
- panic ("del is currently unimplemented" )
277
+ return p .netNS .Do (func (_ ns.NetNS ) error {
278
+ var multiErr * multierror.Error
279
+
280
+ // try to remove the qdisc we added from the redirect interface
281
+ redirectLink , err := p .GetLink (p .redirectInterfaceName )
282
+ switch err .(type ) {
283
+
284
+ case nil :
285
+ // the link exists, so try removing the qdisc
286
+ err := p .RemoveIngressQdisc (redirectLink )
287
+ switch err .(type ) {
288
+ case nil , * internal.QdiscNotFoundError :
289
+ // we removed successfully or there already wasn't a qdisc, nothing to do
290
+ default :
291
+ multiErr = multierror .Append (multiErr , errors .Wrapf (err ,
292
+ "failed to remove ingress qdisc from %q" , redirectLink .Attrs ().Name ))
293
+ }
294
+
295
+ case * internal.LinkNotFoundError :
296
+ // if the link doesn't exist, there's nothing to do
297
+
298
+ default :
299
+ multiErr = multierror .Append (multiErr ,
300
+ errors .Wrapf (err , "failure finding device %q" , p .redirectInterfaceName ))
301
+ }
302
+
303
+ // if there was no previous result, we can't find the vm-tap pair, so we are done here
304
+ if p .currentResult == nil {
305
+ return multiErr .ErrorOrNil ()
306
+ }
307
+
308
+ // try to remove the tap device we added
309
+ _ , tapIface , err := internal .VMTapPair (p .currentResult , p .vmID )
310
+ switch err .(type ) {
311
+
312
+ case nil :
313
+ err = p .RemoveLink (tapIface .Name )
314
+ switch err .(type ) {
315
+ case nil , * internal.LinkNotFoundError :
316
+ // we removed successfully or someone else beat us to removing it first
317
+ default :
318
+ multiErr = multierror .Append (multiErr , errors .Wrapf (err ,
319
+ "failure removing device %q" , tapIface .Name ))
320
+ }
321
+
322
+ case * internal.LinkNotFoundError :
323
+ // if the link doesn't exist, there's nothing to do
324
+
325
+ default :
326
+ multiErr = multierror .Append (multiErr , err )
327
+ }
328
+
329
+ return multiErr .ErrorOrNil ()
330
+ })
233
331
}
234
332
235
333
func (p plugin ) check () error {
236
- panic ("check is currently unimplemented" )
334
+ return p .netNS .Do (func (_ ns.NetNS ) error {
335
+ _ , tapIface , err := internal .VMTapPair (p .currentResult , p .vmID )
336
+ if err != nil {
337
+ return err
338
+ }
339
+
340
+ tapLink , err := p .GetLink (tapIface .Name )
341
+ if err != nil {
342
+ return err
343
+ }
344
+
345
+ redirectLink , err := p .GetLink (p .redirectInterfaceName )
346
+ if err != nil {
347
+ return err
348
+ }
349
+
350
+ _ , err = p .GetIngressQdisc (tapLink )
351
+ if err != nil {
352
+ return err
353
+ }
354
+
355
+ _ , err = p .GetIngressQdisc (redirectLink )
356
+ if err != nil {
357
+ return err
358
+ }
359
+
360
+ _ , err = p .GetRedirectFilter (tapLink , redirectLink )
361
+ if err != nil {
362
+ return err
363
+ }
364
+
365
+ _ , err = p .GetRedirectFilter (redirectLink , tapLink )
366
+ if err != nil {
367
+ return err
368
+ }
369
+
370
+ return nil
371
+ })
372
+ }
373
+
374
+ type NoPreviousResultError struct {}
375
+
376
+ func (e NoPreviousResultError ) Error () string {
377
+ return "no previous result was found, was this plugin chained with a previous one?"
237
378
}
0 commit comments