diff --git a/go.mod b/go.mod index 409dc5f..40cc770 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( asciigoat.org/ini v0.2.5 - darvaza.org/core v0.13.1 + darvaza.org/core v0.13.3 darvaza.org/resolver v0.9.2 darvaza.org/sidecar v0.4.0 darvaza.org/slog v0.5.7 diff --git a/go.sum b/go.sum index 748a1f4..2df28d7 100644 --- a/go.sum +++ b/go.sum @@ -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= 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/core v0.13.1 h1:ZoAfZ3OLnw+t28qMQQxXrDIkETmT2h5gAO6F1XuBpwg= -darvaza.org/core v0.13.1/go.mod h1:47Ydh67KnzjLNu1mzX3r2zpphbxQqEaihMsUq5GflQ4= +darvaza.org/core v0.13.3 h1:DOsidY49WXsWiJulOIxDq578h/3ekgx0trWxbvgv5bc= +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/go.mod h1:XWqPhrxoOKNzRuSozOwmE1M6QVqQL28jEdxylnIO8Nw= darvaza.org/sidecar v0.4.0 h1:wHghxzLsiT82WDBBUf34aTqtOvRBg4UbxVIJgKNXRVA= diff --git a/pkg/cluster/addr.go b/pkg/cluster/addr.go new file mode 100644 index 0000000..3037d4c --- /dev/null +++ b/pkg/cluster/addr.go @@ -0,0 +1,43 @@ +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 +} diff --git a/pkg/cluster/ceph.go b/pkg/cluster/ceph.go index b91b98b..ef7e087 100644 --- a/pkg/cluster/ceph.go +++ b/pkg/cluster/ceph.go @@ -9,6 +9,7 @@ import ( "github.com/gofrs/uuid/v5" "git.jpi.io/amery/jpictl/pkg/ceph" + "git.jpi.io/amery/jpictl/pkg/rings" ) // GetCephFSID returns our Ceph's FSID @@ -66,7 +67,7 @@ func (m *Cluster) GenCephConfig() (*ceph.Config, error) { m.ForEachZone(func(z *Zone) bool { for _, p := range z.GetCephMonitors() { - addr, _ := RingOneAddress(z.ID, p.ID) + addr, _ := rings.RingOneAddress(z.RegionID(), z.ID, p.ID) cfg.Global.Monitors = append(cfg.Global.Monitors, p.Name) cfg.Global.MonitorsAddr = append(cfg.Global.MonitorsAddr, addr) diff --git a/pkg/cluster/ceph_scan.go b/pkg/cluster/ceph_scan.go index bd65821..0a69c1d 100644 --- a/pkg/cluster/ceph_scan.go +++ b/pkg/cluster/ceph_scan.go @@ -5,6 +5,7 @@ import ( "darvaza.org/slog" "git.jpi.io/amery/jpictl/pkg/ceph" + "git.jpi.io/amery/jpictl/pkg/rings" ) type cephScanTODO struct { @@ -14,7 +15,7 @@ type cephScanTODO struct { func (todo *cephScanTODO) checkMachine(p *Machine) bool { // on ceph all addresses are ring1 - ring1, _ := RingOneAddress(p.Zone(), p.ID) + ring1, _ := rings.RingOneAddress(p.Region(), p.Zone(), p.ID) addr := ring1.String() if _, found := todo.names[p.Name]; found { diff --git a/pkg/cluster/env.go b/pkg/cluster/env.go index 5a6674d..42e98a2 100644 --- a/pkg/cluster/env.go +++ b/pkg/cluster/env.go @@ -185,7 +185,7 @@ func genEnvZoneCephMonNames(m Machines) string { func genEnvZoneCephMonIPs(m Machines) string { var buf strings.Builder m.ForEachMachine(func(p *Machine) bool { - addr, _ := RingOneAddress(p.Zone(), p.ID) + addr := p.RingOneAddress() if buf.Len() > 0 { _, _ = buf.WriteRune(' ') diff --git a/pkg/cluster/hosts.go b/pkg/cluster/hosts.go index 359aa75..af7b23a 100644 --- a/pkg/cluster/hosts.go +++ b/pkg/cluster/hosts.go @@ -71,14 +71,14 @@ func (p *Machine) WriteHosts() error { func (z *Zone) genHosts(out *hostsFile, p *Machine) { var names []string - ip, _ := RingOneAddress(p.zone.ID, p.ID) + ip := p.RingOneAddress() names = append(names, p.Name) if p.CephMonitor { names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "ceph")) names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "k3s")) - if z.ID == p.zone.ID { + if z.Is(p.Region(), p.Zone()) { names = append(names, "ceph") names = append(names, "k3s") } @@ -94,7 +94,7 @@ func (z *Zone) genHosts(out *hostsFile, p *Machine) { if p.IsGateway() { var s string - ip, _ = RingZeroAddress(p.zone.ID, p.ID) + ip, _ = p.RingZeroAddress() s = fmt.Sprintf("%s-%v", p.Name, 0) entry = hostsEntry{ diff --git a/pkg/cluster/machine.go b/pkg/cluster/machine.go index 0d7259a..32fc416 100644 --- a/pkg/cluster/machine.go +++ b/pkg/cluster/machine.go @@ -80,6 +80,11 @@ func (m *Machine) Zone() rings.ZoneID { 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) { return m.zone.zones.GetMachineByName(name) } diff --git a/pkg/cluster/machine_rings.go b/pkg/cluster/machine_rings.go index 1c0c2f0..8d01c22 100644 --- a/pkg/cluster/machine_rings.go +++ b/pkg/cluster/machine_rings.go @@ -13,11 +13,13 @@ import ( ) // GetWireguardKeys reads a wgN.key/wgN.pub files -func (m *Machine) GetWireguardKeys(ring int) (wireguard.KeyPair, error) { +func (m *Machine) GetWireguardKeys(ringID rings.RingID) (wireguard.KeyPair, error) { var ( data []byte err error out wireguard.KeyPair + + ring = int(ringID - 1) ) data, err = m.ReadFile("wg%v.key", ring) @@ -54,8 +56,8 @@ func (m *Machine) GetWireguardKeys(ring int) (wireguard.KeyPair, error) { return out, err } -func (m *Machine) tryReadWireguardKeys(ring int) error { - kp, err := m.GetWireguardKeys(ring) +func (m *Machine) tryReadWireguardKeys(ringID rings.RingID) error { + kp, err := m.GetWireguardKeys(ringID) switch { case os.IsNotExist(err): // ignore @@ -66,19 +68,21 @@ func (m *Machine) tryReadWireguardKeys(ring int) error { default: // import keys ri := &RingInfo{ - Ring: ring, + Ring: ringID, Keys: kp, } - return m.applyRingInfo(ring, ri) + return m.applyRingInfo(ringID, ri) } } // RemoveWireguardKeys deletes wgN.key and wgN.pub from // the machine's config directory -func (m *Machine) RemoveWireguardKeys(ring int) error { +func (m *Machine) RemoveWireguardKeys(ringID rings.RingID) error { var err error + ring := int(ringID - 1) + err = m.RemoveFile("wg%v.pub", ring) switch { case os.IsNotExist(err): @@ -97,7 +101,9 @@ func (m *Machine) RemoveWireguardKeys(ring int) error { } // GetWireguardConfig reads a wgN.conf file -func (m *Machine) GetWireguardConfig(ring int) (*wireguard.Config, error) { +func (m *Machine) GetWireguardConfig(ringID rings.RingID) (*wireguard.Config, error) { + ring := int(ringID - 1) + data, err := m.ReadFile("wg%v.conf", ring) if err != nil { return nil, err @@ -107,27 +113,27 @@ func (m *Machine) GetWireguardConfig(ring int) (*wireguard.Config, error) { return wireguard.NewConfigFromReader(r) } -func (m *Machine) tryApplyWireguardConfig(ring int) error { - wg, err := m.GetWireguardConfig(ring) +func (m *Machine) tryApplyWireguardConfig(ringID rings.RingID) error { + wg, err := m.GetWireguardConfig(ringID) switch { case os.IsNotExist(err): return nil case err != nil: return err default: - return m.applyWireguardConfig(ring, wg) + return m.applyWireguardConfig(ringID, wg) } } -func (m *Machine) applyWireguardConfigNode(ring int, wg *wireguard.Config) error { +func (m *Machine) applyWireguardConfigNode(ring rings.RingID, wg *wireguard.Config) error { addr := wg.GetAddress() if !core.IsZero(addr) { - zoneID, nodeID, ok := Rings[ring].Decode(addr) + regionID, zoneID, nodeID, ok := Rings[ring].Decode(addr) if !ok { return fmt.Errorf("%s: invalid address", addr) } - if err := m.applyZoneNodeID(zoneID, nodeID); err != nil { + if err := m.applyZoneNodeID(regionID, zoneID, nodeID); err != nil { return core.Wrap(err, "%s: invalid address", addr) } } @@ -139,7 +145,7 @@ func (m *Machine) applyWireguardConfigNode(ring int, wg *wireguard.Config) error return nil } -func (m *Machine) applyWireguardConfig(ring int, wg *wireguard.Config) error { +func (m *Machine) applyWireguardConfig(ring rings.RingID, wg *wireguard.Config) error { if err := m.applyWireguardConfigNode(ring, wg); err != nil { return err } @@ -163,7 +169,7 @@ func (m *Machine) applyWireguardConfig(ring int, wg *wireguard.Config) error { return nil } -func (m *Machine) getRingInfo(ring int) (*RingInfo, bool) { +func (m *Machine) getRingInfo(ring rings.RingID) (*RingInfo, bool) { for _, ri := range m.Rings { if ri.Ring == ring { return ri, ri.Enabled @@ -173,7 +179,7 @@ func (m *Machine) getRingInfo(ring int) (*RingInfo, bool) { return nil, false } -func (m *Machine) applyRingInfo(ring int, new *RingInfo) error { +func (m *Machine) applyRingInfo(ring rings.RingID, new *RingInfo) error { cur, _ := m.getRingInfo(ring) if cur == nil { // first, append @@ -189,7 +195,9 @@ func (m *Machine) applyRingInfo(ring int, new *RingInfo) error { return cur.Merge(new) } -func (m *Machine) applyWireguardInterfaceConfig(ring int, data wireguard.InterfaceConfig) error { +func (m *Machine) applyWireguardInterfaceConfig(ring rings.RingID, + data wireguard.InterfaceConfig) error { + // ri := &RingInfo{ Ring: ring, Enabled: true, @@ -201,7 +209,9 @@ func (m *Machine) applyWireguardInterfaceConfig(ring int, data wireguard.Interfa return m.applyRingInfo(ring, ri) } -func (m *Machine) applyWireguardPeerConfig(ring int, pc wireguard.PeerConfig) error { +func (m *Machine) applyWireguardPeerConfig(ring rings.RingID, + pc wireguard.PeerConfig) error { + // peer, found := m.getPeerByName(pc.Endpoint.Name()) switch { case !found: @@ -224,16 +234,22 @@ 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(regionID rings.RegionID, + zoneID rings.ZoneID, nodeID rings.NodeID) error { + // switch { - case zoneID == 0: + case !regionID.Valid(): + return fmt.Errorf("invalid %s", "regionID") + case !zoneID.Valid(): return fmt.Errorf("invalid %s", "zoneID") - case nodeID == 0: + case !nodeID.Valid(): return fmt.Errorf("invalid %s", "nodeID") case m.ID != nodeID: return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.ID, nodeID) case m.zone.ID != 0 && m.zone.ID != zoneID: return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", 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) case m.zone.ID == 0: m.zone.ID = zoneID } @@ -260,7 +276,7 @@ func (m *Machine) setRingDefaults(ri *RingInfo) error { // RemoveWireguardConfig deletes wgN.conf from the machine's // config directory. -func (m *Machine) RemoveWireguardConfig(ring int) error { +func (m *Machine) RemoveWireguardConfig(ring rings.RingID) error { err := m.RemoveFile("wg%v.conf", ring) if os.IsNotExist(err) { err = nil @@ -269,7 +285,7 @@ func (m *Machine) RemoveWireguardConfig(ring int) error { return err } -func (m *Machine) createRingInfo(ring int, enabled bool) (*RingInfo, error) { +func (m *Machine) createRingInfo(ring rings.RingID, enabled bool) (*RingInfo, error) { keys, err := wireguard.NewKeyPair() if err != nil { return nil, err diff --git a/pkg/cluster/machine_scan.go b/pkg/cluster/machine_scan.go index f47cabb..459fc9c 100644 --- a/pkg/cluster/machine_scan.go +++ b/pkg/cluster/machine_scan.go @@ -38,8 +38,8 @@ func (m *Machine) init() error { return core.Wrap(err, m.Name) } - for i := 0; i < RingsCount; i++ { - if err := m.tryReadWireguardKeys(i); err != nil { + for _, r := range Rings { + if err := m.tryReadWireguardKeys(r.ID); err != nil { return core.Wrap(err, m.Name) } } @@ -72,12 +72,12 @@ func (m *Machine) setID() error { // scan is called once we know about all zones and machine names func (m *Machine) scan(_ *ScanOptions) error { - for i := 0; i < RingsCount; i++ { - if err := m.tryApplyWireguardConfig(i); err != nil { + for _, ring := range Rings { + if err := m.tryApplyWireguardConfig(ring.ID); err != nil { m.error(err). WithField("subsystem", "wireguard"). WithField("node", m.Name). - WithField("ring", i). + WithField("ring", ring.ID). Print() return err } diff --git a/pkg/cluster/rings.go b/pkg/cluster/rings.go index 37a123d..7ad0513 100644 --- a/pkg/cluster/rings.go +++ b/pkg/cluster/rings.go @@ -5,13 +5,13 @@ import ( "io/fs" "net/netip" + "darvaza.org/core" + "git.jpi.io/amery/jpictl/pkg/rings" "git.jpi.io/amery/jpictl/pkg/wireguard" ) const ( - // RingsCount indicates how many wireguard rings we have - RingsCount = 2 // RingZeroPort is the port wireguard uses for ring0 RingZeroPort = 51800 // RingOnePort is the port wireguard uses for ring1 @@ -21,7 +21,7 @@ const ( // RingInfo contains represents the Wireguard endpoint details // for a Machine on a particular ring type RingInfo struct { - Ring int + Ring rings.RingID Enabled bool Keys wireguard.KeyPair } @@ -51,7 +51,7 @@ func (ri *RingInfo) unsafeMerge(alter *RingInfo) error { ri.Enabled = true } - // fill the gaps on our keypair + // fill the gaps on our key pair if ri.Keys.PrivateKey.IsZero() { ri.Keys.PrivateKey = alter.Keys.PrivateKey } @@ -76,100 +76,34 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool { // RingAddressEncoder provides encoder/decoder access for a particular // Wireguard ring type RingAddressEncoder struct { - ID int + ID rings.RingID 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(rings.RegionID, rings.ZoneID, rings.NodeID) (netip.Addr, error) + Decode func(addr netip.Addr) (rings.RegionID, rings.ZoneID, rings.NodeID, bool) } var ( // RingZero is a wg0 address encoder/decoder RingZero = RingAddressEncoder{ - ID: 0, + ID: rings.RingZeroID, Port: RingZeroPort, - Decode: ParseRingZeroAddress, - Encode: RingZeroAddress, + Decode: rings.DecodeRingZeroAddress, + Encode: rings.RingZeroAddress, } // RingOne is a wg1 address encoder/decoder RingOne = RingAddressEncoder{ - ID: 1, + ID: rings.RingOneID, Port: RingOnePort, - Decode: ParseRingOneAddress, - Encode: RingOneAddress, + Decode: rings.DecodeRingOneAddress, + Encode: rings.RingOneAddress, } // Rings provides indexed access to the ring address encoders - Rings = [RingsCount]RingAddressEncoder{ + Rings = []RingAddressEncoder{ RingZero, RingOne, } ) -// 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 ( _ MachineIterator = (*Ring)(nil) _ ZoneIterator = (*Ring)(nil) @@ -192,7 +126,8 @@ func (r *Ring) AddPeer(p *Machine) bool { nodeID := p.ID zoneID := p.Zone() - addr, _ := r.Encode(zoneID, nodeID) + regionID := p.Region() + addr, _ := r.Encode(regionID, zoneID, nodeID) rp := &RingPeer{ Node: p, @@ -222,27 +157,27 @@ func (r *Ring) AddPeer(p *Machine) bool { } func (r *Ring) setRingZeroAllowedIPs(rp *RingPeer) { - zoneID, _, _ := r.Decode(rp.Address) + regionID, zoneID, _, _ := r.Decode(rp.Address) // everyone on ring0 is a gateway to ring1 - addr, _ := RingOneAddress(zoneID, 0) - rp.AllowCIDR(addr, 12) + subnet, _ := rings.RingOnePrefix(regionID, zoneID) + rp.AllowSubnet(subnet) // peer rp.AllowCIDR(rp.Address, 32) } func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) { - zoneID, _, _ := r.Decode(rp.Address) + regionID, zoneID, _, _ := r.Decode(rp.Address) // peer rp.AllowCIDR(rp.Address, 32) // ring1 gateways connect to all other ring1 networks r.ForEachZone(func(z *Zone) bool { - if z.ID != zoneID { - addr, _ := r.Encode(z.ID, 0) - rp.AllowCIDR(addr, 12) + if !z.Is(regionID, zoneID) { + subnet := z.RingOnePrefix() + rp.AllowSubnet(subnet) } return false }) @@ -251,7 +186,7 @@ func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) { r.ForEachZone(func(z *Zone) bool { z.ForEachMachine(func(p *Machine) bool { if p.IsGateway() { - addr, _ := RingZeroAddress(z.ID, p.ID) + addr, _ := p.RingZeroAddress() rp.AllowCIDR(addr, 32) } return false @@ -318,15 +253,29 @@ type RingPeer struct { // AllowCIDR allows an IP range via this peer func (rp *RingPeer) AllowCIDR(addr netip.Addr, bits int) { - cidr := netip.PrefixFrom(addr, bits) - rp.PeerConfig.AllowedIPs = append(rp.PeerConfig.AllowedIPs, cidr) + rp.AllowSubnet(netip.PrefixFrom(addr, bits)) +} + +// 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 -func NewRing(z ZoneIterator, m MachineIterator, ring int) (*Ring, error) { - r := &Ring{ - RingAddressEncoder: Rings[ring], - ZoneIterator: z, +func NewRing(z ZoneIterator, m MachineIterator, ringID rings.RingID) (*Ring, error) { + var r *Ring + for _, ring := range Rings { + if ring.ID == ringID { + r = &Ring{ + RingAddressEncoder: ring, + ZoneIterator: z, + } + break + } + } + + if r == nil { + return nil, core.QuietWrap(fs.ErrInvalid, "invalid ring (%v)", ringID) } m.ForEachMachine(func(p *Machine) bool { diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index f3beec0..12781d8 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -35,13 +35,13 @@ func (m *Cluster) SyncMkdirAll() error { func (m *Cluster) SyncAllWireguard() error { var err error - for ring := 0; ring < RingsCount; ring++ { - err = m.WriteWireguardKeys(ring) + for _, ring := range Rings { + err = m.WriteWireguardKeys(ring.ID) if err != nil { return err } - err = m.SyncWireguardConfig(ring) + err = m.SyncWireguardConfig(ring.ID) if err != nil { return err } diff --git a/pkg/cluster/wireguard.go b/pkg/cluster/wireguard.go index b6a801a..7656e98 100644 --- a/pkg/cluster/wireguard.go +++ b/pkg/cluster/wireguard.go @@ -3,6 +3,8 @@ package cluster import ( "io/fs" "os" + + "git.jpi.io/amery/jpictl/pkg/rings" ) var ( @@ -26,22 +28,22 @@ var ( // A WireguardConfigPruner deletes wgN.conf on all machines under // its scope with the specified ring disabled type WireguardConfigPruner interface { - PruneWireguardConfig(ring int) error + PruneWireguardConfig(ring rings.RingID) error } // PruneWireguardConfig removes wgN.conf files of machines with // the corresponding ring disabled on all zones -func (m *Cluster) PruneWireguardConfig(ring int) error { +func (m *Cluster) PruneWireguardConfig(ring rings.RingID) error { return pruneWireguardConfig(m, ring) } // PruneWireguardConfig removes wgN.conf files of machines with // the corresponding ring disabled. -func (z *Zone) PruneWireguardConfig(ring int) error { +func (z *Zone) PruneWireguardConfig(ring rings.RingID) error { return pruneWireguardConfig(z, ring) } -func pruneWireguardConfig(m MachineIterator, ring int) error { +func pruneWireguardConfig(m MachineIterator, ring rings.RingID) error { var err error m.ForEachMachine(func(p *Machine) bool { @@ -59,7 +61,7 @@ func pruneWireguardConfig(m MachineIterator, ring int) error { // PruneWireguardConfig deletes the wgN.conf file if its // presence on the ring is disabled -func (m *Machine) PruneWireguardConfig(ring int) error { +func (m *Machine) PruneWireguardConfig(ring rings.RingID) error { _, ok := m.getRingInfo(ring) if !ok { return m.RemoveWireguardConfig(ring) @@ -71,12 +73,12 @@ func (m *Machine) PruneWireguardConfig(ring int) error { // A WireguardConfigWriter rewrites all wgN.conf on all machines under // its scope attached to that ring type WireguardConfigWriter interface { - WriteWireguardConfig(ring int) error + WriteWireguardConfig(ring rings.RingID) error } // WriteWireguardConfig rewrites all wgN.conf on all machines // attached to that ring -func (m *Cluster) WriteWireguardConfig(ring int) error { +func (m *Cluster) WriteWireguardConfig(ring rings.RingID) error { switch ring { case 0: return writeWireguardConfig(m, m, ring) @@ -94,7 +96,7 @@ func (m *Cluster) WriteWireguardConfig(ring int) error { // WriteWireguardConfig rewrites all wgN.conf on all machines // on the Zone attached to that ring -func (z *Zone) WriteWireguardConfig(ring int) error { +func (z *Zone) WriteWireguardConfig(ring rings.RingID) error { switch ring { case 0: return writeWireguardConfig(z.zones, z.zones, ring) @@ -105,7 +107,7 @@ func (z *Zone) WriteWireguardConfig(ring int) error { } } -func writeWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error { +func writeWireguardConfig(z ZoneIterator, m MachineIterator, ring rings.RingID) error { r, err := NewRing(z, m, ring) if err != nil { return err @@ -121,7 +123,7 @@ func writeWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error { // WriteWireguardConfig rewrites the wgN.conf file of this Machine // if enabled -func (m *Machine) WriteWireguardConfig(ring int) error { +func (m *Machine) WriteWireguardConfig(ring rings.RingID) error { r, err := NewRing(m.zone.zones, m.zone, ring) if err != nil { return err @@ -149,16 +151,16 @@ func (m *Machine) writeWireguardRingConfig(r *Ring) error { // A WireguardConfigSyncer updates all wgN.conf on all machines under // its scope reflecting the state of the ring type WireguardConfigSyncer interface { - SyncWireguardConfig(ring int) error + SyncWireguardConfig(ring rings.RingID) error } // SyncWireguardConfig updates all wgN.conf files for the specified // ring -func (m *Cluster) SyncWireguardConfig(ring int) error { +func (m *Cluster) SyncWireguardConfig(ring rings.RingID) error { switch ring { - case 0: + case rings.RingZeroID: return syncWireguardConfig(m, m, ring) - case 1: + case rings.RingOneID: var err error m.ForEachZone(func(z *Zone) bool { err = syncWireguardConfig(m, z, ring) @@ -172,28 +174,28 @@ func (m *Cluster) SyncWireguardConfig(ring int) error { // SyncWireguardConfig updates all wgN.conf files for the specified // ring -func (z *Zone) SyncWireguardConfig(ring int) error { +func (z *Zone) SyncWireguardConfig(ring rings.RingID) error { switch ring { - case 0: + case rings.RingZeroID: return syncWireguardConfig(z.zones, z.zones, ring) - case 1: + case rings.RingOneID: return syncWireguardConfig(z.zones, z, ring) default: return fs.ErrInvalid } } -func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error { - r, err := NewRing(z, m, ring) +func syncWireguardConfig(z ZoneIterator, m MachineIterator, ringID rings.RingID) error { + r, err := NewRing(z, m, ringID) if err != nil { return err } r.ForEachMachine(func(p *Machine) bool { - if _, ok := p.getRingInfo(ring); ok { + if _, ok := p.getRingInfo(ringID); ok { err = p.writeWireguardRingConfig(r) } else { - err = p.RemoveWireguardConfig(ring) + err = p.RemoveWireguardConfig(ringID) } return err != nil }) @@ -203,27 +205,27 @@ func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error { // SyncWireguardConfig updates all wgN.conf files for the specified // ring -func (m *Machine) SyncWireguardConfig(ring int) error { +func (m *Machine) SyncWireguardConfig(ring rings.RingID) error { return m.zone.SyncWireguardConfig(ring) } // A WireguardKeysWriter writes the Wireguard Keys for all machines // under its scope for the specified ring type WireguardKeysWriter interface { - WriteWireguardKeys(ring int) error + WriteWireguardKeys(ring rings.RingID) error } // WriteWireguardKeys rewrites all wgN.{key,pub} files -func (m *Cluster) WriteWireguardKeys(ring int) error { +func (m *Cluster) WriteWireguardKeys(ring rings.RingID) error { return writeWireguardKeys(m, ring) } // WriteWireguardKeys rewrites all wgN.{key,pub} files on this zone -func (z *Zone) WriteWireguardKeys(ring int) error { +func (z *Zone) WriteWireguardKeys(ring rings.RingID) error { return writeWireguardKeys(z, ring) } -func writeWireguardKeys(m MachineIterator, ring int) error { +func writeWireguardKeys(m MachineIterator, ring rings.RingID) error { var err error m.ForEachMachine(func(p *Machine) bool { @@ -240,12 +242,12 @@ func writeWireguardKeys(m MachineIterator, ring int) error { } // WriteWireguardKeys writes the wgN.key/wgN.pub files -func (m *Machine) WriteWireguardKeys(ring int) error { +func (m *Machine) WriteWireguardKeys(ringID rings.RingID) error { var err error var key, pub string var ri *RingInfo - ri, _ = m.getRingInfo(ring) + ri, _ = m.getRingInfo(ringID) if ri != nil { key = ri.Keys.PrivateKey.String() pub = ri.Keys.PublicKey.String() @@ -258,6 +260,7 @@ func (m *Machine) WriteWireguardKeys(ring int) error { pub = ri.Keys.PrivateKey.Public().String() } + ring := int(ringID - 1) err = m.WriteStringFile(key+"\n", "wg%v.key", ring) if err != nil { return err diff --git a/pkg/cluster/zones.go b/pkg/cluster/zones.go index 47d7c91..acfc6c7 100644 --- a/pkg/cluster/zones.go +++ b/pkg/cluster/zones.go @@ -70,3 +70,38 @@ func (z *Zone) GatewayIDs() ([]rings.NodeID, int) { 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 + } +}