Skip to content

Commit

Permalink
initial work to support dynamic cni configurations
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Zappa <[email protected]>
  • Loading branch information
MikeZappa87 committed Aug 2, 2023
1 parent 6603d5b commit 196f34f
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 1 deletion.
87 changes: 86 additions & 1 deletion cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ type CNI interface {
Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
// SetupSerially sets up each of the network interfaces for the namespace in serial
SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
//SetupNetworks sets up a list of networks
SetupNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) (*Result, error)
// Remove tears down the network of the namespace.
Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
// RemoveNetworks tears down a list of provided networks
RemoveNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) error
// Check checks if the network is still in desired state
Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
// Load loads the cni network config
Expand All @@ -45,6 +49,8 @@ type CNI interface {
Status() error
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
GetConfig() *ConfigResult
//BuildCNIMultiNetwork returns a list of networks that match the network name in the cni cache
BuildCNIMultiNetwork(networkNames []*NetworkInterface) []*Network
}

type ConfigResult struct {
Expand Down Expand Up @@ -83,6 +89,11 @@ type libcni struct {
sync.RWMutex
}

type NetworkInterface struct {
NetworkName string
InterfaceName string
}

func defaultCNIConfig() *libcni {
return &libcni{
config: config{
Expand Down Expand Up @@ -183,15 +194,62 @@ func (c *libcni) SetupSerially(ctx context.Context, id string, path string, opts
return c.createResult(result)
}

// BuildCNIMultiNetwork build dynamic list of Networks. Order Matters here!
func (c *libcni) BuildCNIMultiNetwork(networkNames []*NetworkInterface) []*Network {
var network []*Network
ifaceindex := 0
for _, net := range c.Networks() {
for _, x := range networkNames {
if net.config.Name == x.NetworkName {
if x.InterfaceName == "" {
net.ifName = getIfName(c.prefix, ifaceindex)
} else {
net.ifName = x.InterfaceName
}
//Doing this to ensure first selected cni conf ifname is eth0
if net.ifName != "lo" {
ifaceindex++
}

//TODO - Add logic to make sure interface collisions don't happen. However that could be an implementation detail.
network = append(network, net)
}
}
}

return network
}

// SetupSerially setups specific networks in the namespace and returns a Result
func (c *libcni) SetupNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) (*Result, error) {
if err := c.Status(); err != nil {
return nil, err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return nil, err
}
result, err := c.attachWithMultipleNetworksSerially(ctx, ns, networks)
if err != nil {
return nil, err
}
return c.createResult(result)
}

func (c *libcni) attachNetworksSerially(ctx context.Context, ns *Namespace) ([]*types100.Result, error) {
return c.attachWithMultipleNetworksSerially(ctx, ns, c.Networks())
}

func (c *libcni) attachWithMultipleNetworksSerially(ctx context.Context, ns *Namespace, networks []*Network) ([]*types100.Result, error) {
var results []*types100.Result
for _, network := range c.Networks() {
for _, network := range networks {
r, err := network.Attach(ctx, ns)
if err != nil {
return nil, err
}
results = append(results, r)
}

return results, nil
}

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

func (c *libcni) RemoveNetworks(ctx context.Context, id string, path string, networks []*Network, opts ...NamespaceOpts) error {
if err := c.Status(); err != nil {
return err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return err
}
for i := len(networks) - 1; i >= 0; i-- {
if err := networks[i].Remove(ctx, ns); err != nil {
// Based on CNI spec v0.7.0, empty network namespace is allowed to
// do best effort cleanup. However, it is not handled consistently
// right now:
// https://github.com/containernetworking/plugins/issues/210
// TODO(random-liu): Remove the error handling when the issue is
// fixed and the CNI spec v0.6.0 support is deprecated.
// NOTE(claudiub): Some CNIs could return a "not found" error, which could mean that
// it was already deleted.
if (path == "" && strings.Contains(err.Error(), "no such file or directory")) || strings.Contains(err.Error(), "not found") {
continue
}
return err
}
}
return nil
}

// Check checks if the network is still in desired state
func (c *libcni) Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
if err := c.Status(); err != nil {
Expand Down
42 changes: 42 additions & 0 deletions cni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,48 @@ func TestLibCNIType100(t *testing.T) {
assert.NoError(t, err)
}

func TestBuildNetworksWithAnnotation(t *testing.T) {
// Get the default CNI config
l := defaultCNIConfig()
// Create a fake cni config directory and file
cniDir, confDir := makeFakeCNIConfig(t)
defer tearDownCNIConfig(t, cniDir)
l.pluginConfDir = confDir
// Set the minimum network count as 2 for this test
l.networkCount = 2
err := l.Load(WithLoNetwork, WithAllConf)
assert.NoError(t, err)

err = l.Status()
assert.NoError(t, err)

net := []*NetworkInterface{
{
NetworkName: "plugin2",
InterfaceName: "net1",
},
{
NetworkName: "cni-loopback",
InterfaceName: "lo",
},
{
NetworkName: "plugin1",
//InterfaceName: "This should be commented out"
},
}

networks := l.BuildCNIMultiNetwork(net)

assert.Equal(t, len(networks), 3)

assert.Equal(t, "lo", networks[0].ifName)
assert.Equal(t, "cni-loopback", networks[0].config.Name)
assert.Equal(t, "eth0", networks[1].ifName)
assert.Equal(t, "plugin1", networks[1].config.Name)
assert.Equal(t, "net1", networks[2].ifName)
assert.Equal(t, "plugin2", networks[2].config.Name)
}

type MockCNI struct {
mock.Mock
}
Expand Down

0 comments on commit 196f34f

Please sign in to comment.