Skip to content

Commit

Permalink
support CNI status verb
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Zappa <[email protected]>
  • Loading branch information
Michael Zappa authored and MikeZappa87 committed Nov 26, 2024
1 parent 9fbd439 commit 5762dbb
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 38 deletions.
38 changes: 32 additions & 6 deletions cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,19 @@ func (c *libcni) Load(opts ...Opt) error {
func (c *libcni) Status() error {
c.RLock()
defer c.RUnlock()
if len(c.networks) < c.networkCount {
return ErrCNINotInitialized
if err := c.ready(); err != nil {
return err
}

// STATUS is only called for CNI Version 1.1.0 or greater. It is ignored for previous versions.
for _, v := range c.networks {
err := c.cniConfig.GetStatusNetworkList(context.Background(), v.config)

if err != nil {
return err
}
}

return nil
}

Expand All @@ -153,9 +163,11 @@ func (c *libcni) Networks() []*Network {

// Setup setups the network in the namespace and returns a Result
func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
if err := c.Status(); err != nil {
if err := c.ready(); err != nil {
return nil, err
}
c.RLock()
defer c.RUnlock()
ns, err := newNamespace(id, path, opts...)
if err != nil {
return nil, err
Expand All @@ -169,9 +181,11 @@ func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...Name

// SetupSerially setups the network in the namespace and returns a Result
func (c *libcni) SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
if err := c.Status(); err != nil {
if err := c.ready(); err != nil {
return nil, err
}
c.RLock()
defer c.RUnlock()
ns, err := newNamespace(id, path, opts...)
if err != nil {
return nil, err
Expand Down Expand Up @@ -232,9 +246,11 @@ func (c *libcni) attachNetworks(ctx context.Context, ns *Namespace) ([]*types100

// Remove removes the network config from the namespace
func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
if err := c.Status(); err != nil {
if err := c.ready(); err != nil {
return err
}
c.RLock()
defer c.RUnlock()
ns, err := newNamespace(id, path, opts...)
if err != nil {
return err
Expand All @@ -260,9 +276,11 @@ func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...Nam

// 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 {
if err := c.ready(); err != nil {
return err
}
c.RLock()
defer c.RUnlock()
ns, err := newNamespace(id, path, opts...)
if err != nil {
return err
Expand Down Expand Up @@ -310,3 +328,11 @@ func (c *libcni) GetConfig() *ConfigResult {
func (c *libcni) reset() {
c.networks = nil
}

func (c *libcni) ready() error {
if len(c.networks) < c.networkCount {
return ErrCNINotInitialized
}

return nil
}
93 changes: 61 additions & 32 deletions cni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cni

import (
"context"
"errors"
"net"
"testing"

Expand Down Expand Up @@ -292,21 +293,19 @@ func TestLibCNIType120(t *testing.T) {
// Get the default CNI config
l := defaultCNIConfig()
// Create a fake cni config directory and file
cniDir, confDir := makeFakeCNIConfig(t)
cniDir, confDir := buildFakeConfig(t)
defer tearDownCNIConfig(t, cniDir)
l.pluginConfDir = confDir
// Set the minimum network count as 2 for this test
l.networkCount = 2
err := l.Load(WithAllConf)
assert.NoError(t, err)

err = l.Status()
err := l.Load(WithLoNetwork, WithDefaultConf)
assert.NoError(t, err)

mockCNI := &MockCNI{}
l.cniConfig = mockCNI
l.networks[0].cni = mockCNI
l.networks[1].cni = mockCNI
ipv4, err := types.ParseCIDR("10.0.0.1/24")
ipv4, err := types.ParseCIDR("10.88.0.1/16")
assert.NoError(t, err)
expectedRT := &cnilibrary.RuntimeConf{
ContainerID: "container-id1",
Expand All @@ -315,59 +314,65 @@ func TestLibCNIType120(t *testing.T) {
Args: [][2]string(nil),
CapabilityArgs: map[string]interface{}{},
}
mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types100.Result{
CNIVersion: "1.1.0",
Interfaces: []*types100.Interface{

loRT := &cnilibrary.RuntimeConf{
ContainerID: "container-id1",
NetNS: "/proc/12345/ns/net",
IfName: "lo",
Args: [][2]string(nil),
CapabilityArgs: map[string]interface{}{},
}

// mock for loopback
mockCNI.On("GetStatusNetworkList", l.networks[0].config).Return(nil)
mockCNI.On("AddNetworkList", l.networks[0].config, loRT).Return(&types040.Result{
CNIVersion: "0.3.1",
Interfaces: []*types040.Interface{
{
Name: "eth0",
Name: "lo",
},
},
IPs: []*types100.IPConfig{
IPs: []*types040.IPConfig{
{
Interface: types100.Int(0),
Address: *ipv4,
Gateway: net.ParseIP("10.0.0.255"),
Interface: types040.Int(0),
Address: net.IPNet{
IP: net.ParseIP("127.0.0.1"),
},
},
},
}, nil)
mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)
mockCNI.On("CheckNetworkList", l.networks[0].config, expectedRT).Return(nil)
ipv4, err = types.ParseCIDR("10.0.0.2/24")
assert.NoError(t, err)
l.networks[1].cni = mockCNI
expectedRT = &cnilibrary.RuntimeConf{
ContainerID: "container-id1",
NetNS: "/proc/12345/ns/net",
IfName: "eth1",
Args: [][2]string(nil),
CapabilityArgs: map[string]interface{}{},
}
mockCNI.On("DelNetworkList", l.networks[0].config, loRT).Return(nil)
mockCNI.On("CheckNetworkList", l.networks[0].config, loRT).Return(nil)

// mock for primary cni
mockCNI.On("GetStatusNetworkList", l.networks[1].config).Return(nil)
mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types100.Result{
CNIVersion: "1.1.0",
Interfaces: []*types100.Interface{
{
Name: "eth1",
Name: "eth0",
},
},
IPs: []*types100.IPConfig{
{
Interface: types100.Int(0),
Address: *ipv4,
Gateway: net.ParseIP("10.0.0.2"),
Gateway: net.ParseIP("10.88.0.1"),
},
},
}, nil)
mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)
mockCNI.On("CheckNetworkList", l.networks[1].config, expectedRT).Return(nil)
ctx := context.Background()

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

r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
assert.NoError(t, err)
assert.Contains(t, r.Interfaces, "eth0")
assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
assert.Contains(t, r.Interfaces, "eth1")
assert.NotNil(t, r.Interfaces["eth1"].IPConfigs)
assert.Equal(t, r.Interfaces["eth1"].IPConfigs[0].IP.String(), "10.0.0.2")
assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.88.0.1")

err = l.Check(ctx, "container-id1", "/proc/12345/ns/net")
assert.NoError(t, err)
Expand All @@ -376,6 +381,30 @@ func TestLibCNIType120(t *testing.T) {
assert.NoError(t, err)
}

func TestLibCNIType120FailStatus(t *testing.T) {
// Get the default CNI config
l := defaultCNIConfig()
// Create a fake cni config directory and file
cniDir, confDir := buildFakeConfig(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, WithDefaultConf)
assert.NoError(t, err)

mockCNI := &MockCNI{}
l.cniConfig = mockCNI
l.networks[0].cni = mockCNI
l.networks[1].cni = mockCNI

mockCNI.On("GetStatusNetworkList", l.networks[0].config).Return(nil)
mockCNI.On("GetStatusNetworkList", l.networks[1].config).Return(errors.New("no ip addresses"))
l.cniConfig = mockCNI
err = l.Status()
assert.Error(t, err)
}

type MockCNI struct {
mock.Mock
}
Expand Down
61 changes: 61 additions & 0 deletions testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,64 @@ func tearDownCNIConfig(t *testing.T, confDir string) {
t.Fatalf("Failed to cleanup CNI configs: %v", err)
}
}

func buildFakeConfig(t *testing.T) (string, string) {
conf := `
{
"cniVersion": "1.1.0",
"name": "containerd-net",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"promiscMode": true,
"ipam": {
"type": "host-ipam",
"ranges": [
[{
"subnet": "10.88.0.0/16"
}],
[{
"subnet": "2001:4860:4860::/64"
}]
],
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "::/0" }
]
}
},
{
"type": "portmap",
"capabilities": {"portMappings": true}
}
]
}`

cniDir, err := makeTmpDir("fakecni")
if err != nil {
t.Fatalf("Failed to create plugin config dir: %v", err)
}

cniConfDir := path.Join(cniDir, "net.d")
err = os.MkdirAll(cniConfDir, 0777)
if err != nil {
t.Fatalf("Failed to create network config dir: %v", err)
}

networkConfig1 := path.Join(cniConfDir, "mocknetwork1.conflist")
f1, err := os.Create(networkConfig1)
if err != nil {
t.Fatalf("Failed to create network config %v: %v", f1, err)
}

_, err = f1.WriteString(conf)
if err != nil {
t.Fatalf("Failed to write network config file %v: %v", f1, err)
}
f1.Close()

return cniDir, cniConfDir
}

0 comments on commit 5762dbb

Please sign in to comment.