Compare commits
1 Commits
v0.8.2
..
6c875ae832
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c875ae832 |
@@ -15,8 +15,7 @@ TMPDIR ?= .tmp
|
||||
REVIVE ?= $(GOBIN)/revive
|
||||
REVIVE_CONF ?= $(TOOLSDIR)/revive.toml
|
||||
REVIVE_RUN_ARGS ?= -config $(REVIVE_CONF) -formatter friendly
|
||||
REVIVE_VERSION ?= v1.3.7
|
||||
REVIVE_INSTALL_URL ?= github.com/mgechev/revive@$(REVIVE_VERSION)
|
||||
REVIVE_INSTALL_URL ?= github.com/mgechev/revive@master
|
||||
|
||||
GO_INSTALL_URLS = \
|
||||
$(REVIVE_INSTALL_URL) \
|
||||
|
||||
@@ -6,18 +6,16 @@ import (
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
func (m *Cluster) init(opts *ScanOptions) error {
|
||||
for _, fn := range []func(*ScanOptions) error{
|
||||
m.initZones,
|
||||
m.initRegions,
|
||||
m.scanZoneIDs,
|
||||
m.scanSort,
|
||||
m.scanGateways,
|
||||
m.initCephMonitors,
|
||||
m.initRegions,
|
||||
} {
|
||||
if err := fn(opts); err != nil {
|
||||
return err
|
||||
@@ -47,7 +45,7 @@ func (m *Cluster) initZones(opts *ScanOptions) error {
|
||||
|
||||
func (m *Cluster) initZone(z *Zone, _ *ScanOptions) error {
|
||||
var hasMissing bool
|
||||
var lastMachineID rings.NodeID
|
||||
var lastMachineID NodeID
|
||||
|
||||
z.zones = m
|
||||
z.logger = m
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"darvaza.org/core"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,9 +23,9 @@ func (m *Cluster) scan(opts *ScanOptions) error {
|
||||
for _, fn := range []func(*ScanOptions) error{
|
||||
m.scanDirectory,
|
||||
m.scanMachines,
|
||||
m.initRegions,
|
||||
m.scanZoneIDs,
|
||||
m.scanSort,
|
||||
m.initRegions,
|
||||
m.scanGateways,
|
||||
m.scanCephMonitors,
|
||||
} {
|
||||
@@ -116,7 +114,7 @@ func (m *Cluster) scanMachines(opts *ScanOptions) error {
|
||||
|
||||
func (m *Cluster) scanZoneIDs(_ *ScanOptions) error {
|
||||
var hasMissing bool
|
||||
var lastZoneID rings.ZoneID
|
||||
var lastZoneID ZoneID
|
||||
|
||||
m.ForEachZone(func(z *Zone) bool {
|
||||
switch {
|
||||
|
||||
+15
-9
@@ -6,8 +6,6 @@ import (
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
// Env is a shell environment factory for this cluster
|
||||
@@ -37,8 +35,8 @@ func (m *Cluster) Env(export bool) (*Env, error) {
|
||||
}
|
||||
|
||||
// Zones returns the list of Zone IDs
|
||||
func (m *Env) Zones() []rings.ZoneID {
|
||||
var zones []rings.ZoneID
|
||||
func (m *Env) Zones() []ZoneID {
|
||||
var zones []ZoneID
|
||||
|
||||
m.ForEachZone(func(z *Zone) bool {
|
||||
zones = append(zones, z.ID)
|
||||
@@ -53,7 +51,7 @@ func (m *Env) Regions() []string {
|
||||
var regions []string
|
||||
|
||||
m.ForEachRegion(func(r *Region) bool {
|
||||
if r.IsPrimary() {
|
||||
if r.Cluster != nil {
|
||||
regions = append(regions, r.Name)
|
||||
}
|
||||
|
||||
@@ -163,10 +161,18 @@ func genEnvZoneNodes(z *Zone) string {
|
||||
}
|
||||
|
||||
func genEnvZoneRegion(z *Zone) string {
|
||||
if z != nil && z.region != nil {
|
||||
return z.region.Name
|
||||
}
|
||||
return ""
|
||||
var region string
|
||||
|
||||
z.ForEachRegion(func(r *Region) bool {
|
||||
if r.Cluster != nil {
|
||||
region = r.Name
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return region
|
||||
}
|
||||
|
||||
func genEnvZoneCephMonNames(m Machines) string {
|
||||
|
||||
@@ -3,8 +3,6 @@ package cluster
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
// revive:disable:line-length-limit
|
||||
@@ -14,7 +12,7 @@ type Machine struct {
|
||||
zone *Zone
|
||||
logger `json:"-" yaml:"-"`
|
||||
|
||||
ID rings.NodeID
|
||||
ID NodeID
|
||||
Name string `json:"-" yaml:"-"`
|
||||
|
||||
Inactive bool `json:"inactive,omitempty" yaml:"inactive,omitempty"`
|
||||
@@ -76,7 +74,7 @@ func (m *Machine) SetGateway(enabled bool) error {
|
||||
}
|
||||
|
||||
// Zone indicates the [Zone] this machine belongs to
|
||||
func (m *Machine) Zone() rings.ZoneID {
|
||||
func (m *Machine) Zone() ZoneID {
|
||||
return m.zone.ID
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"darvaza.org/core"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
"git.jpi.io/amery/jpictl/pkg/wireguard"
|
||||
)
|
||||
|
||||
@@ -224,7 +223,7 @@ func (m *Machine) applyWireguardPeerConfig(ring int, pc wireguard.PeerConfig) er
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Machine) applyZoneNodeID(zoneID rings.ZoneID, nodeID rings.NodeID) error {
|
||||
func (m *Machine) applyZoneNodeID(zoneID ZoneID, nodeID NodeID) error {
|
||||
switch {
|
||||
case zoneID == 0:
|
||||
return fmt.Errorf("invalid %s", "zoneID")
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"darvaza.org/core"
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
// LookupNetIP uses the DNS Resolver to get the public addresses associated
|
||||
@@ -66,7 +65,7 @@ func (m *Machine) setID() error {
|
||||
return err
|
||||
}
|
||||
|
||||
m.ID = rings.NodeID(id)
|
||||
m.ID = NodeID(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+2
-99
@@ -3,8 +3,6 @@ package cluster
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -26,15 +24,8 @@ type Region struct {
|
||||
zones []*Zone
|
||||
|
||||
Name string
|
||||
ID rings.RegionID `json:",omitempty" yaml:",omitempty"`
|
||||
Cluster *string `json:",omitempty" yaml:",omitempty"`
|
||||
Regions []string `json:",omitempty" yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// IsPrimary indicates the region is primary and corresponds
|
||||
// to a kubernetes cluster.
|
||||
func (r *Region) IsPrimary() bool {
|
||||
return r != nil && r.Cluster != nil
|
||||
Cluster *string `json:",omitempty" yaml:",omitempty"`
|
||||
Regions []string `json:",omitempty" yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// ForEachRegion calls a function for each Region of the cluster
|
||||
@@ -101,8 +92,6 @@ func (m *Cluster) initRegions(_ *ScanOptions) error {
|
||||
}
|
||||
|
||||
m.sortRegions()
|
||||
m.scanRegionID()
|
||||
m.computeZonesRegion()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -221,92 +210,6 @@ func (m *Cluster) finishRegion(r *Region) {
|
||||
r.Regions = sub
|
||||
}
|
||||
|
||||
// revive:disable:cognitive-complexity
|
||||
func (m *Cluster) scanRegionID() {
|
||||
// revive:enable:cognitive-complexity
|
||||
var max rings.RegionID
|
||||
var missing bool
|
||||
|
||||
// check IDs
|
||||
ids := make(map[rings.RegionID]bool)
|
||||
fn := func(r *Region) bool {
|
||||
var term bool
|
||||
|
||||
switch {
|
||||
case !r.IsPrimary():
|
||||
// secondary, no ID.
|
||||
r.ID = 0
|
||||
case !r.ID.Valid():
|
||||
// primary without ID
|
||||
missing = true
|
||||
case ids[r.ID]:
|
||||
// duplicate
|
||||
m.error(nil).WithField("region", r.Name).Print("duplicate ID")
|
||||
missing = true
|
||||
r.ID = 0
|
||||
default:
|
||||
ids[r.ID] = true
|
||||
|
||||
if r.ID > max {
|
||||
max = r.ID
|
||||
}
|
||||
}
|
||||
|
||||
return term
|
||||
}
|
||||
m.ForEachRegion(fn)
|
||||
|
||||
if missing {
|
||||
// assign missing IDs
|
||||
fn := func(r *Region) bool {
|
||||
var term bool
|
||||
|
||||
switch {
|
||||
case !r.IsPrimary():
|
||||
// ignore secondary
|
||||
case r.ID.Valid():
|
||||
// already has an ID
|
||||
default:
|
||||
r.ID = max + 1
|
||||
max = r.ID
|
||||
}
|
||||
|
||||
return term
|
||||
}
|
||||
|
||||
m.ForEachRegion(fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Cluster) computeZonesRegion() {
|
||||
fn := func(r *Region, z *Zone) {
|
||||
if z.region != nil {
|
||||
m.error(nil).
|
||||
WithField("zone", z.Name).
|
||||
WithField("region", []string{
|
||||
z.region.Name,
|
||||
r.Name,
|
||||
}).
|
||||
Print("zone in two regions")
|
||||
} else {
|
||||
z.region = r
|
||||
}
|
||||
}
|
||||
|
||||
m.ForEachRegion(func(r *Region) bool {
|
||||
var term bool
|
||||
|
||||
if r.IsPrimary() {
|
||||
r.ForEachZone(func(z *Zone) bool {
|
||||
fn(r, z)
|
||||
return term
|
||||
})
|
||||
}
|
||||
|
||||
return term
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Cluster) getRegion(name string) (*Region, bool) {
|
||||
for i := range m.Regions {
|
||||
r := &m.Regions[i]
|
||||
|
||||
+27
-16
@@ -5,11 +5,14 @@ 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
|
||||
@@ -78,8 +81,8 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool {
|
||||
type RingAddressEncoder struct {
|
||||
ID int
|
||||
Port uint16
|
||||
Encode func(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool)
|
||||
Decode func(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool)
|
||||
Encode func(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool)
|
||||
Decode func(addr netip.Addr) (zoneID ZoneID, nodeID NodeID, ok bool)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -107,34 +110,42 @@ 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()
|
||||
func ValidZoneID(zoneID ZoneID) bool {
|
||||
switch {
|
||||
case zoneID < 0 || zoneID > MaxZoneID:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
func ValidNodeID(nodeID NodeID) bool {
|
||||
switch {
|
||||
case nodeID < 0 || nodeID > MaxNodeID:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func ParseRingZeroAddress(addr netip.Addr) (zoneID ZoneID, nodeID 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 ZoneID(a4[2]), NodeID(a4[3]), true
|
||||
}
|
||||
}
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
// RingZeroAddress returns a wg0 IP address
|
||||
func RingZeroAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
|
||||
func RingZeroAddress(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool) {
|
||||
switch {
|
||||
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
|
||||
return netip.Addr{}, false
|
||||
@@ -146,13 +157,13 @@ func RingZeroAddress(zoneID rings.ZoneID, nodeID rings.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 rings.ZoneID, nodeID rings.NodeID, ok bool) {
|
||||
func ParseRingOneAddress(addr netip.Addr) (zoneID ZoneID, nodeID 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])
|
||||
zoneID = ZoneID(a4[1] >> 4)
|
||||
nodeID = NodeID(a4[3])
|
||||
return zoneID, nodeID, true
|
||||
}
|
||||
}
|
||||
@@ -160,7 +171,7 @@ func ParseRingOneAddress(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.Nod
|
||||
}
|
||||
|
||||
// RingOneAddress returns a wg1 IP address
|
||||
func RingOneAddress(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) {
|
||||
func RingOneAddress(zoneID ZoneID, nodeID NodeID) (netip.Addr, bool) {
|
||||
switch {
|
||||
case !ValidZoneID(zoneID) || !ValidNodeID(nodeID):
|
||||
return netip.Addr{}, false
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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 ""
|
||||
}
|
||||
@@ -2,8 +2,6 @@ package cluster
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"git.jpi.io/amery/jpictl/pkg/rings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -19,10 +17,9 @@ type ZoneIterator interface {
|
||||
// affinity.
|
||||
type Zone struct {
|
||||
zones *Cluster
|
||||
region *Region
|
||||
logger `json:"-" yaml:"-"`
|
||||
|
||||
ID rings.ZoneID
|
||||
ID ZoneID
|
||||
Name string
|
||||
Regions []string `json:",omitempty" yaml:",omitempty"`
|
||||
|
||||
@@ -34,7 +31,7 @@ func (z *Zone) String() string {
|
||||
}
|
||||
|
||||
// SetGateway configures a machine to be the zone's ring0 gateway
|
||||
func (z *Zone) SetGateway(gatewayID rings.NodeID, enabled bool) error {
|
||||
func (z *Zone) SetGateway(gatewayID NodeID, enabled bool) error {
|
||||
var err error
|
||||
var found bool
|
||||
|
||||
@@ -59,8 +56,8 @@ func (z *Zone) SetGateway(gatewayID rings.NodeID, enabled bool) error {
|
||||
}
|
||||
|
||||
// GatewayIDs returns the list of IDs of machines that act as ring0 gateways
|
||||
func (z *Zone) GatewayIDs() ([]rings.NodeID, int) {
|
||||
var out []rings.NodeID
|
||||
func (z *Zone) GatewayIDs() ([]NodeID, int) {
|
||||
var out []NodeID
|
||||
z.ForEachMachine(func(p *Machine) bool {
|
||||
if p.IsGateway() {
|
||||
out = append(out, p.ID)
|
||||
|
||||
+2
-32
@@ -3,8 +3,6 @@
|
||||
package rings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"darvaza.org/core"
|
||||
@@ -16,10 +14,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) - 2
|
||||
NodeMax = (1 << 12) - 1
|
||||
// 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
|
||||
NodeZeroMax = (1 << 8) - 1
|
||||
|
||||
// RingZeroBits indicates the size of the prefix on the ring 0 (backbone) network.
|
||||
RingZeroBits = 16
|
||||
@@ -39,20 +37,12 @@ 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
|
||||
@@ -63,28 +53,8 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user