Compare commits

..

3 Commits

Author SHA1 Message Date
amery e33ef0019f cluster: use rings.ZoneID and rings.NodeID types
Signed-off-by: Alejandro Mery <amery@jpi.io>
2024-06-02 22:07:28 +00:00
amery 4812cb7d5d rings: fix NodeZeroMax/NodeMax to not accept .255
Signed-off-by: Alejandro Mery <amery@jpi.io>
2024-06-02 22:07:28 +00:00
amery fd3c9b5fa3 rings: add String() to RegionID, ZoneID, NodeID
Signed-off-by: Alejandro Mery <amery@jpi.io>
2024-06-02 21:28:16 +00:00
10 changed files with 70 additions and 66 deletions
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"git.jpi.io/amery/jpictl/pkg/rings"
"gopkg.in/yaml.v3"
)
@@ -45,7 +46,7 @@ func (m *Cluster) initZones(opts *ScanOptions) error {
func (m *Cluster) initZone(z *Zone, _ *ScanOptions) error {
var hasMissing bool
var lastMachineID NodeID
var lastMachineID rings.NodeID
z.zones = m
z.logger = m
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"strings"
"darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
)
const (
@@ -114,7 +115,7 @@ func (m *Cluster) scanMachines(opts *ScanOptions) error {
func (m *Cluster) scanZoneIDs(_ *ScanOptions) error {
var hasMissing bool
var lastZoneID ZoneID
var lastZoneID rings.ZoneID
m.ForEachZone(func(z *Zone) bool {
switch {
+4 -2
View File
@@ -6,6 +6,8 @@ import (
"io"
"sort"
"strings"
"git.jpi.io/amery/jpictl/pkg/rings"
)
// Env is a shell environment factory for this cluster
@@ -35,8 +37,8 @@ func (m *Cluster) Env(export bool) (*Env, error) {
}
// Zones returns the list of Zone IDs
func (m *Env) Zones() []ZoneID {
var zones []ZoneID
func (m *Env) Zones() []rings.ZoneID {
var zones []rings.ZoneID
m.ForEachZone(func(z *Zone) bool {
zones = append(zones, z.ID)
+4 -2
View File
@@ -3,6 +3,8 @@ package cluster
import (
"net/netip"
"strings"
"git.jpi.io/amery/jpictl/pkg/rings"
)
// revive:disable:line-length-limit
@@ -12,7 +14,7 @@ type Machine struct {
zone *Zone
logger `json:"-" yaml:"-"`
ID NodeID
ID rings.NodeID
Name string `json:"-" yaml:"-"`
Inactive bool `json:"inactive,omitempty" yaml:"inactive,omitempty"`
@@ -74,7 +76,7 @@ func (m *Machine) SetGateway(enabled bool) error {
}
// Zone indicates the [Zone] this machine belongs to
func (m *Machine) Zone() ZoneID {
func (m *Machine) Zone() rings.ZoneID {
return m.zone.ID
}
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
"git.jpi.io/amery/jpictl/pkg/wireguard"
)
@@ -223,7 +224,7 @@ func (m *Machine) applyWireguardPeerConfig(ring int, pc wireguard.PeerConfig) er
}
}
func (m *Machine) applyZoneNodeID(zoneID ZoneID, nodeID NodeID) error {
func (m *Machine) applyZoneNodeID(zoneID rings.ZoneID, nodeID rings.NodeID) error {
switch {
case zoneID == 0:
return fmt.Errorf("invalid %s", "zoneID")
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"time"
"darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
)
// LookupNetIP uses the DNS Resolver to get the public addresses associated
@@ -65,7 +66,7 @@ func (m *Machine) setID() error {
return err
}
m.ID = NodeID(id)
m.ID = rings.NodeID(id)
return nil
}
+16 -27
View File
@@ -5,14 +5,11 @@ import (
"io/fs"
"net/netip"
"git.jpi.io/amery/jpictl/pkg/rings"
"git.jpi.io/amery/jpictl/pkg/wireguard"
)
const (
// MaxZoneID indicates the highest ID allowed for a Zone
MaxZoneID = 0xf
// MaxNodeID indicates the highest Machine ID allowed within a Zone
MaxNodeID = 0xff - 1
// RingsCount indicates how many wireguard rings we have
RingsCount = 2
// RingZeroPort is the port wireguard uses for ring0
@@ -81,8 +78,8 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool {
type RingAddressEncoder struct {
ID int
Port uint16
Encode func(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool)
Decode func(addr netip.Addr) (zoneID ZoneID, nodeID NodeID, ok bool)
Encode func(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool)
Decode func(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool)
}
var (
@@ -110,42 +107,34 @@ 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 ZoneID) bool {
switch {
case zoneID < 0 || zoneID > MaxZoneID:
return false
default:
return true
}
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 NodeID) bool {
switch {
case nodeID < 0 || nodeID > MaxNodeID:
return false
default:
return true
}
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 ZoneID, nodeID NodeID, ok bool) {
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 {
return ZoneID(a4[2]), NodeID(a4[3]), true
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 ZoneID, nodeID NodeID) (netip.Addr, bool) {
func RingZeroAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false
@@ -157,13 +146,13 @@ func RingZeroAddress(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool) {
// 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 ZoneID, nodeID NodeID, ok bool) {
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 = ZoneID(a4[1] >> 4)
nodeID = NodeID(a4[3])
zoneID = rings.ZoneID(a4[1] >> 4)
nodeID = rings.NodeID(a4[3])
return zoneID, nodeID, true
}
}
@@ -171,7 +160,7 @@ func ParseRingOneAddress(addr netip.Addr) (zoneID ZoneID, nodeID NodeID, ok bool
}
// RingOneAddress returns a wg1 IP address
func RingOneAddress(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool) {
func RingOneAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false
-25
View File
@@ -1,25 +0,0 @@
package cluster
import "strconv"
type (
// ZoneID is the identifier of a [Zone]
ZoneID int
// NodeID is the identifier of a [Machine]
NodeID int
)
func (z ZoneID) String() string {
return idString(int(z))
}
func (n NodeID) String() string {
return idString(int(n))
}
func idString(v int) string {
if v > 0 {
return strconv.Itoa(v)
}
return ""
}
+6 -4
View File
@@ -2,6 +2,8 @@ package cluster
import (
"io/fs"
"git.jpi.io/amery/jpictl/pkg/rings"
)
var (
@@ -19,7 +21,7 @@ type Zone struct {
zones *Cluster
logger `json:"-" yaml:"-"`
ID ZoneID
ID rings.ZoneID
Name string
Regions []string `json:",omitempty" yaml:",omitempty"`
@@ -31,7 +33,7 @@ func (z *Zone) String() string {
}
// SetGateway configures a machine to be the zone's ring0 gateway
func (z *Zone) SetGateway(gatewayID NodeID, enabled bool) error {
func (z *Zone) SetGateway(gatewayID rings.NodeID, enabled bool) error {
var err error
var found bool
@@ -56,8 +58,8 @@ func (z *Zone) SetGateway(gatewayID NodeID, enabled bool) error {
}
// GatewayIDs returns the list of IDs of machines that act as ring0 gateways
func (z *Zone) GatewayIDs() ([]NodeID, int) {
var out []NodeID
func (z *Zone) GatewayIDs() ([]rings.NodeID, int) {
var out []rings.NodeID
z.ForEachMachine(func(p *Machine) bool {
if p.IsGateway() {
out = append(out, p.ID)
+32 -2
View File
@@ -3,6 +3,8 @@
package rings
import (
"fmt"
"strconv"
"syscall"
"darvaza.org/core"
@@ -14,10 +16,10 @@ const (
// 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) - 1
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) - 1
NodeZeroMax = (1 << 8) - 2
// RingZeroBits indicates the size of the prefix on the ring 0 (backbone) network.
RingZeroBits = 16
@@ -37,12 +39,20 @@ 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
@@ -53,8 +63,28 @@ 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))
}
}