add flannel to support dual stack ip

support three mode ip stack:
- only ipv4 stack
- only ipv6 stack
- dual stack ip

Signed-off-by: yaoice <yao3690093@gmail.com>
This commit is contained in:
yaoice 2021-01-13 11:24:59 +08:00 committed by Casey Callendrello
parent d1769ddd68
commit 8feef71fd3
3 changed files with 524 additions and 106 deletions

View File

@ -57,6 +57,8 @@ type NetConf struct {
type subnetEnv struct { type subnetEnv struct {
nw *net.IPNet nw *net.IPNet
sn *net.IPNet sn *net.IPNet
ip6Nw *net.IPNet
ip6Sn *net.IPNet
mtu *uint mtu *uint
ipmasq *bool ipmasq *bool
} }
@ -64,11 +66,11 @@ type subnetEnv struct {
func (se *subnetEnv) missing() string { func (se *subnetEnv) missing() string {
m := []string{} m := []string{}
if se.nw == nil { if se.nw == nil && se.ip6Nw == nil {
m = append(m, "FLANNEL_NETWORK") m = append(m, []string{"FLANNEL_NETWORK", "FLANNEL_IPV6_NETWORK"}...)
} }
if se.sn == nil { if se.sn == nil && se.ip6Sn == nil {
m = append(m, "FLANNEL_SUBNET") m = append(m, []string{"FLANNEL_SUBNET", "FLANNEL_IPV6_SUBNET"}...)
} }
if se.mtu == nil { if se.mtu == nil {
m = append(m, "FLANNEL_MTU") m = append(m, "FLANNEL_MTU")
@ -128,6 +130,18 @@ func loadFlannelSubnetEnv(fn string) (*subnetEnv, error) {
return nil, err return nil, err
} }
case "FLANNEL_IPV6_NETWORK":
_, se.ip6Nw, err = net.ParseCIDR(parts[1])
if err != nil {
return nil, err
}
case "FLANNEL_IPV6_SUBNET":
_, se.ip6Sn, err = net.ParseCIDR(parts[1])
if err != nil {
return nil, err
}
case "FLANNEL_MTU": case "FLANNEL_MTU":
mtu, err := strconv.ParseUint(parts[1], 10, 32) mtu, err := strconv.ParseUint(parts[1], 10, 32)
if err != nil { if err != nil {

View File

@ -40,13 +40,33 @@ func getDelegateIPAM(n *NetConf, fenv *subnetEnv) (map[string]interface{}, error
if !hasKey(ipam, "type") { if !hasKey(ipam, "type") {
ipam["type"] = "host-local" ipam["type"] = "host-local"
} }
ipam["subnet"] = fenv.sn.String()
var rangesSlice [][]map[string]interface{}
if fenv.sn != nil && fenv.sn.String() != "" {
rangesSlice = append(rangesSlice, []map[string]interface{}{
{"subnet": fenv.sn.String()},
},
)
}
if fenv.ip6Sn != nil && fenv.ip6Sn.String() != "" {
rangesSlice = append(rangesSlice, []map[string]interface{}{
{"subnet": fenv.ip6Sn.String()},
},
)
}
ipam["ranges"] = rangesSlice
rtes, err := getIPAMRoutes(n) rtes, err := getIPAMRoutes(n)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read IPAM routes: %w", err) return nil, fmt.Errorf("failed to read IPAM routes: %w", err)
} }
if fenv.nw != nil {
rtes = append(rtes, types.Route{Dst: *fenv.nw}) rtes = append(rtes, types.Route{Dst: *fenv.nw})
}
if fenv.ip6Nw != nil {
rtes = append(rtes, types.Route{Dst: *fenv.ip6Nw})
}
ipam["routes"] = rtes ipam["routes"] = rtes
return ipam, nil return ipam, nil

View File

@ -31,8 +31,12 @@ import (
var _ = Describe("Flannel", func() { var _ = Describe("Flannel", func() {
var ( var (
originalNS ns.NetNS originalNS ns.NetNS
input string onlyIpv4Input string
subnetFile string onlyIpv6Input string
dualStackInput string
onlyIpv4SubnetFile string
onlyIpv6SubnetFile string
dualStackSubnetFile string
dataDir string dataDir string
) )
@ -70,11 +74,25 @@ var _ = Describe("Flannel", func() {
{ "dst": "10.96.0.0/12" }, { "dst": "10.96.0.0/12" },
{ "dst": "192.168.244.0/24", "gw": "10.1.17.20" }` { "dst": "192.168.244.0/24", "gw": "10.1.17.20" }`
const flannelSubnetEnv = ` const onlyIpv4FlannelSubnetEnv = `
FLANNEL_NETWORK=10.1.0.0/16 FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.17.1/24 FLANNEL_SUBNET=10.1.17.1/24
FLANNEL_MTU=1472 FLANNEL_MTU=1472
FLANNEL_IPMASQ=true FLANNEL_IPMASQ=true
`
const onlyIpv6FlannelSubnetEnv = `
FLANNEL_IPV6_NETWORK=fc00::/48
FLANNEL_IPV6_SUBNET=fc00::1/64
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
`
const dualStackFlannelSubnetEnv = `
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.17.1/24
FLANNEL_IPV6_NETWORK=fc00::/48
FLANNEL_IPV6_SUBNET=fc00::1/64
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
` `
var writeSubnetEnv = func(contents string) string { var writeSubnetEnv = func(contents string) string {
@ -96,7 +114,7 @@ FLANNEL_IPMASQ=true
return c return c
} }
var makeInput = func(inputIPAM string) string { var makeInput = func(inputIPAM string, subnetFile string) string {
ipamPart := "" ipamPart := ""
if len(inputIPAM) > 0 { if len(inputIPAM) > 0 {
ipamPart = ",\n \"ipam\":\n" + inputIPAM ipamPart = ",\n \"ipam\":\n" + inputIPAM
@ -108,21 +126,28 @@ FLANNEL_IPMASQ=true
BeforeEach(func() { BeforeEach(func() {
var err error var err error
// flannel subnet.env // flannel subnet.env
subnetFile = writeSubnetEnv(flannelSubnetEnv) onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv)
onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv)
dualStackSubnetFile = writeSubnetEnv(dualStackFlannelSubnetEnv)
// flannel state dir // flannel state dir
dataDir, err = ioutil.TempDir("", "dataDir") dataDir, err = ioutil.TempDir("", "dataDir")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
input = makeInput("") onlyIpv4Input = makeInput("", onlyIpv4SubnetFile)
onlyIpv6Input = makeInput("", onlyIpv6SubnetFile)
dualStackInput = makeInput("", dualStackSubnetFile)
}) })
AfterEach(func() { AfterEach(func() {
os.Remove(subnetFile) os.Remove(onlyIpv4SubnetFile)
os.Remove(onlyIpv6SubnetFile)
os.Remove(dualStackSubnetFile)
os.Remove(dataDir) os.Remove(dataDir)
}) })
Describe("CNI lifecycle", func() { Describe("CNI lifecycle", func() {
It("uses dataDir for storing network configuration", func() { Context("when using only ipv4 stack", func() {
It("uses dataDir for storing network configuration with ipv4 stack", func() {
const IFNAME = "eth0" const IFNAME = "eth0"
targetNs, err := testutils.NewNS() targetNs, err := testutils.NewNS()
@ -130,23 +155,23 @@ FLANNEL_IPMASQ=true
defer targetNs.Close() defer targetNs.Close()
args := &skel.CmdArgs{ args := &skel.CmdArgs{
ContainerID: "some-container-id", ContainerID: "some-container-id-ipv4",
Netns: targetNs.Path(), Netns: targetNs.Path(),
IfName: IFNAME, IfName: IFNAME,
StdinData: []byte(input), StdinData: []byte(onlyIpv4Input),
} }
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
By("calling ADD") By("calling ADD with ipv4 stack")
resI, _, err := testutils.CmdAddWithArgs(args, func() error { resI, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args) return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir") By("check that plugin writes the net config to dataDir with ipv4 stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id") path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv4")
Expect(path).Should(BeAnExistingFile()) Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path) netConfBytes, err := ioutil.ReadFile(path)
@ -159,7 +184,11 @@ FLANNEL_IPMASQ=true
"dst": "10.1.0.0/16" "dst": "10.1.0.0/16"
} }
], ],
"subnet" : "10.1.17.0/24", "ranges": [
[{
"subnet": "10.1.17.0/24"
}]
],
"type": "host-local" "type": "host-local"
}, },
"isGateway": true, "isGateway": true,
@ -174,20 +203,20 @@ FLANNEL_IPMASQ=true
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1)) Expect(result.IPs).To(HaveLen(1))
By("calling DEL") By("calling DEL with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error { err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args) return cmdDel(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir") By("check that plugin removes net config from state dir with ipv4 stack")
Expect(path).ShouldNot(BeAnExistingFile()) Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again") By("calling DEL again with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error { err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args) return cmdDel(args)
}) })
By("check that plugin does not fail due to missing net config") By("check that plugin does not fail due to missing net config with ipv4 stack")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil return nil
@ -196,28 +225,251 @@ FLANNEL_IPMASQ=true
}) })
}) })
Context("when using only ipv6 stack", func() {
It("uses dataDir for storing network configuration with ipv6 stack", func() {
const IFNAME = "eth0"
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "some-container-id-ipv6",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(onlyIpv6Input),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
By("calling ADD with ipv6 stack")
resI, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir with ipv6 stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv6")
Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := `{
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}
`
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
By("calling DEL with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with ipv6 stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with ipv6 stack")
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
})
Context("when using dual stack", func() {
It("uses dataDir for storing network configuration with dual stack", func() {
const IFNAME = "eth0"
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "some-container-id-dual-stack",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(dualStackInput),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
By("calling ADD with dual stack")
resI, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir with dual stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-dual-stack")
Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := `{
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "10.1.0.0/16"
},
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "10.1.17.0/24"
}],
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}
`
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(2))
By("calling DEL with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with dual stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with dual stack")
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
})
})
Describe("loadFlannelNetConf", func() { Describe("loadFlannelNetConf", func() {
Context("when subnetFile and dataDir are specified", func() { Context("when subnetFile and dataDir are specified with ipv4 stack", func() {
It("loads flannel network config", func() { It("loads flannel network config with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel")) Expect(conf.Type).To(Equal("flannel"))
Expect(conf.SubnetFile).To(Equal(subnetFile)) Expect(conf.SubnetFile).To(Equal(onlyIpv4SubnetFile))
Expect(conf.DataDir).To(Equal(dataDir)) Expect(conf.DataDir).To(Equal(dataDir))
}) })
}) })
Context("when defaulting subnetFile and dataDir", func() { Context("when subnetFile and dataDir are specified with ipv6 stack", func() {
It("loads flannel network config with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel"))
Expect(conf.SubnetFile).To(Equal(onlyIpv6SubnetFile))
Expect(conf.DataDir).To(Equal(dataDir))
})
})
Context("when subnetFile and dataDir are specified with dual stack", func() {
It("loads flannel network config with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput))
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel"))
Expect(conf.SubnetFile).To(Equal(dualStackSubnetFile))
Expect(conf.DataDir).To(Equal(dataDir))
})
})
Context("when defaulting subnetFile and dataDir with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
input = `{ onlyIpv4Input = `{
"name": "cni-flannel", "name": "cni-flannel",
"type": "flannel" "type": "flannel"
}` }`
}) })
It("loads flannel network config with defaults", func() { It("loads flannel network config with defaults with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel"))
Expect(conf.SubnetFile).To(Equal(defaultSubnetFile))
Expect(conf.DataDir).To(Equal(defaultDataDir))
})
})
Context("when defaulting subnetFile and dataDir with ipv6 stack", func() {
BeforeEach(func() {
onlyIpv6Input = `{
"name": "cni-flannel",
"type": "flannel"
}`
})
It("loads flannel network config with defaults with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel"))
Expect(conf.SubnetFile).To(Equal(defaultSubnetFile))
Expect(conf.DataDir).To(Equal(defaultDataDir))
})
})
Context("when defaulting subnetFile and dataDir with dual stack", func() {
BeforeEach(func() {
dualStackInput = `{
"name": "cni-flannel",
"type": "flannel"
}`
})
It("loads flannel network config with defaults with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput))
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Name).To(Equal("cni-flannel"))
Expect(conf.Type).To(Equal("flannel")) Expect(conf.Type).To(Equal("flannel"))
@ -227,9 +479,9 @@ FLANNEL_IPMASQ=true
}) })
Describe("loadFlannelSubnetEnv", func() { Describe("loadFlannelSubnetEnv", func() {
Context("when flannel subnet env is valid", func() { Context("when flannel subnet env is valid with ipv4 stack", func() {
It("loads flannel subnet config", func() { It("loads flannel subnet config with ipv4 stack", func() {
conf, err := loadFlannelSubnetEnv(subnetFile) conf, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
Expect(conf.nw.String()).To(Equal("10.1.0.0/16")) Expect(conf.nw.String()).To(Equal("10.1.0.0/16"))
Expect(conf.sn.String()).To(Equal("10.1.17.0/24")) Expect(conf.sn.String()).To(Equal("10.1.17.0/24"))
@ -239,56 +491,188 @@ FLANNEL_IPMASQ=true
}) })
}) })
Context("when flannel subnet env is invalid", func() { Context("when flannel subnet env is valid with ipv6 stack", func() {
It("loads flannel subnet config with ipv6 stack", func() {
conf, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile)
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.ip6Nw.String()).To(Equal("fc00::/48"))
Expect(conf.ip6Sn.String()).To(Equal("fc00::/64"))
var mtu uint = 1472
Expect(*conf.mtu).To(Equal(mtu))
Expect(*conf.ipmasq).To(BeTrue())
})
})
Context("when flannel subnet env is valid with dual stack", func() {
It("loads flannel subnet config with dual stack", func() {
conf, err := loadFlannelSubnetEnv(dualStackSubnetFile)
Expect(err).ShouldNot(HaveOccurred())
Expect(conf.nw.String()).To(Equal("10.1.0.0/16"))
Expect(conf.sn.String()).To(Equal("10.1.17.0/24"))
Expect(conf.ip6Nw.String()).To(Equal("fc00::/48"))
Expect(conf.ip6Sn.String()).To(Equal("fc00::/64"))
var mtu uint = 1472
Expect(*conf.mtu).To(Equal(mtu))
Expect(*conf.ipmasq).To(BeTrue())
})
})
Context("when flannel subnet env is invalid with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
subnetFile = writeSubnetEnv("foo=bar") onlyIpv4SubnetFile = writeSubnetEnv("foo=bar")
}) })
It("returns an error", func() { It("returns an error", func() {
_, err := loadFlannelSubnetEnv(subnetFile) _, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile)
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ")))
})
})
Context("when flannel subnet env is invalid with ipv6 stack", func() {
BeforeEach(func() {
onlyIpv6SubnetFile = writeSubnetEnv("foo=bar")
})
It("returns an error", func() {
_, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile)
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ")))
})
})
Context("when flannel subnet env is invalid with dual stack", func() {
BeforeEach(func() {
dualStackSubnetFile = writeSubnetEnv("foo=bar")
})
It("returns an error", func() {
_, err := loadFlannelSubnetEnv(dualStackSubnetFile)
Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ")))
}) })
}) })
}) })
}) })
Describe("getDelegateIPAM", func() { Describe("getDelegateIPAM", func() {
Context("when input IPAM is provided", func() { Context("when input IPAM is provided with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
input = makeInput(inputIPAM) onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile)
}) })
It("configures Delegate IPAM accordingly", func() { It("configures Delegate IPAM accordingly with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(subnetFile) fenv, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv) ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n" podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n"
subnet := "\"subnet\": \"10.1.17.0/24\"" subnet := "\"ranges\": [[{\"subnet\": \"10.1.17.0/24\"}]]"
expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam) buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected)) Expect(buf).Should(MatchJSON(expected))
}) })
}) })
Context("when input IPAM is provided without 'type'", func() { Context("when input IPAM is provided with ipv6 stack", func() {
BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile)
})
It("configures Delegate IPAM accordingly with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile)
Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"fc00::/48\" }\n"
subnet := "\"ranges\": [[{ \"subnet\": \"fc00::/64\" }]]"
expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected))
})
})
Context("when input IPAM is provided with dual stack", func() {
BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
dualStackInput = makeInput(inputIPAM, dualStackSubnetFile)
})
It("configures Delegate IPAM accordingly with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput))
Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(dualStackSubnetFile)
Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + ",\n" + "{ \"dst\": \"fc00::/48\" }\n"
subnet := "\"ranges\": [[{ \"subnet\": \"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]"
expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected))
})
})
Context("when input IPAM is provided without 'type' with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
input = makeInput(inputIPAM) onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile)
}) })
It("configures Delegate IPAM with 'host-local' ipam", func() { It("configures Delegate IPAM with 'host-local' ipam with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(subnetFile) fenv, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv) ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n" podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n"
subnet := "\"subnet\": \"10.1.17.0/24\"" subnet := "\"ranges\": [[{\"subnet\": \"10.1.17.0/24\"}]]"
expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected))
})
})
Context("when input IPAM is provided without 'type' with ipv6 stack", func() {
BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile)
})
It("configures Delegate IPAM with 'host-local' ipam with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile)
Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"fc00::/48\" }\n"
subnet := "\"ranges\": [[{ \"subnet\": \"fc00::/64\" }]]"
expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected))
})
})
Context("when input IPAM is provided without 'type' with dual stack", func() {
BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
dualStackInput = makeInput(inputIPAM, dualStackSubnetFile)
})
It("configures Delegate IPAM with 'host-local' ipam with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput))
Expect(err).ShouldNot(HaveOccurred())
fenv, err := loadFlannelSubnetEnv(dualStackSubnetFile)
Expect(err).ShouldNot(HaveOccurred())
ipam, err := getDelegateIPAM(conf, fenv)
Expect(err).ShouldNot(HaveOccurred())
podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + ",\n" + "{ \"dst\": \"fc00::/48\" }\n"
subnet := "\"ranges\": [[{ \"subnet\": \"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]"
expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet)
buf, _ := json.Marshal(ipam) buf, _ := json.Marshal(ipam)
Expect(buf).Should(MatchJSON(expected)) Expect(buf).Should(MatchJSON(expected))