move to libcni 0.7.0

Previous commit "Use ip address from CNI output" introduces
ability to run pod which can havn't eth0. But also it
add problem: after kubelet restart, if we have already started
pod w/o eth0, kubelet can't find proper interface (it's
normal for vhostuser type of cni plugin when eth0 doesn't exist)
and kubelet restarts "broken" pod.
Fix of this issue requeres new feature of libcni - caching
results.

Looks like new libcni requires cniVersion in CNI output.
This patch specifies version both for CNI conf and CNI output.

Signed-off-by: Alexey Perevalov <a.perevalov@samsung.com>
This commit is contained in:
Alexey Perevalov 2019-04-29 17:22:55 +03:00 committed by Dan Williams
parent 0e48ec31f3
commit a2ea2996f3
5 changed files with 63 additions and 25 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package cni package cni
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -326,7 +327,7 @@ func (plugin *cniNetworkPlugin) addToNetwork(network *cniNetwork, podName string
pdesc := podDesc(podNamespace, podName, podSandboxID) pdesc := podDesc(podNamespace, podName, podSandboxID)
netConf, cniNet := network.NetworkConfig, network.CNIConfig netConf, cniNet := network.NetworkConfig, network.CNIConfig
klog.V(4).Infof("Adding %s to network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath) klog.V(4).Infof("Adding %s to network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath)
res, err := cniNet.AddNetworkList(netConf, rt) res, err := cniNet.AddNetworkList(context.TODO(), netConf, rt)
if err != nil { if err != nil {
klog.Errorf("Error adding %s to network %s/%s: %v", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, err) klog.Errorf("Error adding %s to network %s/%s: %v", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, err)
return nil, err return nil, err
@ -345,7 +346,7 @@ func (plugin *cniNetworkPlugin) deleteFromNetwork(network *cniNetwork, podName s
pdesc := podDesc(podNamespace, podName, podSandboxID) pdesc := podDesc(podNamespace, podName, podSandboxID)
netConf, cniNet := network.NetworkConfig, network.CNIConfig netConf, cniNet := network.NetworkConfig, network.CNIConfig
klog.V(4).Infof("Deleting %s from network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath) klog.V(4).Infof("Deleting %s from network %s/%s netns %q", pdesc, netConf.Plugins[0].Network.Type, netConf.Name, podNetnsPath)
err = cniNet.DelNetworkList(netConf, rt) err = cniNet.DelNetworkList(context.TODO(), netConf, rt)
// The pod may not get deleted successfully at the first time. // The pod may not get deleted successfully at the first time.
// Ignore "no such file or directory" error in case the network has already been deleted in previous attempts. // Ignore "no such file or directory" error in case the network has already been deleted in previous attempts.
if err != nil && !strings.Contains(err.Error(), "no such file or directory") { if err != nil && !strings.Contains(err.Error(), "no such file or directory") {

View File

@ -20,6 +20,7 @@ package cni
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -48,7 +49,7 @@ import (
) )
// Returns .in file path, .out file path, and .env file path // Returns .in file path, .out file path, and .env file path
func installPluginUnderTest(t *testing.T, testBinDir, testConfDir, testDataDir, binName string, confName string) (string, string, string) { func installPluginUnderTest(t *testing.T, testBinDir, testConfDir, testDataDir, binName string, confName, podIP string) (string, string, string) {
for _, dir := range []string{testBinDir, testConfDir, testDataDir} { for _, dir := range []string{testBinDir, testConfDir, testDataDir} {
err := os.MkdirAll(dir, 0777) err := os.MkdirAll(dir, 0777)
if err != nil { if err != nil {
@ -56,12 +57,14 @@ func installPluginUnderTest(t *testing.T, testBinDir, testConfDir, testDataDir,
} }
} }
const cniVersion = "0.2.0"
confFile := path.Join(testConfDir, confName+".conf") confFile := path.Join(testConfDir, confName+".conf")
f, err := os.Create(confFile) f, err := os.Create(confFile)
if err != nil { if err != nil {
t.Fatalf("Failed to install plugin %s: %v", confFile, err) t.Fatalf("Failed to install plugin %s: %v", confFile, err)
} }
networkConfig := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true, "bandwidth": true, "ipRanges": true} }`, confName, binName) networkConfig := fmt.Sprintf(`{ "cniVersion": "%s", "name": "%s", "type": "%s", "capabilities": {"portMappings": true, "bandwidth": true, "ipRanges": true} }`, cniVersion, confName, binName)
_, err = f.WriteString(networkConfig) _, err = f.WriteString(networkConfig)
if err != nil { if err != nil {
t.Fatalf("Failed to write network config file (%v)", err) t.Fatalf("Failed to write network config file (%v)", err)
@ -78,8 +81,8 @@ echo "%@" >> {{.OutputEnv}}
export $(echo ${CNI_ARGS} | sed 's/;/ /g') &> /dev/null export $(echo ${CNI_ARGS} | sed 's/;/ /g') &> /dev/null
mkdir -p {{.OutputDir}} &> /dev/null mkdir -p {{.OutputDir}} &> /dev/null
echo -n "$CNI_COMMAND $CNI_NETNS $K8S_POD_NAMESPACE $K8S_POD_NAME $K8S_POD_INFRA_CONTAINER_ID" >& {{.OutputFile}} echo -n "$CNI_COMMAND $CNI_NETNS $K8S_POD_NAMESPACE $K8S_POD_NAME $K8S_POD_INFRA_CONTAINER_ID" >& {{.OutputFile}}
echo -n "{ \"ip4\": { \"ip\": \"10.1.0.23/24\" } }" echo -n "{ \"cniVersion\": \"{{.CNIVersion}}\", \"ip4\": { \"ip\": \"{{.PodIP}}/24\" } }"`
`
inputFile := path.Join(testDataDir, binName+".in") inputFile := path.Join(testDataDir, binName+".in")
outputFile := path.Join(testDataDir, binName+".out") outputFile := path.Join(testDataDir, binName+".out")
envFile := path.Join(testDataDir, binName+".env") envFile := path.Join(testDataDir, binName+".env")
@ -88,6 +91,8 @@ echo -n "{ \"ip4\": { \"ip\": \"10.1.0.23/24\" } }"
"OutputFile": outputFile, "OutputFile": outputFile,
"OutputEnv": envFile, "OutputEnv": envFile,
"OutputDir": testDataDir, "OutputDir": testDataDir,
"CNIVersion": cniVersion,
"PodIP": podIP,
} }
tObj := template.Must(template.New("test").Parse(execScriptTempl)) tObj := template.Must(template.New("test").Parse(execScriptTempl))
@ -190,7 +195,7 @@ func TestCNIPlugin(t *testing.T) {
testBinDir := path.Join(tmpDir, "opt", "cni", "bin") testBinDir := path.Join(tmpDir, "opt", "cni", "bin")
testDataDir := path.Join(tmpDir, "output") testDataDir := path.Join(tmpDir, "output")
defer tearDownPlugin(tmpDir) defer tearDownPlugin(tmpDir)
inputFile, outputFile, outputEnv := installPluginUnderTest(t, testBinDir, testConfDir, testDataDir, binName, netName) inputFile, outputFile, outputEnv := installPluginUnderTest(t, testBinDir, testConfDir, testDataDir, binName, netName, podIP)
containerID := kubecontainer.ContainerID{Type: "test", ID: "test_infra_container"} containerID := kubecontainer.ContainerID{Type: "test", ID: "test_infra_container"}
pods := []*containertest.FakePod{{ pods := []*containertest.FakePod{{
@ -217,7 +222,7 @@ func TestCNIPlugin(t *testing.T) {
cniPlugin.execer = fexec cniPlugin.execer = fexec
cniPlugin.loNetwork.CNIConfig = mockLoCNI cniPlugin.loNetwork.CNIConfig = mockLoCNI
mockLoCNI.On("AddNetworkList", cniPlugin.loNetwork.NetworkConfig, mock.AnythingOfType("*libcni.RuntimeConf")).Return(&types020.Result{IP4: &types020.IPConfig{IP: net.IPNet{IP: []byte{127, 0, 0, 1}}}}, nil) mockLoCNI.On("AddNetworkList", context.TODO(), cniPlugin.loNetwork.NetworkConfig, mock.AnythingOfType("*libcni.RuntimeConf")).Return(&types020.Result{IP4: &types020.IPConfig{IP: net.IPNet{IP: []byte{127, 0, 0, 1}}}}, nil)
// Check that status returns an error // Check that status returns an error
if err := cniPlugin.Status(); err == nil { if err := cniPlugin.Status(); err == nil {

View File

@ -19,6 +19,7 @@ limitations under the License.
package mock_cni package mock_cni
import ( import (
"context"
"github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@ -28,22 +29,52 @@ type MockCNI struct {
mock.Mock mock.Mock
} }
func (m *MockCNI) AddNetwork(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) (types.Result, error) { func (m *MockCNI) AddNetwork(ctx context.Context, net *libcni.NetworkConfig, rt *libcni.RuntimeConf) (types.Result, error) {
args := m.Called(ctx, net, rt)
return args.Get(0).(types.Result), args.Error(1)
}
func (m *MockCNI) DelNetwork(ctx context.Context, net *libcni.NetworkConfig, rt *libcni.RuntimeConf) error {
args := m.Called(ctx, net, rt)
return args.Error(0)
}
func (m *MockCNI) DelNetworkList(ctx context.Context, net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) error {
args := m.Called(ctx, net, rt)
return args.Error(0)
}
func (m *MockCNI) GetNetworkListCachedResult(net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) (types.Result, error) {
args := m.Called(net, rt) args := m.Called(net, rt)
return args.Get(0).(types.Result), args.Error(1) return args.Get(0).(types.Result), args.Error(1)
} }
func (m *MockCNI) DelNetwork(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) error { func (m *MockCNI) AddNetworkList(ctx context.Context, net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) (types.Result, error) {
args := m.Called(net, rt) args := m.Called(ctx, net, rt)
return args.Error(0)
}
func (m *MockCNI) DelNetworkList(net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) error {
args := m.Called(net, rt)
return args.Error(0)
}
func (m *MockCNI) AddNetworkList(net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) (types.Result, error) {
args := m.Called(net, rt)
return args.Get(0).(types.Result), args.Error(1) return args.Get(0).(types.Result), args.Error(1)
} }
func (m *MockCNI) CheckNetworkList(ctx context.Context, net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) error {
args := m.Called(ctx, net, rt)
return args.Error(0)
}
func (m *MockCNI) CheckNetwork(ctx context.Context, net *libcni.NetworkConfig, rt *libcni.RuntimeConf) error {
args := m.Called(ctx, net, rt)
return args.Error(0)
}
func (m *MockCNI) GetNetworkCachedResult(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) (types.Result, error) {
args := m.Called(net, rt)
return args.Get(0).(types.Result), args.Error(0)
}
func (m *MockCNI) ValidateNetworkList(ctx context.Context, net *libcni.NetworkConfigList) ([]string, error) {
args := m.Called(ctx, net)
return args.Get(0).([]string), args.Error(0)
}
func (m *MockCNI) ValidateNetwork(ctx context.Context, net *libcni.NetworkConfig) ([]string, error) {
args := m.Called(ctx, net)
return args.Get(0).([]string), args.Error(0)
}

View File

@ -19,6 +19,7 @@ limitations under the License.
package kubenet package kubenet
import ( import (
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
@ -570,7 +571,7 @@ func (plugin *kubenetNetworkPlugin) addContainerToNetwork(config *libcni.Network
// The network plugin can take up to 3 seconds to execute, // The network plugin can take up to 3 seconds to execute,
// so yield the lock while it runs. // so yield the lock while it runs.
plugin.mu.Unlock() plugin.mu.Unlock()
res, err := plugin.cniConfig.AddNetwork(config, rt) res, err := plugin.cniConfig.AddNetwork(context.TODO(), config, rt)
plugin.mu.Lock() plugin.mu.Lock()
if err != nil { if err != nil {
return nil, fmt.Errorf("Error adding container to network: %v", err) return nil, fmt.Errorf("Error adding container to network: %v", err)
@ -585,7 +586,7 @@ func (plugin *kubenetNetworkPlugin) delContainerFromNetwork(config *libcni.Netwo
} }
klog.V(3).Infof("Removing %s/%s from '%s' with CNI '%s' plugin and runtime: %+v", namespace, name, config.Network.Name, config.Network.Type, rt) klog.V(3).Infof("Removing %s/%s from '%s' with CNI '%s' plugin and runtime: %+v", namespace, name, config.Network.Name, config.Network.Type, rt)
err = plugin.cniConfig.DelNetwork(config, rt) err = plugin.cniConfig.DelNetwork(context.TODO(), config, rt)
// The pod may not get deleted successfully at the first time. // The pod may not get deleted successfully at the first time.
// Ignore "no such file or directory" error in case the network has already been deleted in previous attempts. // Ignore "no such file or directory" error in case the network has already been deleted in previous attempts.
if err != nil && !strings.Contains(err.Error(), "no such file or directory") { if err != nil && !strings.Contains(err.Error(), "no such file or directory") {

View File

@ -143,7 +143,7 @@ func TestTeardownCallsShaper(t *testing.T) {
kubenet.bandwidthShaper = fshaper kubenet.bandwidthShaper = fshaper
kubenet.hostportSyncer = hostporttest.NewFakeHostportSyncer() kubenet.hostportSyncer = hostporttest.NewFakeHostportSyncer()
mockcni.On("DelNetwork", mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil) mockcni.On("DelNetwork", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil)
details := make(map[string]interface{}) details := make(map[string]interface{})
details[network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR] = "10.0.0.1/24" details[network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR] = "10.0.0.1/24"
@ -247,7 +247,7 @@ func TestTearDownWithoutRuntime(t *testing.T) {
existingContainerID := kubecontainer.BuildContainerID("docker", "123") existingContainerID := kubecontainer.BuildContainerID("docker", "123")
kubenet.podIPs[existingContainerID] = tc.ip kubenet.podIPs[existingContainerID] = tc.ip
mockcni.On("DelNetwork", mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil) mockcni.On("DelNetwork", mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil)
if err := kubenet.TearDownPod("namespace", "name", existingContainerID); err != nil { if err := kubenet.TearDownPod("namespace", "name", existingContainerID); err != nil {
t.Fatalf("Unexpected error in TearDownPod: %v", err) t.Fatalf("Unexpected error in TearDownPod: %v", err)