Browse Source

cluster: use rings.ZoneID and rings.NodeID types

Signed-off-by: Alejandro Mery <amery@jpi.io>
Alejandro Mery 6 months ago
parent
commit
7f849415ca
  1. 2
      cmd/jpictl/dns.go
  2. 3
      cmd/jpictl/gateway.go
  3. 6
      pkg/cluster/cluster_import.go
  4. 4
      pkg/cluster/cluster_scan.go
  5. 57
      pkg/cluster/env.go
  6. 6
      pkg/cluster/machine.go
  7. 3
      pkg/cluster/machine_rings.go
  8. 3
      pkg/cluster/machine_scan.go
  9. 43
      pkg/cluster/rings.go
  10. 10
      pkg/cluster/zones.go

2
cmd/jpictl/dns.go

@ -52,7 +52,7 @@ func populateDNSManager(mgr *dns.Manager, m *cluster.Cluster) error {
m.ForEachZone(func(z *cluster.Zone) bool { m.ForEachZone(func(z *cluster.Zone) bool {
z.ForEachMachine(func(p *cluster.Machine) bool { z.ForEachMachine(func(p *cluster.Machine) bool {
err = mgr.AddHost(ctx, z.Name, p.ID, p.IsActive(), p.PublicAddresses...) err = mgr.AddHost(ctx, z.Name, int(p.ID), p.IsActive(), p.PublicAddresses...)
return err != nil return err != nil
}) })

3
cmd/jpictl/gateway.go

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -128,7 +127,7 @@ func gatewayListAll(zi cluster.ZoneIterator) error {
return false return false
} }
for _, i := range ids { for _, i := range ids {
sIDs = append(sIDs, strconv.Itoa(i)) sIDs = append(sIDs, i.String())
} }
b.WriteString(strings.Join(sIDs, ", ")) b.WriteString(strings.Join(sIDs, ", "))
b.WriteString("\n") b.WriteString("\n")

6
pkg/cluster/cluster_import.go

@ -6,6 +6,8 @@ import (
"os" "os"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
func (m *Cluster) init(opts *ScanOptions) error { func (m *Cluster) init(opts *ScanOptions) error {
@ -45,7 +47,7 @@ func (m *Cluster) initZones(opts *ScanOptions) error {
func (m *Cluster) initZone(z *Zone, _ *ScanOptions) error { func (m *Cluster) initZone(z *Zone, _ *ScanOptions) error {
var hasMissing bool var hasMissing bool
var lastMachineID int var lastMachineID rings.NodeID
z.zones = m z.zones = m
z.logger = m z.logger = m
@ -58,7 +60,7 @@ func (m *Cluster) initZone(z *Zone, _ *ScanOptions) error {
case p.ID == 0: case p.ID == 0:
hasMissing = true hasMissing = true
case p.ID > lastMachineID: case p.ID > lastMachineID:
lastMachineID = z.ID lastMachineID = p.ID
} }
return false return false

4
pkg/cluster/cluster_scan.go

@ -7,6 +7,8 @@ import (
"strings" "strings"
"darvaza.org/core" "darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
const ( const (
@ -114,7 +116,7 @@ func (m *Cluster) scanMachines(opts *ScanOptions) error {
func (m *Cluster) scanZoneIDs(_ *ScanOptions) error { func (m *Cluster) scanZoneIDs(_ *ScanOptions) error {
var hasMissing bool var hasMissing bool
var lastZoneID int var lastZoneID rings.ZoneID
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
switch { switch {

57
pkg/cluster/env.go

@ -6,6 +6,8 @@ import (
"io" "io"
"sort" "sort"
"strings" "strings"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
// Env is a shell environment factory for this cluster // 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 // Zones returns the list of Zone IDs
func (m *Env) Zones() []int { func (m *Env) Zones() []rings.ZoneID {
var zones []int var zones []rings.ZoneID
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
zones = append(zones, z.ID) zones = append(zones, z.ID)
@ -70,8 +72,8 @@ func (m *Env) WriteTo(w io.Writer) (int64, error) {
m.writeEnvVar(&buf, m.cephFSID, "FSID") m.writeEnvVar(&buf, m.cephFSID, "FSID")
} }
m.writeEnvVarStrings(&buf, m.Regions(), "REGIONS") m.writeEnvVar(&buf, genEnvStrings(m.Regions()), "REGIONS")
m.writeEnvVarInts(&buf, m.Zones(), "ZONES") m.writeEnvVar(&buf, genEnvInts(m.Zones()), "ZONES")
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
m.writeEnvZone(&buf, z) m.writeEnvZone(&buf, z)
@ -92,7 +94,7 @@ func (m *Env) writeEnvZone(w io.Writer, z *Zone) {
// ZONE{zoneID}_GW // ZONE{zoneID}_GW
gateways, _ := z.GatewayIDs() gateways, _ := z.GatewayIDs()
m.writeEnvVarInts(w, gateways, "ZONE%v_%s", zoneID, "GW") m.writeEnvVar(w, genEnvInts(gateways), "ZONE%v_%s", zoneID, "GW")
// ZONE{zoneID}_REGION // ZONE{zoneID}_REGION
m.writeEnvVar(w, genEnvZoneRegion(z), "ZONE%v_%s", zoneID, "REGION") m.writeEnvVar(w, genEnvZoneRegion(z), "ZONE%v_%s", zoneID, "REGION")
@ -107,32 +109,6 @@ func (m *Env) writeEnvZone(w io.Writer, z *Zone) {
m.writeEnvVar(w, genEnvZoneCephMonIDs(monitors), "MON%v_%s", zoneID, "ID") m.writeEnvVar(w, genEnvZoneCephMonIDs(monitors), "MON%v_%s", zoneID, "ID")
} }
func (m *Env) writeEnvVarInts(w io.Writer, value []int, name string, args ...any) {
var buf bytes.Buffer
for _, v := range value {
if buf.Len() > 0 {
_, _ = fmt.Fprint(&buf, " ")
}
_, _ = fmt.Fprintf(&buf, "%v", v)
}
m.writeEnvVar(w, buf.String(), name, args...)
}
func (m *Env) writeEnvVarStrings(w io.Writer, value []string, name string, args ...any) {
var buf bytes.Buffer
for _, v := range value {
if buf.Len() > 0 {
_, _ = fmt.Fprint(&buf, " ")
}
_, _ = fmt.Fprintf(&buf, "%s", v)
}
m.writeEnvVar(w, buf.String(), name, args...)
}
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) {
var prefix string var prefix string
@ -155,6 +131,23 @@ func (m *Env) writeEnvVar(w io.Writer, value string, name string, args ...any) {
} }
} }
func genEnvInts[T ~int | ~uint](values []T) string {
var buf bytes.Buffer
for _, v := range values {
if buf.Len() > 0 {
_, _ = buf.WriteRune(' ')
}
_, _ = buf.WriteString(fmt.Sprintf("%v", v))
}
return buf.String()
}
func genEnvStrings(values []string) string {
return strings.Join(values, " ")
}
func genEnvZoneNodes(z *Zone) string { func genEnvZoneNodes(z *Zone) string {
if n := z.Len(); n > 0 { if n := z.Len(); n > 0 {
s := make([]string, 0, n) s := make([]string, 0, n)
@ -164,7 +157,7 @@ func genEnvZoneNodes(z *Zone) string {
return false return false
}) })
return strings.Join(s, " ") return genEnvStrings(s)
} }
return "" return ""
} }

6
pkg/cluster/machine.go

@ -3,6 +3,8 @@ package cluster
import ( import (
"net/netip" "net/netip"
"strings" "strings"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
// revive:disable:line-length-limit // revive:disable:line-length-limit
@ -12,7 +14,7 @@ type Machine struct {
zone *Zone zone *Zone
logger `json:"-" yaml:"-"` logger `json:"-" yaml:"-"`
ID int ID rings.NodeID
Name string `json:"-" yaml:"-"` Name string `json:"-" yaml:"-"`
Inactive bool `json:"inactive,omitempty" yaml:"inactive,omitempty"` 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 // Zone indicates the [Zone] this machine belongs to
func (m *Machine) Zone() int { func (m *Machine) Zone() rings.ZoneID {
return m.zone.ID return m.zone.ID
} }

3
pkg/cluster/machine_rings.go

@ -8,6 +8,7 @@ import (
"darvaza.org/core" "darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
"git.jpi.io/amery/jpictl/pkg/wireguard" "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, nodeID int) error { func (m *Machine) applyZoneNodeID(zoneID rings.ZoneID, nodeID rings.NodeID) error {
switch { switch {
case zoneID == 0: case zoneID == 0:
return fmt.Errorf("invalid %s", "zoneID") return fmt.Errorf("invalid %s", "zoneID")

3
pkg/cluster/machine_scan.go

@ -9,6 +9,7 @@ import (
"time" "time"
"darvaza.org/core" "darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
// LookupNetIP uses the DNS Resolver to get the public addresses associated // LookupNetIP uses the DNS Resolver to get the public addresses associated
@ -65,7 +66,7 @@ func (m *Machine) setID() error {
return err return err
} }
m.ID = int(id) m.ID = rings.NodeID(id)
return nil return nil
} }

43
pkg/cluster/rings.go

@ -5,14 +5,11 @@ import (
"io/fs" "io/fs"
"net/netip" "net/netip"
"git.jpi.io/amery/jpictl/pkg/rings"
"git.jpi.io/amery/jpictl/pkg/wireguard" "git.jpi.io/amery/jpictl/pkg/wireguard"
) )
const ( 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 indicates how many wireguard rings we have
RingsCount = 2 RingsCount = 2
// RingZeroPort is the port wireguard uses for ring0 // RingZeroPort is the port wireguard uses for ring0
@ -81,8 +78,8 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool {
type RingAddressEncoder struct { type RingAddressEncoder struct {
ID int ID int
Port uint16 Port uint16
Encode func(zoneID, nodeID int) (netip.Addr, bool) Encode func(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool)
Decode func(addr netip.Addr) (zoneID, nodeID int, ok bool) Decode func(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool)
} }
var ( var (
@ -110,42 +107,34 @@ var (
// ValidZoneID checks if the given zoneID is a valid 4 bit zone number. // ValidZoneID checks if the given zoneID is a valid 4 bit zone number.
// //
// 0 is reserved, and only allowed when composing CIDRs. // 0 is reserved, and only allowed when composing CIDRs.
func ValidZoneID(zoneID int) bool { func ValidZoneID(zoneID rings.ZoneID) bool {
switch { return zoneID == 0 || zoneID.Valid()
case zoneID < 0 || zoneID > MaxZoneID:
return false
default:
return true
}
} }
// ValidNodeID checks if the given nodeID is a valid 8 bit number. // ValidNodeID checks if the given nodeID is a valid 8 bit number.
// nodeID is unique within a Zone. // nodeID is unique within a Zone.
// 0 is reserved, and only allowed when composing CIDRs. // 0 is reserved, and only allowed when composing CIDRs.
func ValidNodeID(nodeID int) bool { func ValidNodeID(nodeID rings.NodeID) bool {
switch { return nodeID == 0 || nodeID.Valid()
case nodeID < 0 || nodeID > MaxNodeID:
return false
default:
return true
}
} }
// ParseRingZeroAddress extracts zone and node ID from a wg0 [netip.Addr] // ParseRingZeroAddress extracts zone and node ID from a wg0 [netip.Addr]
// wg0 addresses are of the form `10.0.{{zoneID}}.{{nodeID}}` // wg0 addresses are of the form `10.0.{{zoneID}}.{{nodeID}}`
func ParseRingZeroAddress(addr netip.Addr) (zoneID int, nodeID int, ok bool) { func ParseRingZeroAddress(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool) {
if addr.IsValid() { if addr.IsValid() {
a4 := addr.As4() a4 := addr.As4()
if a4[0] == 10 && a4[1] == 0 { if a4[0] == 10 && a4[1] == 0 {
return int(a4[2]), int(a4[3]), true zoneID = rings.ZoneID(a4[2])
nodeID = rings.NodeID(a4[3])
return zoneID, nodeID, true
} }
} }
return 0, 0, false return 0, 0, false
} }
// RingZeroAddress returns a wg0 IP address // RingZeroAddress returns a wg0 IP address
func RingZeroAddress(zoneID, nodeID int) (netip.Addr, bool) { func RingZeroAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch { switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID): case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false return netip.Addr{}, false
@ -157,13 +146,13 @@ func RingZeroAddress(zoneID, nodeID int) (netip.Addr, bool) {
// ParseRingOneAddress extracts zone and node ID from a wg1 [netip.Addr] // ParseRingOneAddress extracts zone and node ID from a wg1 [netip.Addr]
// wg1 addresses are of the form `10.{{zoneID << 4}}.{{nodeID}}` // wg1 addresses are of the form `10.{{zoneID << 4}}.{{nodeID}}`
func ParseRingOneAddress(addr netip.Addr) (zoneID int, nodeID int, ok bool) { func ParseRingOneAddress(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool) {
if addr.IsValid() { if addr.IsValid() {
a4 := addr.As4() a4 := addr.As4()
if a4[0] == 10 && a4[2] == 0 { if a4[0] == 10 && a4[2] == 0 {
zoneID = int(a4[1] >> 4) zoneID = rings.ZoneID(a4[1] >> 4)
nodeID = int(a4[3]) nodeID = rings.NodeID(a4[3])
return zoneID, nodeID, true return zoneID, nodeID, true
} }
} }
@ -171,7 +160,7 @@ func ParseRingOneAddress(addr netip.Addr) (zoneID int, nodeID int, ok bool) {
} }
// RingOneAddress returns a wg1 IP address // RingOneAddress returns a wg1 IP address
func RingOneAddress(zoneID, nodeID int) (netip.Addr, bool) { func RingOneAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
switch { switch {
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID): case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
return netip.Addr{}, false return netip.Addr{}, false

10
pkg/cluster/zones.go

@ -2,6 +2,8 @@ package cluster
import ( import (
"io/fs" "io/fs"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
var ( var (
@ -19,7 +21,7 @@ type Zone struct {
zones *Cluster zones *Cluster
logger `json:"-" yaml:"-"` logger `json:"-" yaml:"-"`
ID int ID rings.ZoneID
Name string Name string
Regions []string `json:",omitempty" yaml:",omitempty"` 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 // SetGateway configures a machine to be the zone's ring0 gateway
func (z *Zone) SetGateway(gatewayID int, enabled bool) error { func (z *Zone) SetGateway(gatewayID rings.NodeID, enabled bool) error {
var err error var err error
var found bool var found bool
@ -56,8 +58,8 @@ func (z *Zone) SetGateway(gatewayID int, enabled bool) error {
} }
// GatewayIDs returns the list of IDs of machines that act as ring0 gateways // GatewayIDs returns the list of IDs of machines that act as ring0 gateways
func (z *Zone) GatewayIDs() ([]int, int) { func (z *Zone) GatewayIDs() ([]rings.NodeID, int) {
var out []int var out []rings.NodeID
z.ForEachMachine(func(p *Machine) bool { z.ForEachMachine(func(p *Machine) bool {
if p.IsGateway() { if p.IsGateway() {
out = append(out, p.ID) out = append(out, p.ID)

Loading…
Cancel
Save