Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Fix CNI portmapping cleanup #691

Merged
merged 6 commits into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions e2e/portmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// This is the e2e package to run tests for Ignite
// Currently, we do local e2e tests only
// we have to wait until the CI setup to allow Ignite to run with sudo and in a KVM environment.
//
// How to run tests:
// sudo IGNITE_E2E_HOME=$PWD $(which go) test ./e2e/. -v -count 1 -run Test
//

package e2e

import (
"fmt"
"os/exec"
"testing"

"github.com/weaveworks/ignite/e2e/util"
"gotest.tools/assert"
)

func TestPortmapCleanup(t *testing.T) {
assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set")

vmName := "e2e-test-ignite-portmap-cleanup"
mappedPort := 4242

igniteCmd := util.NewCommand(t, igniteBin)

igniteCmd.New().
WithNetwork("cni").
With("run").
With("--name=" + vmName).
With("--ssh").
With("--ports=" + fmt.Sprintf("%d:%d", mappedPort, mappedPort)).
With(util.DefaultVMImage).
Run()

// Get the VM ID
idCmd := igniteCmd.New().
With("ps").
With("--filter").
With(fmt.Sprintf("{{.ObjectMeta.Name}}=%s", vmName)).
With("--quiet")

idOut, idErr := idCmd.Cmd.CombinedOutput()
assert.Check(t, idErr, fmt.Sprintf("vm id not found: \n%q\n%s", idCmd.Cmd, idOut))
vmID := string(idOut[:len(idOut)-2])

// Check that the IPtable rules are installed
grepOut, grepErr := grepIPTables(vmID)
assert.NilError(t, grepErr, fmt.Sprintf("unable to match iptable rules:\n %s", grepOut))

igniteCmd.New().
With("rm", "-f", vmName).
Run()

// Check that the IPtable rules are removed
grepOut, grepErr = grepIPTables(vmID)
assert.Error(t, grepErr, "exit status 1", fmt.Sprintf("unexpected output in grep : \n%s", grepOut))
assert.Equal(t, len(grepOut), 0, fmt.Sprintf("leftover iptable rules detected:\n%s", string(grepOut)))

}

func grepIPTables(search string) ([]byte, error) {
grepCmd := exec.Command(
"grep",
search,
)
natCmd := exec.Command(
"iptables",
"-t",
"nat",
"-nL",
)
pipe, _ := natCmd.StdoutPipe()
defer pipe.Close()

grepCmd.Stdin = pipe
_ = natCmd.Start()
return grepCmd.Output()
}
18 changes: 16 additions & 2 deletions pkg/network/cni/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func cniToIgniteResult(r *gocni.CNIResult) *network.Result {
return result
}

func (plugin *cniNetworkPlugin) RemoveContainerNetwork(containerID string) (err error) {
func (plugin *cniNetworkPlugin) RemoveContainerNetwork(containerID string, portMappings ...meta.PortMapping) (err error) {
if err = plugin.initialize(); err != nil {
return err
}
Expand All @@ -214,7 +214,21 @@ func (plugin *cniNetworkPlugin) RemoveContainerNetwork(containerID string) (err
return nil
}

return plugin.cni.Remove(context.Background(), containerID, netnsPath)
pms := make([]gocni.PortMapping, 0, len(portMappings))
for _, pm := range portMappings {
hostIP := ""
if pm.BindAddress != nil {
hostIP = pm.BindAddress.String()
}
pms = append(pms, gocni.PortMapping{
HostPort: int32(pm.HostPort),
ContainerPort: int32(pm.VMPort),
Protocol: pm.Protocol.String(),
HostIP: hostIP,
})
}

return plugin.cni.Remove(context.Background(), containerID, netnsPath, gocni.WithCapabilityPortMap(pms))
}

// cleanupBridges makes the defaultNetworkName CNI network config not leak iptables rules
Expand Down
2 changes: 1 addition & 1 deletion pkg/network/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (plugin *dockerNetworkPlugin) SetupContainerNetwork(containerID string, _ .
}, nil
}

func (*dockerNetworkPlugin) RemoveContainerNetwork(_ string) error {
func (*dockerNetworkPlugin) RemoveContainerNetwork(_ string, _ ...meta.PortMapping) error {
// no-op for docker, this is handled automatically
return nil
}
2 changes: 1 addition & 1 deletion pkg/network/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Plugin interface {
SetupContainerNetwork(containerID string, portmappings ...meta.PortMapping) (*Result, error)

// RemoveContainerNetwork is the method called before a container using the network plugin can be deleted
RemoveContainerNetwork(containerID string) error
RemoveContainerNetwork(containerID string, portmappings ...meta.PortMapping) error
}

type Result struct {
Expand Down
7 changes: 4 additions & 3 deletions pkg/operations/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

log "github.com/sirupsen/logrus"
api "github.com/weaveworks/ignite/pkg/apis/ignite"
meta "github.com/weaveworks/ignite/pkg/apis/meta/v1alpha1"
"github.com/weaveworks/ignite/pkg/client"
"github.com/weaveworks/ignite/pkg/dmlegacy/cleanup"
"github.com/weaveworks/ignite/pkg/logs"
Expand Down Expand Up @@ -86,7 +87,7 @@ func StopVM(vm *api.VM, kill, silent bool) error {
}

// Remove VM networking
if err = removeNetworking(util.NewPrefixer().Prefix(vm.GetUID())); err != nil {
if err = removeNetworking(util.NewPrefixer().Prefix(vm.GetUID()), vm.Spec.Network.Ports...); err != nil {
log.Warnf("Failed to cleanup networking for stopped container %s %q: %v", vm.GetKind(), vm.GetUID(), err)

return err
Expand Down Expand Up @@ -119,7 +120,7 @@ func StopVM(vm *api.VM, kill, silent bool) error {
return nil
}

func removeNetworking(containerID string) error {
func removeNetworking(containerID string, portmappings ...meta.PortMapping) error {
log.Infof("Removing the container with ID %q from the %q network", containerID, providers.NetworkPlugin.Name())
return providers.NetworkPlugin.RemoveContainerNetwork(containerID)
return providers.NetworkPlugin.RemoveContainerNetwork(containerID, portmappings...)
}