Compare commits

..

2 Commits

Author SHA1 Message Date
amery 6293125ddf cluster: decouple RingID from WireguardInterfaceID
Signed-off-by: Alejandro Mery <amery@jpi.io>
2024-06-03 18:14:24 +00:00
amery 4bcccb0dbd Merge branch 'pr-amery-rings' into next-amery 2024-06-03 16:21:59 +00:00
13 changed files with 171 additions and 248 deletions
+6 -6
View File
@@ -4,7 +4,7 @@ go 1.19
require ( require (
asciigoat.org/ini v0.2.5 asciigoat.org/ini v0.2.5
darvaza.org/core v0.14.2 darvaza.org/core v0.13.3
darvaza.org/resolver v0.9.2 darvaza.org/resolver v0.9.2
darvaza.org/sidecar v0.4.0 darvaza.org/sidecar v0.4.0
darvaza.org/slog v0.5.7 darvaza.org/slog v0.5.7
@@ -15,8 +15,8 @@ require (
github.com/libdns/libdns v0.2.2 github.com/libdns/libdns v0.2.2
github.com/mgechev/revive v1.3.7 github.com/mgechev/revive v1.3.7
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
golang.org/x/crypto v0.25.0 golang.org/x/crypto v0.23.0
golang.org/x/net v0.27.0 golang.org/x/net v0.25.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@@ -46,7 +46,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.21.0 // indirect
) )
+13 -12
View File
@@ -4,8 +4,8 @@ asciigoat.org/ini v0.2.5 h1:4gRIp9rU+XQt8+HMqZO5R7GavMv9Yl2+N+je6djDIAE=
asciigoat.org/ini v0.2.5/go.mod h1:gmXzJ9XFqf1NLk5nQkj04USQ4tMtdRJHNQX6vp3DzjU= asciigoat.org/ini v0.2.5/go.mod h1:gmXzJ9XFqf1NLk5nQkj04USQ4tMtdRJHNQX6vp3DzjU=
darvaza.org/cache/x/simplelru v0.1.8 h1:rvFucut4wKYbsYc994yR3P0M08NqlsvZxr5G4QK82tw= darvaza.org/cache/x/simplelru v0.1.8 h1:rvFucut4wKYbsYc994yR3P0M08NqlsvZxr5G4QK82tw=
darvaza.org/cache/x/simplelru v0.1.8/go.mod h1:Mv1isOJTcXYK+aK0AvUe+/3KpRTXDsYga6rdTS/upNs= darvaza.org/cache/x/simplelru v0.1.8/go.mod h1:Mv1isOJTcXYK+aK0AvUe+/3KpRTXDsYga6rdTS/upNs=
darvaza.org/core v0.14.2 h1:6p0iznuGfVGbBp+CnkZTw1b76j6Q/j4ffDztZXrrlK8= darvaza.org/core v0.13.3 h1:DOsidY49WXsWiJulOIxDq578h/3ekgx0trWxbvgv5bc=
darvaza.org/core v0.14.2/go.mod h1:C+B0GRNLB+/asGfxjQ9XZERdk7xaFxzt5xTIBPiNm2M= darvaza.org/core v0.13.3/go.mod h1:47Ydh67KnzjLNu1mzX3r2zpphbxQqEaihMsUq5GflQ4=
darvaza.org/resolver v0.9.2 h1:sUX6LZ1eN5TzJW7L4m7HM+BvwBeWl8dYYDGVSe+AIhk= darvaza.org/resolver v0.9.2 h1:sUX6LZ1eN5TzJW7L4m7HM+BvwBeWl8dYYDGVSe+AIhk=
darvaza.org/resolver v0.9.2/go.mod h1:XWqPhrxoOKNzRuSozOwmE1M6QVqQL28jEdxylnIO8Nw= darvaza.org/resolver v0.9.2/go.mod h1:XWqPhrxoOKNzRuSozOwmE1M6QVqQL28jEdxylnIO8Nw=
darvaza.org/sidecar v0.4.0 h1:wHghxzLsiT82WDBBUf34aTqtOvRBg4UbxVIJgKNXRVA= darvaza.org/sidecar v0.4.0 h1:wHghxzLsiT82WDBBUf34aTqtOvRBg4UbxVIJgKNXRVA=
@@ -94,23 +94,24 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-43
View File
@@ -1,43 +0,0 @@
package cluster
import (
"net/netip"
"git.jpi.io/amery/jpictl/pkg/rings"
)
// RingOnePrefix returns the ring 1 subnet of this [Zone].
func (z *Zone) RingOnePrefix() netip.Prefix {
subnet, err := rings.RingOnePrefix(z.RegionID(), z.ID)
if err != nil {
panic(err)
}
return subnet
}
// RingOnePrefix returns the ring 1 subnet this [Machine] belongs
// to.
func (m *Machine) RingOnePrefix() netip.Prefix {
return m.zone.RingOnePrefix()
}
// RingZeroAddress returns the ring 0 address of the [Machine]
// if it can act as gateway.
func (m *Machine) RingZeroAddress() (netip.Addr, bool) {
addr, err := rings.RingZeroAddress(m.Region(), m.Zone(), m.ID)
if err != nil {
return netip.Addr{}, false
}
return addr, true
}
// RingOneAddress returns the ring 1 address of the [Machine]
func (m *Machine) RingOneAddress() netip.Addr {
addr, err := rings.RingOneAddress(m.Region(), m.Zone(), m.ID)
if err != nil {
panic(err)
}
return addr
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (m *Cluster) GenCephConfig() (*ceph.Config, error) {
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
for _, p := range z.GetCephMonitors() { for _, p := range z.GetCephMonitors() {
addr := p.RingOneAddress() addr, _ := RingOneAddress(z.ID, p.ID)
cfg.Global.Monitors = append(cfg.Global.Monitors, p.Name) cfg.Global.Monitors = append(cfg.Global.Monitors, p.Name)
cfg.Global.MonitorsAddr = append(cfg.Global.MonitorsAddr, addr) cfg.Global.MonitorsAddr = append(cfg.Global.MonitorsAddr, addr)
+2 -2
View File
@@ -4,7 +4,6 @@ import (
"os" "os"
"darvaza.org/slog" "darvaza.org/slog"
"git.jpi.io/amery/jpictl/pkg/ceph" "git.jpi.io/amery/jpictl/pkg/ceph"
) )
@@ -15,7 +14,8 @@ type cephScanTODO struct {
func (todo *cephScanTODO) checkMachine(p *Machine) bool { func (todo *cephScanTODO) checkMachine(p *Machine) bool {
// on ceph all addresses are ring1 // on ceph all addresses are ring1
addr := p.RingOneAddress().String() ring1, _ := RingOneAddress(p.Zone(), p.ID)
addr := ring1.String()
if _, found := todo.names[p.Name]; found { if _, found := todo.names[p.Name]; found {
// found on the TODO by name // found on the TODO by name
+40 -87
View File
@@ -4,10 +4,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"sort"
"strings" "strings"
"darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings" "git.jpi.io/amery/jpictl/pkg/rings"
) )
@@ -37,24 +36,20 @@ func (m *Cluster) Env(export bool) (*Env, error) {
return env, nil return env, nil
} }
// Zones returns the list of Zone IDs of a region, // Zones returns the list of Zone IDs
// or from all if none is specified. func (m *Env) Zones() []rings.ZoneID {
func (m *Env) Zones(r *Region) []rings.ZoneID {
var zones []rings.ZoneID var zones []rings.ZoneID
iter := core.IIf[ZoneIterator](r != nil, r, m) m.ForEachZone(func(z *Zone) bool {
iter.ForEachZone(func(z *Zone) bool {
zones = append(zones, z.ID) zones = append(zones, z.ID)
return false return false
}) })
core.SliceSortOrdered(zones)
return zones return zones
} }
// RegionsNames returns a sorted list of primary regions names // Regions returns the list of primary regions
func (m *Env) RegionsNames() []string { func (m *Env) Regions() []string {
var regions []string var regions []string
m.ForEachRegion(func(r *Region) bool { m.ForEachRegion(func(r *Region) bool {
@@ -65,21 +60,7 @@ func (m *Env) RegionsNames() []string {
return false return false
}) })
core.SliceSortOrdered(regions) sort.Strings(regions)
return regions
}
// Regions returns a sorted list of primary regions IDs
func (m *Env) Regions() (regions []rings.RegionID) {
m.ForEachRegion(func(r *Region) bool {
if r.IsPrimary() {
regions = append(regions, r.ID)
}
return false
})
core.SliceSortOrdered(regions)
return regions return regions
} }
@@ -91,75 +72,41 @@ func (m *Env) WriteTo(w io.Writer) (int64, error) {
m.writeEnvVar(&buf, m.cephFSID, "FSID") m.writeEnvVar(&buf, m.cephFSID, "FSID")
} }
regions := m.getRegions() m.writeEnvVar(&buf, genEnvStrings(m.Regions()), "REGIONS")
ids := core.SliceMap(regions, func(_ []rings.RegionID, r *Region) (out []rings.RegionID) { m.writeEnvVar(&buf, genEnvInts(m.Zones()), "ZONES")
return append(out, r.ID)
})
names := core.SliceMap(regions, func(_ []string, r *Region) (out []string) {
return append(out, r.Name)
})
m.writeEnvVar(&buf, genEnvInts(ids), "REGIONS") m.ForEachZone(func(z *Zone) bool {
m.writeEnvVar(&buf, genEnvStrings(names), "REGIONS_NAMES") m.writeEnvZone(&buf, z)
return false
for _, r := range regions { })
m.writeEnvRegion(&buf, r)
}
return buf.WriteTo(w) return buf.WriteTo(w)
} }
func (m *Env) getRegions() (out []*Region) { func (m *Env) writeEnvZone(w io.Writer, z *Zone) {
m.ForEachRegion(func(r *Region) bool { zoneID := z.ID
if r.IsPrimary() {
out = append(out, r)
}
return false
})
core.SliceSortFn(out, func(a, b *Region) bool { // ZONE{zoneID}
return a.ID < b.ID m.writeEnvVar(w, genEnvZoneNodes(z), "ZONE%v", zoneID)
})
return out // ZONE{zoneID}_NAME
} m.writeEnvVar(w, z.Name, "ZONE%v_%s", zoneID, "NAME")
func (m *Env) writeEnvRegion(w io.Writer, r *Region) {
regionID := r.ID
// REGION{regionID}_NAME // ZONE{zoneID}_GW
m.writeEnvVar(w, r.Name, "REGION%v_%s", regionID, "NAME")
// REGION{regionID}_ZONES
m.writeEnvVar(w, genEnvInts(m.Zones(r)), "REGION%v_%s", regionID, "ZONES")
r.ForEachZone(func(z *Zone) bool {
m.writeEnvZone(w, r, z)
return false
})
}
func (m *Env) writeEnvZone(w io.Writer, r *Region, z *Zone) {
zonePrefix := fmt.Sprintf("REGION%v_ZONE%v", r.ID, z.ID)
monPrefix := zonePrefix + "_MON"
// REGION{regionID}_ZONE{zoneID}
m.writeEnvVar(w, genEnvZoneNodes(z), zonePrefix)
// REGION{regionID}_ZONE{zoneID}_NAME
m.writeEnvVar(w, z.Name, zonePrefix+"_NAME")
// REGION{regionID}_ZONE{zoneID}_GW
gateways, _ := z.GatewayIDs() gateways, _ := z.GatewayIDs()
m.writeEnvVar(w, genEnvInts(gateways), zonePrefix+"_GW") m.writeEnvVar(w, genEnvInts(gateways), "ZONE%v_%s", zoneID, "GW")
// ZONE{zoneID}_REGION
m.writeEnvVar(w, genEnvZoneRegion(z), "ZONE%v_%s", zoneID, "REGION")
// Ceph // Ceph
monitors := z.GetCephMonitors() monitors := z.GetCephMonitors()
// REGION{regionID}_MON{zone_ID} // MON{zoneID}_NAME
m.writeEnvVar(w, genEnvZoneCephMonNames(monitors), monPrefix) m.writeEnvVar(w, genEnvZoneCephMonNames(monitors), "MON%v_%s", zoneID, "NAME")
// REGION{regionID}_MON{zone_ID}_IP // MON{zoneID}_IP
m.writeEnvVar(w, genEnvZoneCephMonIPs(monitors), monPrefix+"_IP") m.writeEnvVar(w, genEnvZoneCephMonIPs(monitors), "MON%v_%s", zoneID, "IP")
// REGION{regionID}_MON{zone_ID}_ID // MON{zoneID}_ID
m.writeEnvVar(w, genEnvZoneCephMonIDs(monitors), monPrefix+"_ID") m.writeEnvVar(w, genEnvZoneCephMonIDs(monitors), "MON%v_%s", zoneID, "ID")
} }
func (m *Env) writeEnvVar(w io.Writer, value string, name string, args ...any) { func (m *Env) writeEnvVar(w io.Writer, value string, name string, args ...any) {
@@ -184,15 +131,14 @@ func (m *Env) writeEnvVar(w io.Writer, value string, name string, args ...any) {
} }
} }
func genEnvInts[T core.Signed](values []T) string { func genEnvInts[T ~int | ~uint](values []T) string {
var buf bytes.Buffer var buf bytes.Buffer
for _, v := range values { for _, v := range values {
if buf.Len() > 0 { if buf.Len() > 0 {
_, _ = buf.WriteRune(' ') _, _ = buf.WriteRune(' ')
} }
_, _ = buf.WriteString(fmt.Sprintf("%v", v))
_, _ = buf.WriteString(fmt.Sprintf("%v", int64(v)))
} }
return buf.String() return buf.String()
@@ -216,6 +162,13 @@ func genEnvZoneNodes(z *Zone) string {
return "" return ""
} }
func genEnvZoneRegion(z *Zone) string {
if z != nil && z.region != nil {
return z.region.Name
}
return ""
}
func genEnvZoneCephMonNames(m Machines) string { func genEnvZoneCephMonNames(m Machines) string {
var buf strings.Builder var buf strings.Builder
m.ForEachMachine(func(p *Machine) bool { m.ForEachMachine(func(p *Machine) bool {
@@ -232,7 +185,7 @@ func genEnvZoneCephMonNames(m Machines) string {
func genEnvZoneCephMonIPs(m Machines) string { func genEnvZoneCephMonIPs(m Machines) string {
var buf strings.Builder var buf strings.Builder
m.ForEachMachine(func(p *Machine) bool { m.ForEachMachine(func(p *Machine) bool {
addr := p.RingOneAddress() addr, _ := RingOneAddress(p.Zone(), p.ID)
if buf.Len() > 0 { if buf.Len() > 0 {
_, _ = buf.WriteRune(' ') _, _ = buf.WriteRune(' ')
+3 -3
View File
@@ -71,14 +71,14 @@ func (p *Machine) WriteHosts() error {
func (z *Zone) genHosts(out *hostsFile, p *Machine) { func (z *Zone) genHosts(out *hostsFile, p *Machine) {
var names []string var names []string
ip := p.RingOneAddress() ip, _ := RingOneAddress(p.zone.ID, p.ID)
names = append(names, p.Name) names = append(names, p.Name)
if p.CephMonitor { if p.CephMonitor {
names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "ceph")) names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "ceph"))
names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "k3s")) names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "k3s"))
if z.Is(p.Region(), p.Zone()) { if z.ID == p.zone.ID {
names = append(names, "ceph") names = append(names, "ceph")
names = append(names, "k3s") names = append(names, "k3s")
} }
@@ -94,7 +94,7 @@ func (z *Zone) genHosts(out *hostsFile, p *Machine) {
if p.IsGateway() { if p.IsGateway() {
var s string var s string
ip, _ = p.RingZeroAddress() ip, _ = RingZeroAddress(p.zone.ID, p.ID)
s = fmt.Sprintf("%s-%v", p.Name, 0) s = fmt.Sprintf("%s-%v", p.Name, 0)
entry = hostsEntry{ entry = hostsEntry{
+3 -8
View File
@@ -53,13 +53,13 @@ func (m *Machine) IsActive() bool {
// IsGateway tells if the Machine is a ring0 gateway // IsGateway tells if the Machine is a ring0 gateway
func (m *Machine) IsGateway() bool { func (m *Machine) IsGateway() bool {
_, ok := m.getRingInfo(rings.RingZeroID) _, ok := m.getRingInfo(0)
return ok return ok
} }
// SetGateway enables/disables a Machine ring0 integration // SetGateway enables/disables a Machine ring0 integration
func (m *Machine) SetGateway(enabled bool) error { func (m *Machine) SetGateway(enabled bool) error {
ri, found := m.getRingInfo(rings.RingZeroID) ri, found := m.getRingInfo(0)
switch { switch {
case !found && !enabled: case !found && !enabled:
return nil return nil
@@ -72,7 +72,7 @@ func (m *Machine) SetGateway(enabled bool) error {
} }
ri.Enabled = enabled ri.Enabled = enabled
return m.SyncWireguardConfig(rings.RingZeroID) return m.SyncWireguardConfig(0)
} }
// Zone indicates the [Zone] this machine belongs to // Zone indicates the [Zone] this machine belongs to
@@ -80,11 +80,6 @@ func (m *Machine) Zone() rings.ZoneID {
return m.zone.ID return m.zone.ID
} }
// Region indicates the [Region] this machine belongs to
func (m *Machine) Region() rings.RegionID {
return m.zone.RegionID()
}
func (m *Machine) getPeerByName(name string) (*Machine, bool) { func (m *Machine) getPeerByName(name string) (*Machine, bool) {
return m.zone.zones.GetMachineByName(name) return m.zone.zones.GetMachineByName(name)
} }
+15 -24
View File
@@ -16,6 +16,7 @@ import (
func (m *Machine) GetWireguardKeys(ringID rings.RingID) (wireguard.KeyPair, error) { func (m *Machine) GetWireguardKeys(ringID rings.RingID) (wireguard.KeyPair, error) {
var ( var (
data []byte data []byte
err error
out wireguard.KeyPair out wireguard.KeyPair
) )
@@ -139,12 +140,12 @@ func (m *Machine) tryApplyWireguardConfig(ring rings.RingID) error {
func (m *Machine) applyWireguardConfigNode(ring rings.RingID, wg *wireguard.Config) error { func (m *Machine) applyWireguardConfigNode(ring rings.RingID, wg *wireguard.Config) error {
addr := wg.GetAddress() addr := wg.GetAddress()
if !core.IsZero(addr) { if !core.IsZero(addr) {
regionID, zoneID, nodeID, ok := Rings[ring].Decode(addr) zoneID, nodeID, ok := Rings[ring].Decode(addr)
if !ok { if !ok {
return fmt.Errorf("%s: invalid address", addr) return fmt.Errorf("%s: invalid address", addr)
} }
if err := m.applyZoneNodeID(regionID, zoneID, nodeID); err != nil { if err := m.applyZoneNodeID(zoneID, nodeID); err != nil {
return core.Wrap(err, "%s: invalid address", addr) return core.Wrap(err, "%s: invalid address", addr)
} }
} }
@@ -170,7 +171,7 @@ func (m *Machine) applyWireguardConfig(ring rings.RingID, wg *wireguard.Config)
WithField("subsystem", "wireguard"). WithField("subsystem", "wireguard").
WithField("node", m.Name). WithField("node", m.Name).
WithField("peer", peer.Endpoint.Host). WithField("peer", peer.Endpoint.Host).
WithField("ring", MustWireguardInterfaceID(ring)). WithField("ring", ring).
Print("ignoring unknown endpoint") Print("ignoring unknown endpoint")
case err != nil: case err != nil:
return core.Wrap(err, "peer") return core.Wrap(err, "peer")
@@ -196,7 +197,7 @@ func (m *Machine) applyRingInfo(ring rings.RingID, new *RingInfo) error {
// first, append // first, append
m.debug(). m.debug().
WithField("node", m.Name). WithField("node", m.Name).
WithField("ring", MustWireguardInterfaceID(ring)). WithField("ring", ring).
Print("found") Print("found")
m.Rings = append(m.Rings, new) m.Rings = append(m.Rings, new)
return nil return nil
@@ -220,9 +221,7 @@ func (m *Machine) applyWireguardInterfaceConfig(ring rings.RingID,
return m.applyRingInfo(ring, ri) return m.applyRingInfo(ring, ri)
} }
func (m *Machine) applyWireguardPeerConfig(ring rings.RingID, func (m *Machine) applyWireguardPeerConfig(ring rings.RingID, pc wireguard.PeerConfig) error {
pc wireguard.PeerConfig) error {
//
peer, found := m.getPeerByName(pc.Endpoint.Name()) peer, found := m.getPeerByName(pc.Endpoint.Name())
switch { switch {
case !found: case !found:
@@ -245,29 +244,21 @@ func (m *Machine) applyWireguardPeerConfig(ring rings.RingID,
} }
} }
func (m *Machine) applyZoneNodeID(regionID rings.RegionID, func (m *Machine) applyZoneNodeID(zoneID rings.ZoneID, nodeID rings.NodeID) error {
zoneID rings.ZoneID, nodeID rings.NodeID) error {
//
switch { switch {
case !regionID.Valid(): case zoneID == 0:
return fmt.Errorf("invalid %s", "regionID")
case !zoneID.Valid():
return fmt.Errorf("invalid %s", "zoneID") return fmt.Errorf("invalid %s", "zoneID")
case !nodeID.Valid(): case nodeID == 0:
return fmt.Errorf("invalid %s", "nodeID") return fmt.Errorf("invalid %s", "nodeID")
case m.ID != nodeID: case m.ID != nodeID:
return fmt.Errorf("invalid %s: %v ≠ %v", "nodeID", m.ID, nodeID) return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.ID, nodeID)
case m.zone.ID != 0 && m.zone.ID != zoneID: case m.zone.ID != 0 && m.zone.ID != zoneID:
return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.zone.ID, zoneID) return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.zone.ID, zoneID)
case m.Region() != regionID: case m.zone.ID == 0:
return fmt.Errorf("invalid %s: %v ≠ %v", "regionID", m.Region(), regionID) m.zone.ID = zoneID
default:
if m.zone.ID == 0 {
m.zone.ID = zoneID
}
return nil
} }
return nil
} }
func (m *Machine) setRingDefaults(ri *RingInfo) error { func (m *Machine) setRingDefaults(ri *RingInfo) error {
@@ -275,7 +266,7 @@ func (m *Machine) setRingDefaults(ri *RingInfo) error {
m.info(). m.info().
WithField("subsystem", "wireguard"). WithField("subsystem", "wireguard").
WithField("node", m.Name). WithField("node", m.Name).
WithField("ring", ri.Ring). WithField("ring", ri.Ring.RingID()).
Print("generating key pair") Print("generating key pair")
kp, err := wireguard.NewKeyPair() kp, err := wireguard.NewKeyPair()
+1 -1
View File
@@ -78,7 +78,7 @@ func (m *Machine) scan(_ *ScanOptions) error {
m.error(err). m.error(err).
WithField("subsystem", "wireguard"). WithField("subsystem", "wireguard").
WithField("node", m.Name). WithField("node", m.Name).
WithField("ring", MustWireguardInterfaceID(ring.ID)). WithField("ring", ring.ID).
Print() Print()
return err return err
} }
+84 -23
View File
@@ -136,8 +136,8 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool {
type RingAddressEncoder struct { type RingAddressEncoder struct {
ID rings.RingID ID rings.RingID
Port uint16 Port uint16
Encode func(rings.RegionID, rings.ZoneID, rings.NodeID) (netip.Addr, error) Encode func(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool)
Decode func(addr netip.Addr) (rings.RegionID, rings.ZoneID, rings.NodeID, bool) Decode func(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool)
} }
var ( var (
@@ -145,15 +145,15 @@ var (
RingZero = RingAddressEncoder{ RingZero = RingAddressEncoder{
ID: rings.RingZeroID, ID: rings.RingZeroID,
Port: RingZeroPort, Port: RingZeroPort,
Decode: rings.DecodeRingZeroAddress, Decode: ParseRingZeroAddress,
Encode: rings.RingZeroAddress, Encode: RingZeroAddress,
} }
// RingOne is a wg1 address encoder/decoder // RingOne is a wg1 address encoder/decoder
RingOne = RingAddressEncoder{ RingOne = RingAddressEncoder{
ID: rings.RingOneID, ID: rings.RingOneID,
Port: RingOnePort, Port: RingOnePort,
Decode: rings.DecodeRingOneAddress, Decode: ParseRingOneAddress,
Encode: rings.RingOneAddress, Encode: RingOneAddress,
} }
// Rings provides indexed access to the ring address encoders // Rings provides indexed access to the ring address encoders
Rings = []RingAddressEncoder{ Rings = []RingAddressEncoder{
@@ -162,6 +162,72 @@ var (
} }
) )
// ValidZoneID checks if the given zoneID is a valid 4 bit zone number.
//
// 0 is reserved, and only allowed when composing CIDRs.
func ValidZoneID(zoneID rings.ZoneID) bool {
return zoneID == 0 || zoneID.Valid()
}
// ValidNodeID checks if the given nodeID is a valid 8 bit number.
// nodeID is unique within a Zone.
// 0 is reserved, and only allowed when composing CIDRs.
func ValidNodeID(nodeID rings.NodeID) bool {
return nodeID == 0 || nodeID.Valid()
}
// ParseRingZeroAddress extracts zone and node ID from a wg0 [netip.Addr]
// wg0 addresses are of the form `10.0.{{zoneID}}.{{nodeID}}`
func ParseRingZeroAddress(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool) {
if addr.IsValid() {
a4 := addr.As4()
if a4[0] == 10 && a4[1] == 0 {
zoneID = rings.ZoneID(a4[2])
nodeID = rings.NodeID(a4[3])
return zoneID, nodeID, true
}
}
return 0, 0, false
}
// RingZeroAddress returns a wg0 IP address
func RingZeroAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false
default:
a4 := [4]uint8{10, 0, uint8(zoneID), uint8(nodeID)}
return netip.AddrFrom4(a4), true
}
}
// ParseRingOneAddress extracts zone and node ID from a wg1 [netip.Addr]
// wg1 addresses are of the form `10.{{zoneID << 4}}.{{nodeID}}`
func ParseRingOneAddress(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool) {
if addr.IsValid() {
a4 := addr.As4()
if a4[0] == 10 && a4[2] == 0 {
zoneID = rings.ZoneID(a4[1] >> 4)
nodeID = rings.NodeID(a4[3])
return zoneID, nodeID, true
}
}
return 0, 0, false
}
// RingOneAddress returns a wg1 IP address
func RingOneAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false
default:
a4 := [4]uint8{10, uint8(zoneID << 4), 0, uint8(nodeID)}
return netip.AddrFrom4(a4), true
}
}
var ( var (
_ MachineIterator = (*Ring)(nil) _ MachineIterator = (*Ring)(nil)
_ ZoneIterator = (*Ring)(nil) _ ZoneIterator = (*Ring)(nil)
@@ -184,15 +250,14 @@ func (r *Ring) AddPeer(p *Machine) bool {
nodeID := p.ID nodeID := p.ID
zoneID := p.Zone() zoneID := p.Zone()
regionID := p.Region() addr, _ := r.Encode(zoneID, nodeID)
addr, _ := r.Encode(regionID, zoneID, nodeID)
rp := &RingPeer{ rp := &RingPeer{
Node: p, Node: p,
Address: addr, Address: addr,
PrivateKey: ri.Keys.PrivateKey, PrivateKey: ri.Keys.PrivateKey,
PeerConfig: wireguard.PeerConfig{ PeerConfig: wireguard.PeerConfig{
Name: fmt.Sprintf("%s-%v", p.Name, ri.Ring), Name: fmt.Sprintf("%s-%v", p.Name, r.ID),
PublicKey: ri.Keys.PublicKey, PublicKey: ri.Keys.PublicKey,
Endpoint: wireguard.EndpointAddress{ Endpoint: wireguard.EndpointAddress{
Host: p.FullName(), Host: p.FullName(),
@@ -215,27 +280,27 @@ func (r *Ring) AddPeer(p *Machine) bool {
} }
func (r *Ring) setRingZeroAllowedIPs(rp *RingPeer) { func (r *Ring) setRingZeroAllowedIPs(rp *RingPeer) {
regionID, zoneID, _, _ := r.Decode(rp.Address) zoneID, _, _ := r.Decode(rp.Address)
// everyone on ring0 is a gateway to ring1 // everyone on ring0 is a gateway to ring1
subnet, _ := rings.RingOnePrefix(regionID, zoneID) addr, _ := RingOneAddress(zoneID, 0)
rp.AllowSubnet(subnet) rp.AllowCIDR(addr, 12)
// peer // peer
rp.AllowCIDR(rp.Address, 32) rp.AllowCIDR(rp.Address, 32)
} }
func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) { func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) {
regionID, zoneID, _, _ := r.Decode(rp.Address) zoneID, _, _ := r.Decode(rp.Address)
// peer // peer
rp.AllowCIDR(rp.Address, 32) rp.AllowCIDR(rp.Address, 32)
// ring1 gateways connect to all other ring1 networks // ring1 gateways connect to all other ring1 networks
r.ForEachZone(func(z *Zone) bool { r.ForEachZone(func(z *Zone) bool {
if !z.Is(regionID, zoneID) { if z.ID != zoneID {
subnet := z.RingOnePrefix() addr, _ := r.Encode(z.ID, 0)
rp.AllowSubnet(subnet) rp.AllowCIDR(addr, 12)
} }
return false return false
}) })
@@ -244,7 +309,7 @@ func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) {
r.ForEachZone(func(z *Zone) bool { r.ForEachZone(func(z *Zone) bool {
z.ForEachMachine(func(p *Machine) bool { z.ForEachMachine(func(p *Machine) bool {
if p.IsGateway() { if p.IsGateway() {
addr, _ := p.RingZeroAddress() addr, _ := RingZeroAddress(z.ID, p.ID)
rp.AllowCIDR(addr, 32) rp.AllowCIDR(addr, 32)
} }
return false return false
@@ -311,12 +376,8 @@ type RingPeer struct {
// AllowCIDR allows an IP range via this peer // AllowCIDR allows an IP range via this peer
func (rp *RingPeer) AllowCIDR(addr netip.Addr, bits int) { func (rp *RingPeer) AllowCIDR(addr netip.Addr, bits int) {
rp.AllowSubnet(netip.PrefixFrom(addr, bits)) cidr := netip.PrefixFrom(addr, bits)
} rp.PeerConfig.AllowedIPs = append(rp.PeerConfig.AllowedIPs, cidr)
// AllowSubnet allows an IP range via this peer
func (rp *RingPeer) AllowSubnet(subnet netip.Prefix) {
rp.PeerConfig.AllowedIPs = append(rp.PeerConfig.AllowedIPs, subnet)
} }
// NewRing composes a new Ring for Wireguard setup // NewRing composes a new Ring for Wireguard setup
+3 -3
View File
@@ -61,10 +61,10 @@ func pruneWireguardConfig(m MachineIterator, ring rings.RingID) error {
// PruneWireguardConfig deletes the wgN.conf file if its // PruneWireguardConfig deletes the wgN.conf file if its
// presence on the ring is disabled // presence on the ring is disabled
func (m *Machine) PruneWireguardConfig(ring rings.RingID) error { func (m *Machine) PruneWireguardConfig(ringID rings.RingID) error {
_, ok := m.getRingInfo(ring) _, ok := m.getRingInfo(ringID)
if !ok { if !ok {
return m.RemoveWireguardConfig(ring) return m.RemoveWireguardConfig(ringID)
} }
return nil return nil
-35
View File
@@ -70,38 +70,3 @@ func (z *Zone) GatewayIDs() ([]rings.NodeID, int) {
return out, len(out) return out, len(out)
} }
// RegionID returns the primary [Region] of a [Zone].
func (z *Zone) RegionID() rings.RegionID {
if z != nil && z.region != nil {
return z.region.ID
}
return 0
}
// Is checks if the given [rings.RegionID] and [rings.ZoneID] match
// the [Zone].
func (z *Zone) Is(regionID rings.RegionID, zoneID rings.ZoneID) bool {
switch {
case z.ID != zoneID:
return false
case z.RegionID() != regionID:
return false
default:
return true
}
}
// Eq checks if two [Zone]s are the same.
func (z *Zone) Eq(z2 *Zone) bool {
switch {
case z == nil, z2 == nil:
return false
case z.ID != z2.ID:
return false
case z.RegionID() != z2.RegionID():
return false
default:
return true
}
}