Skip to content

Commit

Permalink
Fix podman network IDs handling
Browse files Browse the repository at this point in the history
The libpod network logic knows about networks IDs but OCICNI
does not. We cannot pass the network ID to OCICNI. Instead we
need to make sure we only use network names internally. This
is also important for libpod since we also only store the
network names in the state. If we would add a ID there the
same networks could accidentally be added twice.

Fixes #9451

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Paul Holzinger committed Feb 22, 2021
1 parent 6fbf73e commit 9d818be
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 15 deletions.
4 changes: 2 additions & 2 deletions libpod/network/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, err
return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name))
}

// ReadRawCNIConfByName reads the raw CNI configuration for a CNI
// ReadRawCNIConfByNameOrID reads the raw CNI configuration for a CNI
// network by name
func ReadRawCNIConfByName(config *config.Config, name string) ([]byte, error) {
func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error) {
confFile, err := GetCNIConfigPathByNameOrID(config, name)
if err != nil {
return nil, err
Expand Down
19 changes: 17 additions & 2 deletions libpod/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"os"

"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/containers/common/pkg/config"
Expand Down Expand Up @@ -222,7 +223,7 @@ func RemoveNetwork(config *config.Config, name string) error {

// InspectNetwork reads a CNI config and returns its configuration
func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
b, err := ReadRawCNIConfByName(config, name)
b, err := ReadRawCNIConfByNameOrID(config, name)
if err != nil {
return nil, err
}
Expand All @@ -234,7 +235,7 @@ func InspectNetwork(config *config.Config, name string) (map[string]interface{},
// Exists says whether a given network exists or not; it meant
// specifically for restful responses so 404s can be used
func Exists(config *config.Config, name string) (bool, error) {
_, err := ReadRawCNIConfByName(config, name)
_, err := ReadRawCNIConfByNameOrID(config, name)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchNetwork {
return false, nil
Expand Down Expand Up @@ -277,3 +278,17 @@ func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entitie
}
return reports, nil
}

// NormalizeName translates a network ID into a name.
// If the input is a name the name is returned.
func NormalizeName(config *config.Config, nameOrID string) (string, error) {
path, err := GetCNIConfigPathByNameOrID(config, nameOrID)
if err != nil {
return "", err
}
conf, err := libcni.ConfListFromFile(path)
if err != nil {
return "", err
}
return conf.Name, nil
}
14 changes: 6 additions & 8 deletions libpod/networking_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1139,13 +1139,12 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
return err
}

exists, err := network.Exists(c.runtime.config, netName)
// check if network exists and if the input is a ID we get the name
// ocicni only uses names so it is important that we only use the name
netName, err = network.NormalizeName(c.runtime.config, netName)
if err != nil {
return err
}
if !exists {
return errors.Wrap(define.ErrNoSuchNetwork, netName)
}

index, nameExists := networks[netName]
if !nameExists && len(networks) > 0 {
Expand Down Expand Up @@ -1196,13 +1195,12 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
return err
}

exists, err := network.Exists(c.runtime.config, netName)
// check if network exists and if the input is a ID we get the name
// ocicni only uses names so it is important that we only use the name
netName, err = network.NormalizeName(c.runtime.config, netName)
if err != nil {
return err
}
if !exists {
return errors.Wrap(define.ErrNoSuchNetwork, netName)
}

c.lock.Lock()
defer c.lock.Unlock()
Expand Down
16 changes: 16 additions & 0 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/libpod/shutdown"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
Expand Down Expand Up @@ -285,6 +286,21 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, err
}

// normalize the networks to names
// ocicni only knows about cni names so we have to make
// sure we do not use ids internally
if len(ctr.config.Networks) > 0 {
netNames := make([]string, 0, len(ctr.config.Networks))
for _, nameOrID := range ctr.config.Networks {
netName, err := network.NormalizeName(r.config, nameOrID)
if err != nil {
return nil, err
}
netNames = append(netNames, netName)
}
ctr.config.Networks = netNames
}

// Inhibit shutdown until creation succeeds
shutdown.Inhibit()
defer shutdown.Uninhibit()
Expand Down
12 changes: 9 additions & 3 deletions test/e2e/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

. "github.com/containers/podman/v3/test/utils"
"github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand Down Expand Up @@ -576,15 +577,20 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).ToNot(BeZero())
})

It("create container in pod with network should fail", func() {
It("create container in pod with network should not fail", func() {
name := "createwithnetwork"
pod := podmanTest.RunTopContainerInPod("", "new:"+name)
pod.WaitWithDefaultTimeout()
Expect(pod.ExitCode()).To(BeZero())

session := podmanTest.Podman([]string{"create", "--pod", name, "--network", "foobar", ALPINE, "top"})
netName := "pod" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
defer podmanTest.removeCNINetwork(netName)

session = podmanTest.Podman([]string{"create", "--pod", name, "--network", netName, ALPINE, "top"})
session.WaitWithDefaultTimeout()
//Expect(session.ExitCode()).ToNot(BeZero())
Expect(session.ExitCode()).To(BeZero())
})

Expand Down
85 changes: 85 additions & 0 deletions test/e2e/network_connect_disconnect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,55 @@ var _ = Describe("Podman network connect and disconnect", func() {
Expect(exec.ExitCode()).To(BeZero())
})

It("podman network connect and run with network ID", func() {
SkipIfRemote("remote flakes to much I will fix this in another PR")
SkipIfRootless("network connect and disconnect are only rootful")
netName := "ID" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
defer podmanTest.removeCNINetwork(netName)

session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
netID := session.OutputToString()

ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netID, ALPINE, "top"})
ctr.WaitWithDefaultTimeout()
Expect(ctr.ExitCode()).To(BeZero())

exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"})
exec.WaitWithDefaultTimeout()
Expect(exec.ExitCode()).To(BeZero())

// Create a second network
newNetName := "ID2" + stringid.GenerateNonCryptoID()
session = podmanTest.Podman([]string{"network", "create", newNetName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
defer podmanTest.removeCNINetwork(newNetName)

session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + newNetName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
newNetID := session.OutputToString()

connect := podmanTest.Podman([]string{"network", "connect", newNetID, "test"})
connect.WaitWithDefaultTimeout()
Expect(connect.ExitCode()).To(BeZero())

inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{.NetworkSettings.Networks}}"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(BeZero())
Expect(inspect.OutputToString()).To(ContainSubstring(netName))
Expect(inspect.OutputToString()).To(ContainSubstring(newNetName))

exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"})
exec.WaitWithDefaultTimeout()
Expect(exec.ExitCode()).To(BeZero())
})

It("podman network disconnect when not running", func() {
SkipIfRootless("network connect and disconnect are only rootful")
netName1 := "aliasTest" + stringid.GenerateNonCryptoID()
Expand Down Expand Up @@ -234,4 +283,40 @@ var _ = Describe("Podman network connect and disconnect", func() {
exec.WaitWithDefaultTimeout()
Expect(exec.ExitCode()).ToNot(BeZero())
})

It("podman network disconnect and run with network ID", func() {
SkipIfRemote("remote flakes to much I will fix this in another PR")
SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
defer podmanTest.removeCNINetwork(netName)

session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
netID := session.OutputToString()

ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netID, ALPINE, "top"})
ctr.WaitWithDefaultTimeout()
Expect(ctr.ExitCode()).To(BeZero())

exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"})
exec.WaitWithDefaultTimeout()
Expect(exec.ExitCode()).To(BeZero())

dis := podmanTest.Podman([]string{"network", "disconnect", netID, "test"})
dis.WaitWithDefaultTimeout()
Expect(dis.ExitCode()).To(BeZero())

inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(BeZero())
Expect(inspect.OutputToString()).To(Equal("0"))

exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"})
exec.WaitWithDefaultTimeout()
Expect(exec.ExitCode()).ToNot(BeZero())
})
})

0 comments on commit 9d818be

Please sign in to comment.