Skip to content

Commit

Permalink
feat(dns): Rewrite dns persistence to allow virtual-outbound to be added
Browse files Browse the repository at this point in the history
Currently the persisted state on the vips was very restraining.
With the addition of virtual-outbounds we'll need something more flexible
This migrates the state to a new format that will be extensible.

Signed-off-by: Charly Molter <charly.molter@konghq.com>
  • Loading branch information
lahabana committed Aug 3, 2021
1 parent b2f88d7 commit 1188d51
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 335 deletions.
58 changes: 0 additions & 58 deletions pkg/dns/ip.go

This file was deleted.

54 changes: 0 additions & 54 deletions pkg/dns/ip_test.go

This file was deleted.

58 changes: 58 additions & 0 deletions pkg/dns/vips/global_view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package vips

import (
"net"

"github.com/Nordix/simple-ipam/pkg/ipam"
)

// GlobalView keeps a list of all hostname/ips and add the possibility to allocate new ips
type GlobalView struct {
ipam *ipam.IPAM
ipToHostname map[string]Entry
hostnameToIp map[Entry]string
}

// Reserve add an ip/host to the list of reserved ips (useful when loading an existing store).
func (g *GlobalView) Reserve(hostname Entry, ip string) error {
err := g.ipam.Reserve(net.ParseIP(ip))
if err != nil {
return err
}
g.ipToHostname[ip] = hostname
g.hostnameToIp[hostname] = ip
return nil
}

// Allocate assign an ip to a host
func (g *GlobalView) Allocate(hostname Entry) (string, error) {
ip := g.hostnameToIp[hostname]
if ip != "" {
return ip, nil
}
netIp, err := g.ipam.Allocate()
if err != nil {
return "", err
}
ip = netIp.String()
g.ipToHostname[ip] = hostname
g.hostnameToIp[hostname] = ip
return ip, nil
}

func (g *GlobalView) VipList() map[Entry]string {
return g.hostnameToIp
}

func NewGlobalView(cidr string) (*GlobalView, error) {
newIPAM, err := ipam.New(cidr)
if err != nil {
return nil, err
}

return &GlobalView{
hostnameToIp: map[Entry]string{},
ipToHostname: map[string]Entry{},
ipam: newIPAM,
}, nil
}
108 changes: 108 additions & 0 deletions pkg/dns/vips/global_view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package vips_test

import (
"fmt"
"math"

"github.com/kumahq/kuma/pkg/dns/vips"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("global view", func() {

It("should fail when no more ips", func() {
// given
gv, err := vips.NewGlobalView("192.168.0.1/32")
Expect(err).ToNot(HaveOccurred())
Expect(gv).ToNot(BeNil())

// when
ip1, err := gv.Allocate(vips.NewServiceEntry("foo.bar"))
// then
Expect(err).ToNot(HaveOccurred())
Expect(ip1).ToNot(Equal(""))

// when
ip2, err := gv.Allocate(vips.NewServiceEntry("bar.bar"))
// then
Expect(err).To(HaveOccurred())
Expect(ip2).To(Equal(""))
})

It("should allocate IPs", func() {
// given
gv, err := vips.NewGlobalView("192.168.0.1/24")
Expect(err).ToNot(HaveOccurred())
Expect(gv).ToNot(BeNil())

// when
ip1, err := gv.Allocate(vips.NewServiceEntry("foo.bar"))
// then
Expect(err).ToNot(HaveOccurred())

// when
ip2, err := gv.Allocate(vips.NewServiceEntry("bar.bar"))
// then
Expect(err).ToNot(HaveOccurred())

// then
vips := map[vips.Entry]string{
vips.NewServiceEntry("foo.bar"): ip1,
vips.NewServiceEntry("bar.bar"): ip2,
}

Expect(gv.VipList()).To(Equal(vips))
})

It("should allocate 2^16 IP addresses", func() {
// given
gv, err := vips.NewGlobalView("240.0.0.0/4")
Expect(err).ToNot(HaveOccurred())
Expect(gv).ToNot(BeNil())

for i := 0; i < math.MaxInt16; i++ {
// when
_, err := gv.Allocate(vips.NewHostEntry(fmt.Sprintf("foo-%d.mesh", i)))
// then
Expect(err).ToNot(HaveOccurred())
}
})

It("should give the same ip if we ask for a host already allocated", func() {
// given
host := vips.NewServiceEntry("foo.com")
gv, err := vips.NewGlobalView("240.0.0.0/4")
Expect(err).ToNot(HaveOccurred())
Expect(gv).ToNot(BeNil())

err = gv.Reserve(host, "240.0.0.1")
Expect(err).ToNot(HaveOccurred())

// when
ip, err := gv.Allocate(host)
Expect(err).ToNot(HaveOccurred())

// then
Expect(ip).To(Equal("240.0.0.1"))
})

It("should give the same ip if when allocating it twice", func() {
// given
host := vips.NewServiceEntry("foo.com")
gv, err := vips.NewGlobalView("240.0.0.0/4")
Expect(err).ToNot(HaveOccurred())
Expect(gv).ToNot(BeNil())

ip, err := gv.Allocate(host)
Expect(err).ToNot(HaveOccurred())

// when
ip2, err := gv.Allocate(host)
Expect(err).ToNot(HaveOccurred())

// then
Expect(ip2).To(Equal(ip))
})
})
55 changes: 31 additions & 24 deletions pkg/dns/vips/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,13 @@ func NewPersistence(resourceManager manager.ReadOnlyResourceManager, configManag
}
}

func (m *Persistence) Get() (global List, meshed map[string]List, errs error) {
func (m *Persistence) Get() (meshed map[string]*VirtualOutboundView, errs error) {
resourceList := &config_model.ConfigResourceList{}
if err := m.configManager.List(context.Background(), resourceList); err != nil {
return nil, nil, err
return nil, err
}

global = List{}
meshed = map[string]List{}
meshed = map[string]*VirtualOutboundView{}
for _, resource := range resourceList.Items {
mesh, ok := MeshFromConfigKey(resource.Meta.GetName())
if !ok {
Expand All @@ -69,53 +68,61 @@ func (m *Persistence) Get() (global List, meshed map[string]List, errs error) {
errs = multierr.Append(errs, err)
continue
}
global.Append(v)
meshed[mesh] = v
}
return
}

func (m *Persistence) unmarshal(config string) (List, error) {
v := List{}
if err := json.Unmarshal([]byte(config), &v); err == nil {
return v, nil
func (m *Persistence) unmarshal(config string) (*VirtualOutboundView, error) {
res := []VirtualOutbound{}
if err := json.Unmarshal([]byte(config), &res); err == nil {
return NewVirtualOutboundView(res), nil
}
// backwards compatibility
backwardCompatible := map[string]string{}
if err := json.Unmarshal([]byte(config), &backwardCompatible); err != nil {
return nil, err
backCompat := List{}
if err := json.Unmarshal([]byte(config), &backCompat); err != nil {
// backwards compatibility
backwardCompatible := map[string]string{}
if err := json.Unmarshal([]byte(config), &backwardCompatible); err != nil {
return nil, err
}
for service, vip := range backwardCompatible {
backCompat[NewServiceEntry(service)] = vip
}
}
v = List{}
for service, vip := range backwardCompatible {
v[NewServiceEntry(service)] = vip
for entry, vip := range backCompat {
res = append(res, VirtualOutbound{
Hostname: entry.Name,
Type: entry.Type,
Address: vip,
})
}
return v, nil
return NewVirtualOutboundView(res), nil
}

func (m *Persistence) GetByMesh(mesh string) (List, error) {
func (m *Persistence) GetByMesh(mesh string) (*VirtualOutboundView, error) {
name := fmt.Sprintf(template, mesh)
resource := config_model.NewConfigResource()
err := m.configManager.Get(context.Background(), resource, store.GetByKey(name, ""))
if err != nil {
if store.IsResourceNotFound(err) {
return List{}, nil
return NewVirtualOutboundView([]VirtualOutbound{}), nil
}
return nil, err
}

if resource.Spec.Config == "" {
return List{}, nil
return NewVirtualOutboundView([]VirtualOutbound{}), nil
}

vips, err := m.unmarshal(resource.Spec.GetConfig())
virtualOutboundView, err := m.unmarshal(resource.Spec.GetConfig())
if err != nil {
return nil, errors.Wrap(err, "could not unmarshal")
}

return vips, nil
return virtualOutboundView, nil
}

func (m *Persistence) Set(mesh string, vips List) error {
func (m *Persistence) Set(mesh string, vips *VirtualOutboundView) error {
ctx := context.Background()
name := fmt.Sprintf(template, mesh)
resource := config_model.NewConfigResource()
Expand All @@ -127,7 +134,7 @@ func (m *Persistence) Set(mesh string, vips List) error {
create = true
}

jsonBytes, err := json.Marshal(vips)
jsonBytes, err := json.Marshal(vips.GetVirtualOutbounds())
if err != nil {
return errors.Wrap(err, "unable to marshall VIP list")
}
Expand Down
Loading

0 comments on commit 1188d51

Please sign in to comment.