Skip to content

Commit 46c60c5

Browse files
committed
initial work to support dynamic cni configurations
Signed-off-by: Michael Zappa <michael.zappa@gmail.com>
1 parent 6603d5b commit 46c60c5

2 files changed

Lines changed: 128 additions & 1 deletion

File tree

cni.go

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@ type CNI interface {
3535
Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
3636
// SetupSerially sets up each of the network interfaces for the namespace in serial
3737
SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
38+
//SetupNetworks sets up a list of networks
39+
SetupNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) (*Result, error)
3840
// Remove tears down the network of the namespace.
3941
Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
42+
// RemoveNetworks tears down a list of provided networks
43+
RemoveNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) error
4044
// Check checks if the network is still in desired state
4145
Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
4246
// Load loads the cni network config
@@ -45,6 +49,8 @@ type CNI interface {
4549
Status() error
4650
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
4751
GetConfig() *ConfigResult
52+
//BuildMultiNetwork returns a list of networks that match the network name in the cni cache
53+
BuildMultiNetwork(networkNames []*NetworkInterface) []*Network
4854
}
4955

5056
type ConfigResult struct {
@@ -83,6 +89,11 @@ type libcni struct {
8389
sync.RWMutex
8490
}
8591

92+
type NetworkInterface struct {
93+
NetworkName string
94+
InterfaceName string
95+
}
96+
8697
func defaultCNIConfig() *libcni {
8798
return &libcni{
8899
config: config{
@@ -183,15 +194,62 @@ func (c *libcni) SetupSerially(ctx context.Context, id string, path string, opts
183194
return c.createResult(result)
184195
}
185196

197+
// BuildMultiNetwork build dynamic list of Networks. Order Matters here!
198+
func (c *libcni) BuildMultiNetwork(networkNames []*NetworkInterface) []*Network {
199+
var network []*Network
200+
ifaceindex := 0
201+
for _, net := range c.Networks() {
202+
for _, x := range networkNames {
203+
if net.config.Name == x.NetworkName {
204+
if x.InterfaceName == "" {
205+
net.ifName = getIfName(c.prefix, ifaceindex)
206+
} else {
207+
net.ifName = x.InterfaceName
208+
}
209+
//Doing this to ensure first selected cni conf ifname is eth0
210+
if net.ifName != "lo" {
211+
ifaceindex++
212+
}
213+
214+
//TODO - Add logic to make sure interface collisions don't happen. However that could be an implementation detail.
215+
network = append(network, net)
216+
}
217+
}
218+
}
219+
220+
return network
221+
}
222+
223+
// SetupSerially setups specific networks in the namespace and returns a Result
224+
func (c *libcni) SetupNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) (*Result, error) {
225+
if err := c.Status(); err != nil {
226+
return nil, err
227+
}
228+
ns, err := newNamespace(id, path, opts...)
229+
if err != nil {
230+
return nil, err
231+
}
232+
result, err := c.attachWithMultipleNetworksSerially(ctx, ns, networks)
233+
if err != nil {
234+
return nil, err
235+
}
236+
return c.createResult(result)
237+
}
238+
186239
func (c *libcni) attachNetworksSerially(ctx context.Context, ns *Namespace) ([]*types100.Result, error) {
240+
return c.attachWithMultipleNetworksSerially(ctx, ns, c.Networks())
241+
}
242+
243+
func (c *libcni) attachWithMultipleNetworksSerially(ctx context.Context, ns *Namespace, networks []*Network) ([]*types100.Result, error) {
187244
var results []*types100.Result
188-
for _, network := range c.Networks() {
245+
for _, network := range networks {
189246
r, err := network.Attach(ctx, ns)
190247
if err != nil {
191248
return nil, err
192249
}
193250
results = append(results, r)
194251
}
252+
195253
return results, nil
196254
}
197255

@@ -258,6 +316,33 @@ func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...Nam
258316
return nil
259317
}
260318

319+
func (c *libcni) RemoveNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) error {
320+
if err := c.Status(); err != nil {
321+
return err
322+
}
323+
ns, err := newNamespace(id, path, opts...)
324+
if err != nil {
325+
return err
326+
}
327+
for i := len(networks) - 1; i >= 0; i-- {
328+
if err := networks[i].Remove(ctx, ns); err != nil {
329+
// Based on CNI spec v0.7.0, empty network namespace is allowed to
330+
// do best effort cleanup. However, it is not handled consistently
331+
// right now:
332+
// https://github.com/containernetworking/plugins/issues/210
333+
// TODO(random-liu): Remove the error handling when the issue is
334+
// fixed and the CNI spec v0.6.0 support is deprecated.
335+
// NOTE(claudiub): Some CNIs could return a "not found" error, which could mean that
336+
// it was already deleted.
337+
if (path == "" && strings.Contains(err.Error(), "no such file or directory")) || strings.Contains(err.Error(), "not found") {
338+
continue
339+
}
340+
return err
341+
}
342+
}
343+
return nil
344+
}
345+
261346
// Check checks if the network is still in desired state
262347
func (c *libcni) Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
263348
if err := c.Status(); err != nil {

cni_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,48 @@ func TestLibCNIType100(t *testing.T) {
285285
assert.NoError(t, err)
286286
}
287287

288+
func TestBuildNetworksWithAnnotation(t *testing.T) {
289+
// Get the default CNI config
290+
l := defaultCNIConfig()
291+
// Create a fake cni config directory and file
292+
cniDir, confDir := makeFakeCNIConfig(t)
293+
defer tearDownCNIConfig(t, cniDir)
294+
l.pluginConfDir = confDir
295+
// Set the minimum network count as 2 for this test
296+
l.networkCount = 2
297+
err := l.Load(WithLoNetwork, WithAllConf)
298+
assert.NoError(t, err)
299+
300+
err = l.Status()
301+
assert.NoError(t, err)
302+
303+
net := []*NetworkInterface{
304+
{
305+
NetworkName: "plugin2",
306+
InterfaceName: "net1",
307+
},
308+
{
309+
NetworkName: "cni-loopback",
310+
InterfaceName: "lo",
311+
},
312+
{
313+
NetworkName: "plugin1",
314+
//InterfaceName: "This should be commented out"
315+
},
316+
}
317+
318+
networks := l.BuildMultiNetwork(net)
319+
320+
assert.Equal(t, len(networks), 3)
321+
322+
assert.Equal(t, "lo", networks[0].ifName)
323+
assert.Equal(t, "cni-loopback", networks[0].config.Name)
324+
assert.Equal(t, "eth0", networks[1].ifName)
325+
assert.Equal(t, "plugin1", networks[1].config.Name)
326+
assert.Equal(t, "net1", networks[2].ifName)
327+
assert.Equal(t, "plugin2", networks[2].config.Name)
328+
}
329+
288330
type MockCNI struct {
289331
mock.Mock
290332
}

0 commit comments

Comments
 (0)