Compare commits
3 Commits
main
...
dev-amery-
Author | SHA1 | Date |
---|---|---|
Alejandro Mery | 4ab2c9d068 | 10 months ago |
Alejandro Mery | 098482a890 | 10 months ago |
Alejandro Mery | 0a48a40796 | 10 months ago |
41 changed files with 541 additions and 2116 deletions
@ -1,200 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"io" |
|
||||||
"net/netip" |
|
||||||
"os" |
|
||||||
|
|
||||||
"darvaza.org/core" |
|
||||||
"github.com/spf13/cobra" |
|
||||||
|
|
||||||
"git.jpi.io/amery/jpictl/pkg/cluster" |
|
||||||
"git.jpi.io/amery/jpictl/pkg/rings" |
|
||||||
"git.jpi.io/amery/jpictl/pkg/tools" |
|
||||||
) |
|
||||||
|
|
||||||
type inventory struct { |
|
||||||
r []*cluster.Region |
|
||||||
z [][]*cluster.Zone |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) renderRingZero(out *tools.LazyBuffer) error { |
|
||||||
ring0 := netip.PrefixFrom(rings.UnsafeRingZeroAddress(0, 0, 0), rings.RingZeroBits) |
|
||||||
from, to, _ := rings.PrefixToRange(ring0) |
|
||||||
|
|
||||||
_ = out.Printf("; wg%v\n", 0) |
|
||||||
_ = out.Printf("%s\t%s-%s\n", ring0, from, to) |
|
||||||
|
|
||||||
if err := g.renderRingZeroRegions(out); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
return g.renderRingZeroZones(out) |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) renderRingZeroRegions(out *tools.LazyBuffer) error { |
|
||||||
for _, r := range g.r { |
|
||||||
if err := g.renderRingZeroRegion(out, r); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (*inventory) renderRingZeroRegion(out *tools.LazyBuffer, r *cluster.Region) error { |
|
||||||
addr := rings.UnsafeRingZeroAddress(r.ID, 0, 0) |
|
||||||
ring0r := netip.PrefixFrom(addr, rings.RingZeroBits+4) |
|
||||||
from, to, _ := rings.PrefixToRange(ring0r) |
|
||||||
|
|
||||||
_ = out.Printf("%s\t%s-%s\t# %s\n", ring0r, from, to, r.Name) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) renderRingZeroZones(out *tools.LazyBuffer) error { |
|
||||||
for i, r := range g.r { |
|
||||||
for _, z := range g.z[i] { |
|
||||||
if err := g.renderRingZeroZone(out, r, z); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
func (*inventory) renderRingZeroZone(out *tools.LazyBuffer, r *cluster.Region, z *cluster.Zone) error { |
|
||||||
addr := rings.UnsafeRingZeroAddress(r.ID, z.ID, 0) |
|
||||||
ring0rz := netip.PrefixFrom(addr, rings.RingZeroBits+4+4) |
|
||||||
from, to, _ := rings.PrefixToRange(ring0rz) |
|
||||||
|
|
||||||
_ = out.Printf("; wg%v: %s (%s)\n", 0, z.Name, r.Name) |
|
||||||
_ = out.Printf("%s\t%s-%s\t%s\n", ring0rz, from, to, z.Name) |
|
||||||
|
|
||||||
z.ForEachMachine(func(m *cluster.Machine) bool { |
|
||||||
if m.IsGateway() { |
|
||||||
addr, _ := m.RingZeroAddress() |
|
||||||
cidr := netip.PrefixFrom(addr, 32) |
|
||||||
_ = out.Printf("%s\t\t%s-%v\n", cidr, m.Name, 0) |
|
||||||
} |
|
||||||
|
|
||||||
return false |
|
||||||
}) |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) renderRingOne(out *tools.LazyBuffer) error { |
|
||||||
for i, r := range g.r { |
|
||||||
for _, z := range g.z[i] { |
|
||||||
if err := g.renderRingOneZone(out, r, z); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (*inventory) renderRingOneZone(out *tools.LazyBuffer, r *cluster.Region, z *cluster.Zone) error { |
|
||||||
ring1, err := rings.RingOnePrefix(r.ID, z.ID) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
from, to, _ := rings.PrefixToRange(ring1) |
|
||||||
|
|
||||||
_ = out.Printf("; wg%v: %s (%s)\n", 1, z.Name, r.Name) |
|
||||||
_ = out.Printf("%s\t%s-%s\t%s\n", ring1, from, to, z.Name) |
|
||||||
|
|
||||||
z.ForEachMachine(func(m *cluster.Machine) bool { |
|
||||||
addr := m.RingOneAddress() |
|
||||||
cidr := netip.PrefixFrom(addr, 32) |
|
||||||
_ = out.Printf("%s\t\t%s\n", cidr, m.Name) |
|
||||||
return false |
|
||||||
}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) Marshal() ([]byte, error) { |
|
||||||
var buf tools.LazyBuffer |
|
||||||
|
|
||||||
if err := g.renderRingZero(&buf); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if err := g.renderRingOne(&buf); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return buf.Bytes(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) WriteTo(out io.Writer) (int64, error) { |
|
||||||
b, err := g.Marshal() |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
|
|
||||||
buf := bytes.NewBuffer(b) |
|
||||||
return buf.WriteTo(out) |
|
||||||
} |
|
||||||
|
|
||||||
func genInventory(m *cluster.Cluster) (*inventory, error) { |
|
||||||
g := new(inventory) |
|
||||||
|
|
||||||
g.populateRegions(m) |
|
||||||
g.populateZones() |
|
||||||
|
|
||||||
return g, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) populateRegions(m *cluster.Cluster) { |
|
||||||
m.ForEachRegion(func(r *cluster.Region) bool { |
|
||||||
if r.IsPrimary() { |
|
||||||
g.r = append(g.r, r) |
|
||||||
} |
|
||||||
return false |
|
||||||
}) |
|
||||||
|
|
||||||
core.SliceSortFn(g.r, func(a, b *cluster.Region) bool { |
|
||||||
return a.ID < b.ID |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func (g *inventory) populateZones() { |
|
||||||
g.z = make([][]*cluster.Zone, len(g.r)) |
|
||||||
for i, r := range g.r { |
|
||||||
r.ForEachZone(func(z *cluster.Zone) bool { |
|
||||||
g.z[i] = append(g.z[i], z) |
|
||||||
return false |
|
||||||
}) |
|
||||||
|
|
||||||
core.SliceSortFn(g.z[i], func(a, b *cluster.Zone) bool { |
|
||||||
return a.ID < b.ID |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Command
|
|
||||||
var listCmd = &cobra.Command{ |
|
||||||
Use: "list", |
|
||||||
Short: "list shows the IP/CIDR inventory", |
|
||||||
PreRun: setVerbosity, |
|
||||||
RunE: func(_ *cobra.Command, _ []string) error { |
|
||||||
m, err := cfg.LoadZones(false) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
out, err := genInventory(m) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
_, err = out.WriteTo(os.Stdout) |
|
||||||
return err |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
rootCmd.AddCommand(listCmd) |
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
_ "embed" |
|
||||||
|
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
|
|
||||||
"github.com/spf13/cobra" |
|
||||||
) |
|
||||||
|
|
||||||
//go:generate sh -c "git describe | tr -d '\r\n' > .version"
|
|
||||||
//go:embed .version
|
|
||||||
var version string |
|
||||||
|
|
||||||
var versionCmd = &cobra.Command{ |
|
||||||
Use: "version", |
|
||||||
Short: "Returns jpictl's version", |
|
||||||
Args: cobra.NoArgs, |
|
||||||
Run: func(_ *cobra.Command, _ []string) { |
|
||||||
_, _ = fmt.Fprintf(os.Stdout, "%s\n", version) |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
if version == "" { |
|
||||||
version = "undetermined" |
|
||||||
} |
|
||||||
|
|
||||||
rootCmd.AddCommand(versionCmd) |
|
||||||
} |
|
@ -1,43 +1,54 @@ |
|||||||
module git.jpi.io/amery/jpictl |
module git.jpi.io/amery/jpictl |
||||||
|
|
||||||
go 1.21 |
go 1.19 |
||||||
|
|
||||||
|
replace asciigoat.org/ini => ../../../asciigoat.org/ini |
||||||
|
|
||||||
require ( |
require ( |
||||||
asciigoat.org/core v0.3.9 // indirect |
|
||||||
asciigoat.org/ini v0.2.5 |
asciigoat.org/ini v0.2.5 |
||||||
darvaza.org/cache/x/simplelru v0.1.8 // indirect |
darvaza.org/core v0.12.0 |
||||||
darvaza.org/core v0.14.2 |
|
||||||
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 |
||||||
darvaza.org/slog/handlers/discard v0.4.11 |
darvaza.org/slog/handlers/discard v0.4.11 |
||||||
darvaza.org/slog/handlers/filter v0.4.9 // indirect |
github.com/gofrs/uuid/v5 v5.0.0 |
||||||
darvaza.org/slog/handlers/zerolog v0.4.9 // indirect |
|
||||||
) |
|
||||||
|
|
||||||
require ( |
|
||||||
github.com/gofrs/uuid/v5 v5.2.0 |
|
||||||
github.com/hack-pad/hackpadfs v0.2.1 |
github.com/hack-pad/hackpadfs v0.2.1 |
||||||
github.com/libdns/cloudflare v0.1.1 |
github.com/libdns/cloudflare v0.1.0 |
||||||
github.com/libdns/libdns v0.2.2 |
github.com/libdns/libdns v0.2.1 |
||||||
|
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.20.0 |
||||||
golang.org/x/net v0.27.0 |
golang.org/x/net v0.21.0 |
||||||
gopkg.in/yaml.v3 v3.0.1 |
gopkg.in/yaml.v3 v3.0.1 |
||||||
) |
) |
||||||
|
|
||||||
require ( |
require ( |
||||||
|
asciigoat.org/core v0.3.9 // indirect |
||||||
|
darvaza.org/cache/x/simplelru v0.1.8 // indirect |
||||||
|
darvaza.org/slog/handlers/filter v0.4.9 // indirect |
||||||
|
darvaza.org/slog/handlers/zerolog v0.4.9 // indirect |
||||||
|
github.com/BurntSushi/toml v1.3.2 // indirect |
||||||
|
github.com/chavacava/garif v0.1.0 // indirect |
||||||
|
github.com/fatih/color v1.16.0 // indirect |
||||||
|
github.com/fatih/structtag v1.2.0 // indirect |
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect |
github.com/inconshreveable/mousetrap v1.1.0 // indirect |
||||||
github.com/kr/pretty v0.3.1 // indirect |
github.com/kr/pretty v0.3.1 // indirect |
||||||
github.com/mattn/go-colorable v0.1.13 // indirect |
github.com/mattn/go-colorable v0.1.13 // indirect |
||||||
github.com/mattn/go-isatty v0.0.20 // indirect |
github.com/mattn/go-isatty v0.0.20 // indirect |
||||||
github.com/miekg/dns v1.1.59 // indirect |
github.com/mattn/go-runewidth v0.0.15 // indirect |
||||||
|
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect |
||||||
|
github.com/miekg/dns v1.1.58 // indirect |
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect |
||||||
|
github.com/olekukonko/tablewriter v0.0.5 // indirect |
||||||
|
github.com/pkg/errors v0.9.1 // indirect |
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect |
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect |
github.com/rogpeppe/go-internal v1.11.0 // indirect |
||||||
github.com/rs/zerolog v1.33.0 // indirect |
github.com/rs/zerolog v1.32.0 // indirect |
||||||
|
github.com/spf13/afero v1.11.0 // indirect |
||||||
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.15.0 // indirect |
||||||
golang.org/x/sync v0.7.0 // indirect |
golang.org/x/sync v0.6.0 // indirect |
||||||
golang.org/x/sys v0.22.0 // indirect |
golang.org/x/sys v0.17.0 // indirect |
||||||
golang.org/x/text v0.16.0 // indirect |
golang.org/x/text v0.14.0 // indirect |
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect |
golang.org/x/tools v0.18.0 // indirect |
||||||
) |
) |
||||||
|
@ -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,77 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import "net/netip" |
|
||||||
|
|
||||||
// AddrFromU32 converts a 32bit value into an IPv4
|
|
||||||
// address.
|
|
||||||
func AddrFromU32(v uint32) netip.Addr { |
|
||||||
return AddrFrom4( |
|
||||||
uint(v>>24), |
|
||||||
uint(v>>16), |
|
||||||
uint(v>>8), |
|
||||||
uint(v), |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
// AddrFrom4 assembles an IPv4 address for 4 numbers.
|
|
||||||
// each number is truncated to 8-bits.
|
|
||||||
func AddrFrom4(a, b, c, d uint) netip.Addr { |
|
||||||
return netip.AddrFrom4([4]byte{ |
|
||||||
byte(a & 0xff), |
|
||||||
byte(b & 0xff), |
|
||||||
byte(c & 0xff), |
|
||||||
byte(d & 0xff), |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
// AddrToU32 converts a valid IPv4 address into it's
|
|
||||||
// 32bit numeric representation.
|
|
||||||
func AddrToU32(addr netip.Addr) (v uint32, ok bool) { |
|
||||||
if addr.IsValid() { |
|
||||||
if addr.Is4() || addr.Is4In6() { |
|
||||||
a4 := addr.As4() |
|
||||||
|
|
||||||
v = uint32(a4[0])<<24 + |
|
||||||
uint32(a4[1])<<16 + |
|
||||||
uint32(a4[2])<<8 + |
|
||||||
uint32(a4[3]) |
|
||||||
return v, true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return 0, false |
|
||||||
} |
|
||||||
|
|
||||||
// PrefixToRange returns the beginning and end of a
|
|
||||||
// [netip.Prefix] (aka CIDR or subnet).
|
|
||||||
func PrefixToRange(subnet netip.Prefix) (from, to netip.Addr, ok bool) { |
|
||||||
var u uint32 |
|
||||||
|
|
||||||
addr := subnet.Addr() |
|
||||||
if u, ok = AddrToU32(addr); ok { |
|
||||||
bits := subnet.Bits() |
|
||||||
|
|
||||||
switch { |
|
||||||
case bits > 32, bits < 0: |
|
||||||
// bad
|
|
||||||
case bits == 32: |
|
||||||
// single
|
|
||||||
from, to, ok = addr, addr, true |
|
||||||
default: |
|
||||||
// subnet
|
|
||||||
shift := 32 - bits |
|
||||||
|
|
||||||
m1 := uint32((1 << shift) - 1) |
|
||||||
m0 := uint32(0xffffffff) & ^m1 |
|
||||||
|
|
||||||
u0 := u & m0 |
|
||||||
u1 := u0 + m1 |
|
||||||
|
|
||||||
ok = true |
|
||||||
from = AddrFromU32(u0) |
|
||||||
to = AddrFromU32(u1) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return from, to, ok |
|
||||||
} |
|
@ -1,178 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"net/netip" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
func TestAddrFrom4(t *testing.T) { |
|
||||||
cases := []struct { |
|
||||||
v [4]uint |
|
||||||
s string |
|
||||||
}{ |
|
||||||
{[4]uint{0, 0, 0, 0}, "0.0.0.0"}, |
|
||||||
{[4]uint{127, 0, 0, 1}, "127.0.0.1"}, |
|
||||||
{[4]uint{4096 + 127, 0, 0, 1}, "127.0.0.1"}, |
|
||||||
{[4]uint{257, 258, 259, 260}, "1.2.3.4"}, |
|
||||||
{[4]uint{255, 255, 255, 255}, "255.255.255.255"}, |
|
||||||
} |
|
||||||
|
|
||||||
for i, tc := range cases { |
|
||||||
fn := fmt.Sprintf("%v.%v.%v.%v", tc.v[0], tc.v[1], tc.v[2], tc.v[3]) |
|
||||||
addr := AddrFrom4(tc.v[0], tc.v[1], tc.v[2], tc.v[3]) |
|
||||||
s := addr.String() |
|
||||||
|
|
||||||
if s == tc.s { |
|
||||||
t.Logf("[%v/%v]: %s → %s", i, len(cases), fn, s) |
|
||||||
} else { |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %s (expected %s)", i, len(cases), fn, s, tc.s) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestAddrU32Invalid(t *testing.T) { |
|
||||||
cases := []netip.Addr{ |
|
||||||
{}, |
|
||||||
netip.IPv6Unspecified(), |
|
||||||
netip.IPv6Loopback(), |
|
||||||
} |
|
||||||
|
|
||||||
for i, tc := range cases { |
|
||||||
v, ok := AddrToU32(tc) |
|
||||||
switch { |
|
||||||
case !ok && v == 0: |
|
||||||
t.Logf("[%v/%v]: %s → %v %v", i, len(cases), tc, 0, false) |
|
||||||
default: |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %v %v (expected %v %v)", i, len(cases), |
|
||||||
tc, v, ok, 0, false) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestAddrU32Valid(t *testing.T) { |
|
||||||
cases := []netip.Addr{ |
|
||||||
netip.IPv4Unspecified(), |
|
||||||
AddrFrom4(0, 0, 0, 0), |
|
||||||
AddrFrom4(1, 2, 3, 4), |
|
||||||
AddrFrom4(10, 20, 30, 40), |
|
||||||
AddrFrom4(127, 0, 0, 1), |
|
||||||
AddrFrom4(255, 255, 255, 255), |
|
||||||
MustParseAddr("::ffff:1.2.3.4"), |
|
||||||
} |
|
||||||
|
|
||||||
for i, tc := range cases { |
|
||||||
u32, ok := AddrToU32(tc) |
|
||||||
if !ok { |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %v %v", i, len(cases), tc, u32, ok) |
|
||||||
continue |
|
||||||
} |
|
||||||
|
|
||||||
addr := AddrFromU32(u32) |
|
||||||
if tc.Is4In6() { |
|
||||||
ok = addr.Compare(tc.Unmap()) == 0 |
|
||||||
} else { |
|
||||||
ok = addr.Compare(tc) == 0 |
|
||||||
} |
|
||||||
|
|
||||||
if ok { |
|
||||||
t.Logf("[%v/%v]: %s → %v → %s", i, len(cases), tc, u32, addr) |
|
||||||
} else { |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %v → %s", i, len(cases), tc, u32, addr) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func MustParseAddr(s string) netip.Addr { |
|
||||||
addr, err := netip.ParseAddr(s) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
return addr |
|
||||||
} |
|
||||||
|
|
||||||
func MustParsePrefix(s string) netip.Prefix { |
|
||||||
subnet, err := netip.ParsePrefix(s) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
return subnet |
|
||||||
} |
|
||||||
|
|
||||||
func TestPrefixToRangeValid(t *testing.T) { |
|
||||||
cases := []struct { |
|
||||||
subnet netip.Prefix |
|
||||||
from netip.Addr |
|
||||||
to netip.Addr |
|
||||||
}{ |
|
||||||
{ |
|
||||||
MustParsePrefix("127.0.0.1/32"), |
|
||||||
MustParseAddr("127.0.0.1"), |
|
||||||
MustParseAddr("127.0.0.1"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("127.0.0.1/24"), |
|
||||||
MustParseAddr("127.0.0.0"), |
|
||||||
MustParseAddr("127.0.0.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("127.0.1.2/16"), |
|
||||||
MustParseAddr("127.0.0.0"), |
|
||||||
MustParseAddr("127.0.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("127.1.2.3/8"), |
|
||||||
MustParseAddr("127.0.0.0"), |
|
||||||
MustParseAddr("127.255.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.20.30.40/12"), |
|
||||||
MustParseAddr("10.16.0.0"), |
|
||||||
MustParseAddr("10.31.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.20.30.40/20"), |
|
||||||
MustParseAddr("10.20.16.0"), |
|
||||||
MustParseAddr("10.20.31.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.0.0.0/12"), |
|
||||||
MustParseAddr("10.0.0.0"), |
|
||||||
MustParseAddr("10.15.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.16.0.0/12"), |
|
||||||
MustParseAddr("10.16.0.0"), |
|
||||||
MustParseAddr("10.31.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.32.0.0/12"), |
|
||||||
MustParseAddr("10.32.0.0"), |
|
||||||
MustParseAddr("10.47.255.255"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
MustParsePrefix("10.48.0.0/12"), |
|
||||||
MustParseAddr("10.48.0.0"), |
|
||||||
MustParseAddr("10.63.255.255"), |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
for i, tc := range cases { |
|
||||||
from, to, ok := PrefixToRange(tc.subnet) |
|
||||||
if ok && from.IsValid() && to.IsValid() && |
|
||||||
from.Compare(tc.from) == 0 && |
|
||||||
to.Compare(tc.to) == 0 { |
|
||||||
//
|
|
||||||
t.Logf("[%v/%v]: %s → %s - %s", |
|
||||||
i, len(cases), |
|
||||||
tc.subnet, |
|
||||||
from, to) |
|
||||||
} else { |
|
||||||
t.Errorf("ERROR: [%v/%v]: %q → %s - %s %v (expected %s - %s %v)", |
|
||||||
i, len(cases), |
|
||||||
tc.subnet, |
|
||||||
from, to, ok, |
|
||||||
tc.from, tc.to, true) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,122 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/netip" |
|
||||||
) |
|
||||||
|
|
||||||
// DecodeAddress extracts ring address fields from a given 10.0.0.0/8
|
|
||||||
// address.
|
|
||||||
//
|
|
||||||
// revive:disable:function-result-limit
|
|
||||||
func DecodeAddress[T ~uint | NodeID](addr netip.Addr) (RingID, RegionID, ZoneID, T) { |
|
||||||
// revive:enable:function-result-limit
|
|
||||||
if addr.IsValid() { |
|
||||||
if addr.Is4In6() { |
|
||||||
addr = addr.Unmap() |
|
||||||
} |
|
||||||
|
|
||||||
if addr.Is4() { |
|
||||||
a4 := addr.As4() |
|
||||||
return unsafeDecodeAddress[T](a4[0], a4[1], a4[2], a4[3]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return UnspecifiedRingID, 0, 0, 0 |
|
||||||
} |
|
||||||
|
|
||||||
// revive:disable:function-result-limit
|
|
||||||
func unsafeDecodeAddress[T ~uint | NodeID](a, b, c, d byte) (RingID, RegionID, ZoneID, T) { |
|
||||||
// revive:enable:function-result-limit
|
|
||||||
switch { |
|
||||||
case a != 10: |
|
||||||
return UnspecifiedRingID, 0, 0, 0 |
|
||||||
case b == 0x00: |
|
||||||
// 10.00.RZ.dd
|
|
||||||
k := RingZeroID |
|
||||||
r := RegionID(c >> 4) |
|
||||||
z := ZoneID(c & 0xf) |
|
||||||
n := T(d) |
|
||||||
|
|
||||||
return k, r, z, n |
|
||||||
case b&0xf0 != 0: |
|
||||||
// 10.Rb.cc.dd
|
|
||||||
k := RingThreeID |
|
||||||
r := RegionID(b >> 4) |
|
||||||
|
|
||||||
n2 := T(b & 0x0f) |
|
||||||
n1 := T(c) |
|
||||||
n0 := T(d) |
|
||||||
n := n0 + n1<<8 + n2<<16 |
|
||||||
|
|
||||||
return k, r, 0, n |
|
||||||
case c&0xf0 != 0: |
|
||||||
// 10.0R.Zc.dd
|
|
||||||
k := RingOneID |
|
||||||
r := RegionID(b) |
|
||||||
z := ZoneID(c >> 4) |
|
||||||
|
|
||||||
n1 := T(c & 0x0f) |
|
||||||
n0 := T(d) |
|
||||||
n := n0 + n1<<8 |
|
||||||
|
|
||||||
return k, r, z, n |
|
||||||
default: |
|
||||||
// 10.0R.0c.dd
|
|
||||||
k := RingTwoID |
|
||||||
r := RegionID(b) |
|
||||||
|
|
||||||
n1 := T(c & 0x0f) |
|
||||||
n0 := T(d) |
|
||||||
n := n0 + n1<<8 |
|
||||||
|
|
||||||
return k, r, 0, n |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeRingZeroAddress attempts to extract region, zone and node identifiers
|
|
||||||
// from a given ring 0 address.
|
|
||||||
//
|
|
||||||
// revive:disable:function-result-limit
|
|
||||||
func DecodeRingZeroAddress(addr netip.Addr) (RegionID, ZoneID, NodeID, bool) { |
|
||||||
// revive:enable:function-result-limit
|
|
||||||
k, r, z, n := DecodeAddress[NodeID](addr) |
|
||||||
if k == RingZeroID { |
|
||||||
return r, z, n, true |
|
||||||
} |
|
||||||
|
|
||||||
return 0, 0, 0, false |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeRingOneAddress attempts to extract region, zone and node identifiers
|
|
||||||
// from a given ring 1 address.
|
|
||||||
//
|
|
||||||
// revive:disable:function-result-limit
|
|
||||||
func DecodeRingOneAddress(addr netip.Addr) (RegionID, ZoneID, NodeID, bool) { |
|
||||||
// revive:enable:function-result-limit
|
|
||||||
k, r, z, n := DecodeAddress[NodeID](addr) |
|
||||||
if k == RingOneID { |
|
||||||
return r, z, n, true |
|
||||||
} |
|
||||||
|
|
||||||
return 0, 0, 0, false |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeRingTwoAddress attempts to extract region and unique identifier for
|
|
||||||
// a kubernetes service from a given ring 2 address.
|
|
||||||
func DecodeRingTwoAddress(addr netip.Addr) (RegionID, uint, bool) { |
|
||||||
k, r, _, n := DecodeAddress[uint](addr) |
|
||||||
if k == RingTwoID { |
|
||||||
return r, n, true |
|
||||||
} |
|
||||||
return 0, 0, false |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeRingThreeAddress attempts to extract region and unique identifier for
|
|
||||||
// a kubernetes pod from a given ring 3 address.
|
|
||||||
func DecodeRingThreeAddress(addr netip.Addr) (RegionID, uint, bool) { |
|
||||||
k, r, _, n := DecodeAddress[uint](addr) |
|
||||||
if k == RingThreeID { |
|
||||||
return r, n, true |
|
||||||
} |
|
||||||
return 0, 0, false |
|
||||||
} |
|
@ -1,53 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"net/netip" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
func TestDecodeRingZeroAddress(t *testing.T) { |
|
||||||
RZNDecodeTest(t, "DecodeRingZeroAddress", DecodeRingZeroAddress, []RZNDecodeTestCase{ |
|
||||||
{1, 1, 50, MustParseAddr("10.0.17.50"), true}, |
|
||||||
{1, 2, 50, MustParseAddr("10.0.18.50"), true}, |
|
||||||
{2, 3, 1, MustParseAddr("10.0.35.1"), true}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TesDecodetRingOneAddress(t *testing.T) { |
|
||||||
RZNDecodeTest(t, "DecodeRingOneAddress", DecodeRingOneAddress, []RZNDecodeTestCase{ |
|
||||||
{1, 1, 50, MustParseAddr("10.1.16.50"), true}, |
|
||||||
{1, 2, 50, MustParseAddr("10.1.32.50"), true}, |
|
||||||
{2, 3, 300, MustParseAddr("10.2.49.44"), true}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
type RZNDecodeTestCase struct { |
|
||||||
region RegionID |
|
||||||
zone ZoneID |
|
||||||
node NodeID |
|
||||||
addr netip.Addr |
|
||||||
ok bool |
|
||||||
} |
|
||||||
|
|
||||||
func RZNDecodeTest(t *testing.T, |
|
||||||
fnName string, fn func(netip.Addr) (RegionID, ZoneID, NodeID, bool), |
|
||||||
cases []RZNDecodeTestCase) { |
|
||||||
//
|
|
||||||
for i, tc := range cases { |
|
||||||
s := fmt.Sprintf("%s(%q)", fnName, tc.addr) |
|
||||||
|
|
||||||
r, z, n, ok := fn(tc.addr) |
|
||||||
|
|
||||||
switch { |
|
||||||
case ok != tc.ok, r != tc.region, z != tc.zone, n != tc.node: |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %v %v %v %v (expected %v %v %v %v)", |
|
||||||
i, len(cases), s, |
|
||||||
r, z, n, ok, |
|
||||||
tc.region, tc.zone, tc.node, tc.ok) |
|
||||||
default: |
|
||||||
t.Logf("[%v/%v]: %s → %v %v %v %v", i, len(cases), s, |
|
||||||
r, z, n, ok) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,154 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import "net/netip" |
|
||||||
|
|
||||||
// RingZeroPrefix represents the backbone that connects gateways
|
|
||||||
// of the different Ring 1 networks.
|
|
||||||
//
|
|
||||||
// The ring 0 network corresponds to what would be ring 2 for region_id 0.
|
|
||||||
// 10.0.0.0-10.0.255.255
|
|
||||||
func RingZeroPrefix(region RegionID, zone ZoneID) (cidr netip.Prefix, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
case !zone.Valid(): |
|
||||||
err = ErrOutOfRange(zone, "zone") |
|
||||||
default: |
|
||||||
addr := UnsafeRingZeroAddress(region, zone, 0) |
|
||||||
cidr = netip.PrefixFrom(addr, RingZeroBits) |
|
||||||
} |
|
||||||
|
|
||||||
return cidr, err |
|
||||||
} |
|
||||||
|
|
||||||
// RingZeroAddress returns a Ring 0 address for a particular node.
|
|
||||||
//
|
|
||||||
// A ring 0 address looks like 10.0.(region_id << 4 + zone_id).(node_id)/20
|
|
||||||
func RingZeroAddress(region RegionID, zone ZoneID, node NodeID) (addr netip.Addr, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
case !zone.Valid(): |
|
||||||
err = ErrOutOfRange(zone, "zone") |
|
||||||
case !node.ValidZero(): |
|
||||||
err = ErrOutOfRange(node, "node") |
|
||||||
default: |
|
||||||
addr = UnsafeRingZeroAddress(region, zone, node) |
|
||||||
} |
|
||||||
|
|
||||||
return addr, err |
|
||||||
} |
|
||||||
|
|
||||||
// RingOnePrefix represents a (virtual) local network of a zone.
|
|
||||||
//
|
|
||||||
// Ring 1 is `10.(region_id).(zone_id << 4).(node_id)/20` network
|
|
||||||
// grouped under what would be Ring 2 for region_id 0.
|
|
||||||
// There are 12 bits worth of nodes but nodes under 255 are special
|
|
||||||
// as they also get a slot on Ring 0.
|
|
||||||
func RingOnePrefix(region RegionID, zone ZoneID) (cidr netip.Prefix, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
case !zone.Valid(): |
|
||||||
err = ErrOutOfRange(zone, "zone") |
|
||||||
default: |
|
||||||
addr := UnsafeRingOneAddress(region, zone, 0) |
|
||||||
cidr = netip.PrefixFrom(addr, RingOneBits) |
|
||||||
} |
|
||||||
return cidr, err |
|
||||||
} |
|
||||||
|
|
||||||
// RingOneAddress returns a Ring 1 address for a particular node.
|
|
||||||
//
|
|
||||||
// A ring 1 address is `10.(region_id).(zone_id << 4).(node_id)/20`
|
|
||||||
// but the node_id can take up to 12 bits.
|
|
||||||
func RingOneAddress(region RegionID, zone ZoneID, node NodeID) (addr netip.Addr, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
case !zone.Valid(): |
|
||||||
err = ErrOutOfRange(zone, "zone") |
|
||||||
case !node.Valid(): |
|
||||||
err = ErrOutOfRange(node, "node") |
|
||||||
default: |
|
||||||
addr = UnsafeRingOneAddress(region, zone, node) |
|
||||||
} |
|
||||||
return addr, err |
|
||||||
} |
|
||||||
|
|
||||||
// RingTwoPrefix represents the services of a cluster
|
|
||||||
//
|
|
||||||
// Ring 2 subnets are of the form `10.(region_id).0.0/20`,
|
|
||||||
// using the address space that would belong to the ring 3
|
|
||||||
// region_id 0.
|
|
||||||
func RingTwoPrefix(region RegionID) (cidr netip.Prefix, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
default: |
|
||||||
addr := UnsafeRingTwoAddress(region, 0) |
|
||||||
cidr = netip.PrefixFrom(addr, RingTwoBits) |
|
||||||
} |
|
||||||
return cidr, err |
|
||||||
} |
|
||||||
|
|
||||||
// RingThreePrefix returns the subnet corresponding to
|
|
||||||
// the pods of a cluster.
|
|
||||||
//
|
|
||||||
// Ring 3 is a `10.(region_id << 4).0.0/12` network
|
|
||||||
func RingThreePrefix(region RegionID) (subnet netip.Prefix, err error) { |
|
||||||
switch { |
|
||||||
case !region.Valid(): |
|
||||||
err = ErrOutOfRange(region, "region") |
|
||||||
default: |
|
||||||
addr := UnsafeRingThreeAddress(region, 0) |
|
||||||
subnet = netip.PrefixFrom(addr, RingThreeBits) |
|
||||||
} |
|
||||||
return subnet, err |
|
||||||
} |
|
||||||
|
|
||||||
// UnsafeRingZeroAddress is equivalent ot RingZeroAddress but without validating
|
|
||||||
// the input.
|
|
||||||
func UnsafeRingZeroAddress(region RegionID, zone ZoneID, node NodeID) netip.Addr { |
|
||||||
r := uint(region) |
|
||||||
z := uint(zone) |
|
||||||
n := uint(node) |
|
||||||
|
|
||||||
return AddrFrom4(10, 0, r<<4+z, n) |
|
||||||
} |
|
||||||
|
|
||||||
// UnsafeRingOneAddress is equivalent ot RingOneAddress but without validating
|
|
||||||
// the input.
|
|
||||||
func UnsafeRingOneAddress(region RegionID, zone ZoneID, node NodeID) netip.Addr { |
|
||||||
r := uint(region) |
|
||||||
z := uint(zone) |
|
||||||
n := uint(node) |
|
||||||
|
|
||||||
n1 := n >> 8 |
|
||||||
n0 := n >> 0 |
|
||||||
|
|
||||||
return AddrFrom4(10, r, z<<4+n1, n0) |
|
||||||
} |
|
||||||
|
|
||||||
// UnsafeRingTwoAddress is equivalent ot RingTwoAddress but without validating
|
|
||||||
// the input.
|
|
||||||
func UnsafeRingTwoAddress(region RegionID, n uint) netip.Addr { |
|
||||||
r := uint(region) |
|
||||||
|
|
||||||
n1 := n >> 8 |
|
||||||
n0 := n >> 0 |
|
||||||
|
|
||||||
return AddrFrom4(10, r, n1, n0) |
|
||||||
} |
|
||||||
|
|
||||||
// UnsafeRingThreeAddress is equivalent ot RingThreeAddress but without validating
|
|
||||||
// the input.
|
|
||||||
func UnsafeRingThreeAddress(region RegionID, n uint) netip.Addr { |
|
||||||
r := uint(region) |
|
||||||
|
|
||||||
n2 := n >> 16 |
|
||||||
n1 := n >> 8 |
|
||||||
n0 := n >> 0 |
|
||||||
|
|
||||||
return AddrFrom4(10, r<<4+n2, n1, n0) |
|
||||||
} |
|
@ -1,63 +0,0 @@ |
|||||||
package rings |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"net/netip" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
func TestRingZeroAddress(t *testing.T) { |
|
||||||
RZNTest(t, "RingZeroAddress", RingZeroAddress, []RZNTestCase{ |
|
||||||
{1, 1, 50, MustParseAddr("10.0.17.50")}, |
|
||||||
{1, 2, 50, MustParseAddr("10.0.18.50")}, |
|
||||||
{2, 3, 1, MustParseAddr("10.0.35.1")}, |
|
||||||
{2, 3, 300, netip.Addr{}}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestRingOneAddress(t *testing.T) { |
|
||||||
RZNTest(t, "RingOneAddress", RingOneAddress, []RZNTestCase{ |
|
||||||
{1, 1, 50, MustParseAddr("10.1.16.50")}, |
|
||||||
{1, 2, 50, MustParseAddr("10.1.32.50")}, |
|
||||||
{2, 3, 300, MustParseAddr("10.2.49.44")}, |
|
||||||
{1, 20, 50, netip.Addr{}}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
type RZNTestCase struct { |
|
||||||
region RegionID |
|
||||||
zone ZoneID |
|
||||||
node NodeID |
|
||||||
addr netip.Addr |
|
||||||
} |
|
||||||
|
|
||||||
func RZNTest(t *testing.T, |
|
||||||
fnName string, fn func(RegionID, ZoneID, NodeID) (netip.Addr, error), |
|
||||||
cases []RZNTestCase) { |
|
||||||
//
|
|
||||||
for i, tc := range cases { |
|
||||||
s := fmt.Sprintf("%s(%v, %v, %v)", fnName, |
|
||||||
tc.region, |
|
||||||
tc.zone, |
|
||||||
tc.node, |
|
||||||
) |
|
||||||
|
|
||||||
addr, err := fn(tc.region, tc.zone, tc.node) |
|
||||||
|
|
||||||
switch { |
|
||||||
case !tc.addr.IsValid(): |
|
||||||
// expect error
|
|
||||||
if err != nil { |
|
||||||
t.Logf("[%v/%v]: %s → %s", i, len(cases), s, err) |
|
||||||
} else { |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %s (expected %s)", i, len(cases), s, addr, "error") |
|
||||||
} |
|
||||||
case err != nil: |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %s (expected %s)", i, len(cases), s, err, tc.addr) |
|
||||||
case addr.Compare(tc.addr) != 0: |
|
||||||
t.Errorf("ERROR: [%v/%v]: %s → %s (expected %s)", i, len(cases), s, addr, tc.addr) |
|
||||||
default: |
|
||||||
t.Logf("[%v/%v]: %s → %s", i, len(cases), s, addr) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,116 +0,0 @@ |
|||||||
// Package rings provides logic to work with the four rings
|
|
||||||
// of a cluster
|
|
||||||
package rings |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"strconv" |
|
||||||
"syscall" |
|
||||||
|
|
||||||
"darvaza.org/core" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
// UnspecifiedRingID is the zero value of RingID and not considered
|
|
||||||
// valid.
|
|
||||||
UnspecifiedRingID RingID = iota |
|
||||||
RingZeroID // RingZeroID is the RingID for RingZero (backbone)
|
|
||||||
RingOneID // RingOneID is the RingID for RingOne (local zone)
|
|
||||||
RingTwoID // RingTwoID is the RingID for RingTwo (region services)
|
|
||||||
RingThreeID // RingThreeID is the RingID for RingThree (region cluster pods)
|
|
||||||
|
|
||||||
// RingMax indicates the highest [Ring] identifier
|
|
||||||
RingMax = RingThreeID |
|
||||||
|
|
||||||
// RegionMax indicates the highest number that can be used for a [RegionID].
|
|
||||||
RegionMax = (1 << 4) - 1 |
|
||||||
// ZoneMax indicates the highest number that can be used for a [ZoneID].
|
|
||||||
ZoneMax = (1 << 4) - 1 |
|
||||||
// NodeMax indicates the highest number that can be used for a [NodeID].
|
|
||||||
NodeMax = (1 << 12) - 2 |
|
||||||
// NodeZeroMax indicates the highest number that can be used for a [NodeID]
|
|
||||||
// when its a gateway connected to Ring 0 (backbone).
|
|
||||||
NodeZeroMax = (1 << 8) - 2 |
|
||||||
|
|
||||||
// RingZeroBits indicates the size of the prefix on the ring 0 (backbone) network.
|
|
||||||
RingZeroBits = 16 |
|
||||||
// RingOneBits indicates the size of the prefix on the ring 1 (lan) network.
|
|
||||||
RingOneBits = 20 |
|
||||||
// RingTwoBits indicates the size of the prefix on the ring 2 (services) network
|
|
||||||
// of all kubernetes clusters.
|
|
||||||
RingTwoBits = 20 |
|
||||||
// RingThreeBits indicates the size of the prefix on the ring 3 (pods) network
|
|
||||||
// of the kubernetes cluster of a region.
|
|
||||||
RingThreeBits = 12 |
|
||||||
) |
|
||||||
|
|
||||||
// RingID identifies a Ring
|
|
||||||
type RingID int |
|
||||||
|
|
||||||
// Valid tells a [RingID] is within the valid range.
|
|
||||||
func (n RingID) Valid() bool { return n > 0 && n <= RingMax } |
|
||||||
|
|
||||||
func (n RingID) String() string { |
|
||||||
return idString(n) |
|
||||||
} |
|
||||||
|
|
||||||
// A Ring identifies what ring an address belongs to
|
|
||||||
type Ring interface { |
|
||||||
ID() RingID |
|
||||||
} |
|
||||||
|
|
||||||
// RegionID is the identifier of a region, valid between 1 and [RegionMax].
|
|
||||||
type RegionID int |
|
||||||
|
|
||||||
// Valid tells a [RegionID] is within the valid range.
|
|
||||||
func (n RegionID) Valid() bool { return n > 0 && n <= RegionMax } |
|
||||||
|
|
||||||
func (n RegionID) String() string { |
|
||||||
return idString(n) |
|
||||||
} |
|
||||||
|
|
||||||
// ZoneID is the identifier of a zone within a region, valid between 1 and [ZoneMax].
|
|
||||||
type ZoneID int |
|
||||||
|
|
||||||
// Valid tells a [ZoneID] is within the valid range.
|
|
||||||
func (n ZoneID) Valid() bool { return n > 0 && n <= ZoneMax } |
|
||||||
|
|
||||||
func (n ZoneID) String() string { |
|
||||||
return idString(n) |
|
||||||
} |
|
||||||
|
|
||||||
// NodeID is the identifier of a machine within a zone of a region, valid between
|
|
||||||
// 1 and [NodeMax], but between 1 and [NodeZeroMax] if it will be a zone gateway.
|
|
||||||
type NodeID int |
|
||||||
|
|
||||||
// Valid tells a [NodeID] is within the valid range.
|
|
||||||
func (n NodeID) Valid() bool { return n > 0 && n <= NodeMax } |
|
||||||
|
|
||||||
// ValidZero tells a [NodeID] is within the valid range for a gateway.
|
|
||||||
func (n NodeID) ValidZero() bool { return n > 0 && n <= NodeZeroMax } |
|
||||||
|
|
||||||
func (n NodeID) String() string { |
|
||||||
return idString(n) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrOutOfRange is an error indicating the value of a field
|
|
||||||
// is out of range.
|
|
||||||
func ErrOutOfRange[T ~int | ~uint32](value T, field string) error { |
|
||||||
return core.Wrap(syscall.EINVAL, "%s out of range (%v)", field, value) |
|
||||||
} |
|
||||||
|
|
||||||
type intID interface { |
|
||||||
~int |
|
||||||
Valid() bool |
|
||||||
} |
|
||||||
|
|
||||||
func idString[T intID](p T) string { |
|
||||||
switch { |
|
||||||
case p == 0: |
|
||||||
return "unspecified" |
|
||||||
case p.Valid(): |
|
||||||
return strconv.Itoa(int(p)) |
|
||||||
default: |
|
||||||
return fmt.Sprintf("invalid (%v)", int(p)) |
|
||||||
} |
|
||||||
} |
|
@ -1,72 +0,0 @@ |
|||||||
package tools |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"fmt" |
|
||||||
"io" |
|
||||||
) |
|
||||||
|
|
||||||
// LazyBuffer is a [bytes.Buffer] that minimizes counting and error checks.
|
|
||||||
type LazyBuffer bytes.Buffer |
|
||||||
|
|
||||||
// Sys returns the underlying [bytes.Buffer].
|
|
||||||
func (buf *LazyBuffer) Sys() *bytes.Buffer { |
|
||||||
if buf == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return (*bytes.Buffer)(buf) |
|
||||||
} |
|
||||||
|
|
||||||
// Len tells the size in bytes of the currently stored data.
|
|
||||||
func (buf *LazyBuffer) Len() int { return buf.Sys().Len() } |
|
||||||
|
|
||||||
// String returns the stored data as string.
|
|
||||||
func (buf *LazyBuffer) String() string { return buf.Sys().String() } |
|
||||||
|
|
||||||
// Bytes returns the stored data as a bytes slice.
|
|
||||||
func (buf *LazyBuffer) Bytes() []byte { return buf.Sys().Bytes() } |
|
||||||
|
|
||||||
// Write implements the standard io.Writer interface.
|
|
||||||
func (buf *LazyBuffer) Write(b []byte) (int, error) { return buf.Sys().Write(b) } |
|
||||||
|
|
||||||
// WriteTo implements the standard WriteTo() interface.
|
|
||||||
func (buf *LazyBuffer) WriteTo(out io.Writer) (int64, error) { return buf.Sys().WriteTo(out) } |
|
||||||
|
|
||||||
// Print appends the [fmt.Print] equivalent to the buffer.
|
|
||||||
func (buf *LazyBuffer) Print(a ...any) error { |
|
||||||
_, err := fmt.Fprint(buf.Sys(), a...) |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// Println appends the [fmt.Println] equivalent to the buffer.
|
|
||||||
func (buf *LazyBuffer) Println(a ...any) error { |
|
||||||
_, err := fmt.Fprintln(buf.Sys(), a...) |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// Printf appends the [fmt.Printf] equivalent to the buffer.
|
|
||||||
func (buf *LazyBuffer) Printf(format string, a ...any) error { |
|
||||||
_, err := fmt.Fprintf(buf.Sys(), format, a...) |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// WriteRunes appends the given runes as UTF-8 characters to the buffer.
|
|
||||||
func (buf *LazyBuffer) WriteRunes(runes ...rune) { |
|
||||||
for _, r := range runes { |
|
||||||
_, _ = buf.Sys().WriteRune(r) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// WriteBytes writes the given byte arrays to the buffer.
|
|
||||||
func (buf *LazyBuffer) WriteBytes(s ...[]byte) { |
|
||||||
for _, b := range s { |
|
||||||
_, _ = buf.Sys().Write(b) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// WriteStrings writes the given strings as UTF-8 to the buffer.
|
|
||||||
func (buf *LazyBuffer) WriteStrings(strings ...string) { |
|
||||||
for _, s := range strings { |
|
||||||
_, _ = buf.Sys().WriteString(s) |
|
||||||
} |
|
||||||
} |
|
@ -1,11 +1,7 @@ |
|||||||
// Package tools contains helpers
|
//go:build tools
|
||||||
package tools |
|
||||||
|
|
||||||
import "io" |
package tools |
||||||
|
|
||||||
// LazyClose closes an [io.Closer] and discards the error
|
import ( |
||||||
func LazyClose(p io.Closer) { |
_ "github.com/mgechev/revive" |
||||||
if p != nil { |
) |
||||||
_ = p.Close() |
|
||||||
} |
|
||||||
} |
|
||||||
|
Loading…
Reference in new issue