Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libnetwork/cni: add support for arbitrary ipam plugins #965

Merged
merged 3 commits into from
Mar 17, 2022
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
12 changes: 9 additions & 3 deletions libnetwork/cni/cni_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,21 @@ func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool {
// It returns an array of subnets and an extra bool if dhcp is configured.
func convertIPAMConfToNetwork(network *types.Network, ipam *ipamConfig, confPath string) error {
if ipam.PluginType == types.DHCPIPAMDriver {
network.IPAMOptions["driver"] = types.DHCPIPAMDriver
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
return nil
}

if ipam.PluginType != types.HostLocalIPAMDriver {
return errors.Errorf("unsupported ipam plugin %s in %s", ipam.PluginType, confPath)
// This is not an error. While we only support certain ipam drivers, we
// cannot make it fail for unsupported ones. CNI is still able to use them,
// just our translation logic cannot convert this into a Network.
// For the same reason this is not warning, it would just be annoying for
// everyone using a unknown ipam driver.
logrus.Infof("unsupported ipam plugin %q in %s", ipam.PluginType, confPath)
return nil
}

network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
for _, r := range ipam.Ranges {
for _, ipam := range r {
s := types.Subnet{}
Expand Down
4 changes: 2 additions & 2 deletions libnetwork/cni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,12 @@ func createIPMACVLAN(network *types.Network) error {
}
}
if len(network.Subnets) == 0 {
network.IPAMOptions["driver"] = types.DHCPIPAMDriver
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
if network.Internal {
return errors.New("internal is not supported with macvlan and dhcp ipam driver")
}
} else {
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
}
return nil
}
41 changes: 36 additions & 5 deletions libnetwork/cni/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var _ = Describe("Config", func() {
}
logBuffer = bytes.Buffer{}
logrus.SetOutput(&logBuffer)
logrus.SetLevel(logrus.InfoLevel)
})

JustBeforeEach(func() {
Expand All @@ -45,6 +46,7 @@ var _ = Describe("Config", func() {
})

AfterEach(func() {
logrus.SetLevel(logrus.InfoLevel)
os.RemoveAll(cniConfDir)
})

Expand Down Expand Up @@ -1071,6 +1073,8 @@ var _ = Describe("Config", func() {

Context("network load valid existing ones", func() {

numberOfConfigFiles := 0

BeforeEach(func() {
dir := "testfiles/valid"
files, err := ioutil.ReadDir(dir)
Expand All @@ -1088,26 +1092,41 @@ var _ = Describe("Config", func() {
Fail("Failed to copy test files")
}
}
numberOfConfigFiles = len(files)
})

It("load networks from disk", func() {
logrus.SetLevel(logrus.WarnLevel)
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))
// test the we do not show logrus warnings/errors
logString := logBuffer.String()
Expect(logString).To(BeEmpty())
})

It("load networks from disk with log level debug", func() {
logrus.SetLevel(logrus.DebugLevel)
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(numberOfConfigFiles))
// check for the unsupported ipam plugin message
logString := logBuffer.String()
Expect(logString).ToNot(BeEmpty())
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"\\\" in %s", cniConfDir+"/ipam-none.conflist"))
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"\\\" in %s", cniConfDir+"/ipam-empty.conflist"))
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"static\\\" in %s", cniConfDir+"/ipam-static.conflist"))
})

It("change network struct fields should not affect network struct in the backend", func() {
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))

nets[0].Name = "myname"
nets, err = libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))
Expect(nets).ToNot(ContainElement(HaveNetworkName("myname")))

network, err := libpodNet.NetworkInspect("bridge")
Expand Down Expand Up @@ -1144,6 +1163,7 @@ var _ = Describe("Config", func() {
Expect(network.Driver).To(Equal("macvlan"))
Expect(network.Subnets).To(HaveLen(0))
// DHCP
Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp"))
})

It("internal network", func() {
Expand Down Expand Up @@ -1230,6 +1250,15 @@ var _ = Describe("Config", func() {
))
})

It("ipam static network", func() {
network, err := libpodNet.NetworkInspect("ipam-static")
Expect(err).To(BeNil())
Expect(network.Name).To(Equal("ipam-static"))
Expect(network.ID).To(HaveLen(64))
Expect(network.Driver).To(Equal("bridge"))
Expect(network.Subnets).To(HaveLen(0))
})

It("network list with filters (name)", func() {
filters := map[string][]string{
"name": {"internal", "bridge"},
Expand Down Expand Up @@ -1304,10 +1333,12 @@ var _ = Describe("Config", func() {

networks, err := libpodNet.NetworkList(filterFuncs...)
Expect(err).To(BeNil())
Expect(networks).To(HaveLen(9))
Expect(networks).To(HaveLen(numberOfConfigFiles))
Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"),
HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"),
HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"), HaveNetworkName("dualstack")))
HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"),
HaveNetworkName("dualstack"), HaveNetworkName("ipam-none"), HaveNetworkName("ipam-empty"),
HaveNetworkName("ipam-static")))
})

It("network list with filters (label)", func() {
Expand Down
2 changes: 2 additions & 0 deletions libnetwork/cni/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ var _ = Describe("run CNI", func() {
if err != nil {
Fail("Failed to create netns")
}
logrus.SetLevel(logrus.WarnLevel)
})

JustBeforeEach(func() {
Expand All @@ -104,6 +105,7 @@ var _ = Describe("run CNI", func() {
})

AfterEach(func() {
logrus.SetLevel(logrus.InfoLevel)
os.RemoveAll(cniConfDir)

netns.UnmountNS(netNSTest)
Expand Down
34 changes: 34 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-empty.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"cniVersion": "0.4.0",
"name": "ipam-empty",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman124",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
33 changes: 33 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-none.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"cniVersion": "0.4.0",
"name": "ipam-none",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman123",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
49 changes: 49 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-static.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"cniVersion": "0.4.0",
"name": "ipam-static",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman125",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "static",
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"addresses": [
[
{
"subnet": "10.89.0.89/16",
"gateway": "10.89.0.1"
}
]
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
4 changes: 2 additions & 2 deletions libnetwork/internal/util/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet,
}
}

if network.IPAMOptions["driver"] != types.DHCPIPAMDriver {
if network.IPAMOptions[types.Driver] != types.DHCPIPAMDriver {
if len(network.Subnets) == 0 {
freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks, subnetPools)
if err != nil {
Expand Down Expand Up @@ -63,7 +63,7 @@ func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet,
network.Subnets = append(network.Subnets, *freeSubnet)
}
}
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
}
return nil
}
2 changes: 1 addition & 1 deletion libnetwork/internal/util/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func validatePerNetworkOpts(network *types.Network, netOpts *types.PerNetworkOpt
if netOpts.InterfaceName == "" {
return errors.Errorf("interface name on network %s is empty", network.Name)
}
if network.IPAMOptions["driver"] == types.HostLocalIPAMDriver {
if network.IPAMOptions[types.Driver] == types.HostLocalIPAMDriver {
outer:
for _, ip := range netOpts.StaticIPs {
for _, s := range network.Subnets {
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/netavark/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func createMacvlan(network *types.Network) error {
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark")
}
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver

// validate the given options, we do not need them but just check to make sure they are valid
for key, value := range network.Options {
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/netavark/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func (n *netavarkNetwork) deallocIPs(opts *types.NetworkOptions) error {
// it checks the ipam driver and if subnets are set
func requiresIPAMAlloc(network *types.Network) bool {
// only do host allocation when driver is set to HostLocalIPAMDriver or unset
switch network.IPAMOptions["driver"] {
switch network.IPAMOptions[types.Driver] {
case "", types.HostLocalIPAMDriver:
default:
return false
Expand Down
1 change: 1 addition & 0 deletions libnetwork/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
IPVLANNetworkDriver = "ipvlan"

// IPAM drivers
Driver = "driver"
// HostLocalIPAMDriver store the ip
HostLocalIPAMDriver = "host-local"
// DHCPIPAMDriver get subnet and ip from dhcp server
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/util/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.Name, filterValues)
}, nil

case "driver":
case types.Driver:
// matches network driver
return func(net types.Network) bool {
return util.StringInSlice(net.Driver, filterValues)
Expand Down