cluster: migrate to using pkg/rings for Addresses #51

Merged
karasz merged 2 commits from pr-amery-rings into main 6 months ago
  1. 2
      go.mod
  2. 4
      go.sum
  3. 43
      pkg/cluster/addr.go
  4. 2
      pkg/cluster/ceph.go
  5. 4
      pkg/cluster/ceph_scan.go
  6. 2
      pkg/cluster/env.go
  7. 15
      pkg/cluster/errors.go
  8. 6
      pkg/cluster/hosts.go
  9. 11
      pkg/cluster/machine.go
  10. 123
      pkg/cluster/machine_rings.go
  11. 11
      pkg/cluster/machine_scan.go
  12. 199
      pkg/cluster/rings.go
  13. 6
      pkg/cluster/sync.go
  14. 80
      pkg/cluster/wireguard.go
  15. 35
      pkg/cluster/zones.go

2
go.mod

@ -4,7 +4,7 @@ go 1.19
require ( require (
asciigoat.org/ini v0.2.5 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/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

4
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= 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 h1:rvFucut4wKYbsYc994yR3P0M08NqlsvZxr5G4QK82tw=
darvaza.org/cache/x/simplelru v0.1.8/go.mod h1:Mv1isOJTcXYK+aK0AvUe+/3KpRTXDsYga6rdTS/upNs= 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.3 h1:DOsidY49WXsWiJulOIxDq578h/3ekgx0trWxbvgv5bc=
darvaza.org/core v0.13.1/go.mod h1:47Ydh67KnzjLNu1mzX3r2zpphbxQqEaihMsUq5GflQ4= 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 h1:sUX6LZ1eN5TzJW7L4m7HM+BvwBeWl8dYYDGVSe+AIhk=
darvaza.org/resolver v0.9.2/go.mod h1:XWqPhrxoOKNzRuSozOwmE1M6QVqQL28jEdxylnIO8Nw= darvaza.org/resolver v0.9.2/go.mod h1:XWqPhrxoOKNzRuSozOwmE1M6QVqQL28jEdxylnIO8Nw=
darvaza.org/sidecar v0.4.0 h1:wHghxzLsiT82WDBBUf34aTqtOvRBg4UbxVIJgKNXRVA= darvaza.org/sidecar v0.4.0 h1:wHghxzLsiT82WDBBUf34aTqtOvRBg4UbxVIJgKNXRVA=

43
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
}

2
pkg/cluster/ceph.go

@ -66,7 +66,7 @@ func (m *Cluster) GenCephConfig() (*ceph.Config, error) {
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
for _, p := range z.GetCephMonitors() { for _, p := range z.GetCephMonitors() {
addr, _ := RingOneAddress(z.ID, p.ID) addr := p.RingOneAddress()
cfg.Global.Monitors = append(cfg.Global.Monitors, p.Name) cfg.Global.Monitors = append(cfg.Global.Monitors, p.Name)
cfg.Global.MonitorsAddr = append(cfg.Global.MonitorsAddr, addr) cfg.Global.MonitorsAddr = append(cfg.Global.MonitorsAddr, addr)

4
pkg/cluster/ceph_scan.go

@ -4,6 +4,7 @@ import (
"os" "os"
"darvaza.org/slog" "darvaza.org/slog"
"git.jpi.io/amery/jpictl/pkg/ceph" "git.jpi.io/amery/jpictl/pkg/ceph"
) )
@ -14,8 +15,7 @@ type cephScanTODO struct {
func (todo *cephScanTODO) checkMachine(p *Machine) bool { func (todo *cephScanTODO) checkMachine(p *Machine) bool {
// on ceph all addresses are ring1 // on ceph all addresses are ring1
ring1, _ := RingOneAddress(p.Zone(), p.ID) addr := p.RingOneAddress().String()
addr := ring1.String()
if _, found := todo.names[p.Name]; found { if _, found := todo.names[p.Name]; found {
// found on the TODO by name // found on the TODO by name

2
pkg/cluster/env.go

@ -185,7 +185,7 @@ func genEnvZoneCephMonNames(m Machines) string {
func genEnvZoneCephMonIPs(m Machines) string { func genEnvZoneCephMonIPs(m Machines) string {
var buf strings.Builder var buf strings.Builder
m.ForEachMachine(func(p *Machine) bool { m.ForEachMachine(func(p *Machine) bool {
addr, _ := RingOneAddress(p.Zone(), p.ID) addr := p.RingOneAddress()
if buf.Len() > 0 { if buf.Len() > 0 {
_, _ = buf.WriteRune(' ') _, _ = buf.WriteRune(' ')

15
pkg/cluster/errors.go

@ -1,6 +1,13 @@
package cluster package cluster
import "errors" import (
"errors"
"io/fs"
"darvaza.org/core"
"git.jpi.io/amery/jpictl/pkg/rings"
)
var ( var (
// ErrInvalidName indicates the name isn't valid // ErrInvalidName indicates the name isn't valid
@ -14,3 +21,9 @@ var (
// the intended purpose // the intended purpose
ErrInvalidNode = errors.New("invalid node") ErrInvalidNode = errors.New("invalid node")
) )
// ErrInvalidRing returns an error indicating the [rings.RingID]
// can't be used for the intended purpose
func ErrInvalidRing(ringID rings.RingID) error {
return core.QuietWrap(fs.ErrInvalid, "invalid ring %v", ringID)
}

6
pkg/cluster/hosts.go

@ -71,14 +71,14 @@ func (p *Machine) WriteHosts() error {
func (z *Zone) genHosts(out *hostsFile, p *Machine) { func (z *Zone) genHosts(out *hostsFile, p *Machine) {
var names []string var names []string
ip, _ := RingOneAddress(p.zone.ID, p.ID) ip := p.RingOneAddress()
names = append(names, p.Name) names = append(names, p.Name)
if p.CephMonitor { if p.CephMonitor {
names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "ceph")) names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "ceph"))
names = append(names, fmt.Sprintf("%s-%s", p.zone.Name, "k3s")) 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, "ceph")
names = append(names, "k3s") names = append(names, "k3s")
} }
@ -94,7 +94,7 @@ func (z *Zone) genHosts(out *hostsFile, p *Machine) {
if p.IsGateway() { if p.IsGateway() {
var s string var s string
ip, _ = RingZeroAddress(p.zone.ID, p.ID) ip, _ = p.RingZeroAddress()
s = fmt.Sprintf("%s-%v", p.Name, 0) s = fmt.Sprintf("%s-%v", p.Name, 0)
entry = hostsEntry{ entry = hostsEntry{

11
pkg/cluster/machine.go

@ -53,13 +53,13 @@ func (m *Machine) IsActive() bool {
// IsGateway tells if the Machine is a ring0 gateway // IsGateway tells if the Machine is a ring0 gateway
func (m *Machine) IsGateway() bool { func (m *Machine) IsGateway() bool {
_, ok := m.getRingInfo(0) _, ok := m.getRingInfo(rings.RingZeroID)
return ok return ok
} }
// SetGateway enables/disables a Machine ring0 integration // SetGateway enables/disables a Machine ring0 integration
func (m *Machine) SetGateway(enabled bool) error { func (m *Machine) SetGateway(enabled bool) error {
ri, found := m.getRingInfo(0) ri, found := m.getRingInfo(rings.RingZeroID)
switch { switch {
case !found && !enabled: case !found && !enabled:
return nil return nil
@ -72,7 +72,7 @@ func (m *Machine) SetGateway(enabled bool) error {
} }
ri.Enabled = enabled ri.Enabled = enabled
return m.SyncWireguardConfig(0) return m.SyncWireguardConfig(rings.RingZeroID)
} }
// Zone indicates the [Zone] this machine belongs to // Zone indicates the [Zone] this machine belongs to
@ -80,6 +80,11 @@ func (m *Machine) Zone() rings.ZoneID {
return m.zone.ID 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) { func (m *Machine) getPeerByName(name string) (*Machine, bool) {
return m.zone.zones.GetMachineByName(name) return m.zone.zones.GetMachineByName(name)
} }

123
pkg/cluster/machine_rings.go

@ -13,14 +13,21 @@ import (
) )
// GetWireguardKeys reads a wgN.key/wgN.pub files // 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 ( var (
data []byte data []byte
err error
out wireguard.KeyPair out wireguard.KeyPair
) )
data, err = m.ReadFile("wg%v.key", ring) ring, err := AsWireguardInterfaceID(ringID)
if err != nil {
// invalid ring
return out, err
}
keyFile, pubFile, _ := ring.Files()
data, err = m.ReadFile(keyFile)
if err != nil { if err != nil {
// failed to read // failed to read
return out, err return out, err
@ -29,11 +36,11 @@ func (m *Machine) GetWireguardKeys(ring int) (wireguard.KeyPair, error) {
out.PrivateKey, err = wireguard.PrivateKeyFromBase64(string(data)) out.PrivateKey, err = wireguard.PrivateKeyFromBase64(string(data))
if err != nil { if err != nil {
// bad key // bad key
err = core.Wrap(err, "wg%v.key", ring) err = core.Wrap(err, keyFile)
return out, err return out, err
} }
data, err = m.ReadFile("wg%v.pub", ring) data, err = m.ReadFile(pubFile)
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
// no wgN.pub is fine // no wgN.pub is fine
@ -45,7 +52,7 @@ func (m *Machine) GetWireguardKeys(ring int) (wireguard.KeyPair, error) {
out.PublicKey, err = wireguard.PublicKeyFromBase64(string(data)) out.PublicKey, err = wireguard.PublicKeyFromBase64(string(data))
if err != nil { if err != nil {
// bad key // bad key
err = core.Wrap(err, "wg%v.pub", ring) err = core.Wrap(err, pubFile)
return out, err return out, err
} }
} }
@ -54,8 +61,8 @@ func (m *Machine) GetWireguardKeys(ring int) (wireguard.KeyPair, error) {
return out, err return out, err
} }
func (m *Machine) tryReadWireguardKeys(ring int) error { func (m *Machine) tryReadWireguardKeys(ringID rings.RingID) error {
kp, err := m.GetWireguardKeys(ring) kp, err := m.GetWireguardKeys(ringID)
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
// ignore // ignore
@ -66,20 +73,25 @@ func (m *Machine) tryReadWireguardKeys(ring int) error {
default: default:
// import keys // import keys
ri := &RingInfo{ ri := &RingInfo{
Ring: ring, Ring: MustWireguardInterfaceID(ringID),
Keys: kp, Keys: kp,
} }
return m.applyRingInfo(ring, ri) return m.applyRingInfo(ringID, ri)
} }
} }
// RemoveWireguardKeys deletes wgN.key and wgN.pub from // RemoveWireguardKeys deletes wgN.key and wgN.pub from
// the machine's config directory // the machine's config directory
func (m *Machine) RemoveWireguardKeys(ring int) error { func (m *Machine) RemoveWireguardKeys(ringID rings.RingID) error {
var err error ring, err := AsWireguardInterfaceID(ringID)
if err != nil {
return err
}
keyFile, pubFile, _ := ring.Files()
err = m.RemoveFile("wg%v.pub", ring) err = m.RemoveFile(pubFile)
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
// ignore // ignore
@ -87,7 +99,7 @@ func (m *Machine) RemoveWireguardKeys(ring int) error {
return err return err
} }
err = m.RemoveFile("wg%v.key", ring) err = m.RemoveFile(keyFile)
if os.IsNotExist(err) { if os.IsNotExist(err) {
// ignore // ignore
err = nil err = nil
@ -97,8 +109,13 @@ func (m *Machine) RemoveWireguardKeys(ring int) error {
} }
// GetWireguardConfig reads a wgN.conf file // 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) {
data, err := m.ReadFile("wg%v.conf", ring) ring, err := AsWireguardInterfaceID(ringID)
if err != nil {
return nil, err
}
data, err := m.ReadFile(ring.ConfFile())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -107,7 +124,7 @@ func (m *Machine) GetWireguardConfig(ring int) (*wireguard.Config, error) {
return wireguard.NewConfigFromReader(r) return wireguard.NewConfigFromReader(r)
} }
func (m *Machine) tryApplyWireguardConfig(ring int) error { func (m *Machine) tryApplyWireguardConfig(ring rings.RingID) error {
wg, err := m.GetWireguardConfig(ring) wg, err := m.GetWireguardConfig(ring)
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
@ -119,15 +136,15 @@ func (m *Machine) tryApplyWireguardConfig(ring int) error {
} }
} }
func (m *Machine) applyWireguardConfigNode(ring int, wg *wireguard.Config) error { func (m *Machine) applyWireguardConfigNode(ring rings.RingID, wg *wireguard.Config) error {
addr := wg.GetAddress() addr := wg.GetAddress()
if !core.IsZero(addr) { if !core.IsZero(addr) {
zoneID, nodeID, ok := Rings[ring].Decode(addr) regionID, zoneID, nodeID, ok := Rings[ring].Decode(addr)
if !ok { if !ok {
return fmt.Errorf("%s: invalid address", addr) 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) return core.Wrap(err, "%s: invalid address", addr)
} }
} }
@ -139,7 +156,7 @@ func (m *Machine) applyWireguardConfigNode(ring int, wg *wireguard.Config) error
return nil 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 { if err := m.applyWireguardConfigNode(ring, wg); err != nil {
return err return err
} }
@ -153,7 +170,7 @@ func (m *Machine) applyWireguardConfig(ring int, wg *wireguard.Config) error {
WithField("subsystem", "wireguard"). WithField("subsystem", "wireguard").
WithField("node", m.Name). WithField("node", m.Name).
WithField("peer", peer.Endpoint.Host). WithField("peer", peer.Endpoint.Host).
WithField("ring", ring). WithField("ring", MustWireguardInterfaceID(ring)).
Print("ignoring unknown endpoint") Print("ignoring unknown endpoint")
case err != nil: case err != nil:
return core.Wrap(err, "peer") return core.Wrap(err, "peer")
@ -163,9 +180,9 @@ func (m *Machine) applyWireguardConfig(ring int, wg *wireguard.Config) error {
return nil return nil
} }
func (m *Machine) getRingInfo(ring int) (*RingInfo, bool) { func (m *Machine) getRingInfo(ring rings.RingID) (*RingInfo, bool) {
for _, ri := range m.Rings { for _, ri := range m.Rings {
if ri.Ring == ring { if ri.RingID() == ring {
return ri, ri.Enabled return ri, ri.Enabled
} }
} }
@ -173,13 +190,13 @@ func (m *Machine) getRingInfo(ring int) (*RingInfo, bool) {
return nil, false 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) cur, _ := m.getRingInfo(ring)
if cur == nil { if cur == nil {
// first, append // first, append
m.debug(). m.debug().
WithField("node", m.Name). WithField("node", m.Name).
WithField("ring", ring). WithField("ring", MustWireguardInterfaceID(ring)).
Print("found") Print("found")
m.Rings = append(m.Rings, new) m.Rings = append(m.Rings, new)
return nil return nil
@ -189,9 +206,11 @@ func (m *Machine) applyRingInfo(ring int, new *RingInfo) error {
return cur.Merge(new) 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{ ri := &RingInfo{
Ring: ring, Ring: MustWireguardInterfaceID(ring),
Enabled: true, Enabled: true,
Keys: wireguard.KeyPair{ Keys: wireguard.KeyPair{
PrivateKey: data.PrivateKey, PrivateKey: data.PrivateKey,
@ -201,7 +220,9 @@ func (m *Machine) applyWireguardInterfaceConfig(ring int, data wireguard.Interfa
return m.applyRingInfo(ring, ri) 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()) peer, found := m.getPeerByName(pc.Endpoint.Name())
switch { switch {
case !found: case !found:
@ -213,7 +234,7 @@ func (m *Machine) applyWireguardPeerConfig(ring int, pc wireguard.PeerConfig) er
default: default:
// apply RingInfo // apply RingInfo
ri := &RingInfo{ ri := &RingInfo{
Ring: ring, Ring: MustWireguardInterfaceID(ring),
Enabled: true, Enabled: true,
Keys: wireguard.KeyPair{ Keys: wireguard.KeyPair{
PublicKey: pc.PublicKey, PublicKey: pc.PublicKey,
@ -224,21 +245,29 @@ 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 { switch {
case zoneID == 0: case !regionID.Valid():
return fmt.Errorf("invalid %s", "regionID")
case !zoneID.Valid():
return fmt.Errorf("invalid %s", "zoneID") return fmt.Errorf("invalid %s", "zoneID")
case nodeID == 0: case !nodeID.Valid():
return fmt.Errorf("invalid %s", "nodeID") return fmt.Errorf("invalid %s", "nodeID")
case m.ID != nodeID: case m.ID != nodeID:
return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.ID, nodeID) return fmt.Errorf("invalid %s: %v ≠ %v", "nodeID", m.ID, nodeID)
case m.zone.ID != 0 && 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) return fmt.Errorf("invalid %s: %v ≠ %v", "zoneID", m.zone.ID, zoneID)
case m.zone.ID == 0: case m.Region() != regionID:
m.zone.ID = zoneID return fmt.Errorf("invalid %s: %v ≠ %v", "regionID", m.Region(), regionID)
} default:
if m.zone.ID == 0 {
m.zone.ID = zoneID
}
return nil return nil
}
} }
func (m *Machine) setRingDefaults(ri *RingInfo) error { func (m *Machine) setRingDefaults(ri *RingInfo) error {
@ -260,8 +289,13 @@ func (m *Machine) setRingDefaults(ri *RingInfo) error {
// RemoveWireguardConfig deletes wgN.conf from the machine's // RemoveWireguardConfig deletes wgN.conf from the machine's
// config directory. // config directory.
func (m *Machine) RemoveWireguardConfig(ring int) error { func (m *Machine) RemoveWireguardConfig(ringID rings.RingID) error {
err := m.RemoveFile("wg%v.conf", ring) ring, err := AsWireguardInterfaceID(ringID)
if err != nil {
return err
}
err = m.RemoveFile(ring.ConfFile())
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = nil err = nil
} }
@ -269,7 +303,12 @@ func (m *Machine) RemoveWireguardConfig(ring int) error {
return err return err
} }
func (m *Machine) createRingInfo(ring int, enabled bool) (*RingInfo, error) { func (m *Machine) createRingInfo(ringID rings.RingID, enabled bool) (*RingInfo, error) {
ring, err := AsWireguardInterfaceID(ringID)
if err != nil {
return nil, err
}
keys, err := wireguard.NewKeyPair() keys, err := wireguard.NewKeyPair()
if err != nil { if err != nil {
return nil, err return nil, err
@ -281,7 +320,7 @@ func (m *Machine) createRingInfo(ring int, enabled bool) (*RingInfo, error) {
Keys: keys, Keys: keys,
} }
err = m.applyRingInfo(ring, ri) err = m.applyRingInfo(ringID, ri)
if err != nil { if err != nil {
return nil, err return nil, err
} }

11
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" "git.jpi.io/amery/jpictl/pkg/rings"
) )
@ -38,8 +39,8 @@ func (m *Machine) init() error {
return core.Wrap(err, m.Name) return core.Wrap(err, m.Name)
} }
for i := 0; i < RingsCount; i++ { for _, ring := range Rings {
if err := m.tryReadWireguardKeys(i); err != nil { if err := m.tryReadWireguardKeys(ring.ID); err != nil {
return core.Wrap(err, m.Name) return core.Wrap(err, m.Name)
} }
} }
@ -72,12 +73,12 @@ func (m *Machine) setID() error {
// scan is called once we know about all zones and machine names // scan is called once we know about all zones and machine names
func (m *Machine) scan(_ *ScanOptions) error { func (m *Machine) scan(_ *ScanOptions) error {
for i := 0; i < RingsCount; i++ { for _, ring := range Rings {
if err := m.tryApplyWireguardConfig(i); err != nil { if err := m.tryApplyWireguardConfig(ring.ID); err != nil {
m.error(err). m.error(err).
WithField("subsystem", "wireguard"). WithField("subsystem", "wireguard").
WithField("node", m.Name). WithField("node", m.Name).
WithField("ring", i). WithField("ring", MustWireguardInterfaceID(ring.ID)).
Print() Print()
return err return err
} }

199
pkg/cluster/rings.go

@ -4,28 +4,86 @@ import (
"fmt" "fmt"
"io/fs" "io/fs"
"net/netip" "net/netip"
"strconv"
"git.jpi.io/amery/jpictl/pkg/rings" "git.jpi.io/amery/jpictl/pkg/rings"
"git.jpi.io/amery/jpictl/pkg/wireguard" "git.jpi.io/amery/jpictl/pkg/wireguard"
) )
const ( const (
// RingsCount indicates how many wireguard rings we have
RingsCount = 2
// RingZeroPort is the port wireguard uses for ring0 // RingZeroPort is the port wireguard uses for ring0
RingZeroPort = 51800 RingZeroPort = 51800
// RingOnePort is the port wireguard uses for ring1 // RingOnePort is the port wireguard uses for ring1
RingOnePort = 51810 RingOnePort = 51810
) )
// WireguardInterfaceID represents the number in the `wg%v`
// interface name.
type WireguardInterfaceID uint
// AsWireguardInterfaceID returns the [WireguardInterfaceID] for
// a valid [rings.RingID].
func AsWireguardInterfaceID(ring rings.RingID) (WireguardInterfaceID, error) {
switch ring {
case rings.RingZeroID:
return 0, nil
case rings.RingOneID:
return 1, nil
default:
return 0, ErrInvalidRing(ring)
}
}
// MustWireguardInterfaceID returns the [WireguardInterfaceID] for
// a valid [rings.RingID], and panics if it's not.
func MustWireguardInterfaceID(ring rings.RingID) WireguardInterfaceID {
id, err := AsWireguardInterfaceID(ring)
if err != nil {
panic(err)
}
return id
}
// RingID tells the [rings.RingID] of the [WireguardInterfaceID].
func (wi WireguardInterfaceID) RingID() rings.RingID {
return rings.RingID(wi + 1)
}
// PubFile returns "wgN.pub"
func (wi WireguardInterfaceID) PubFile() string {
return fmt.Sprintf("wg%v.pub", wi)
}
// KeyFile returns "wgN.key"
func (wi WireguardInterfaceID) KeyFile() string {
return fmt.Sprintf("wg%v.key", wi)
}
// ConfFile returns "wgN.conf"
func (wi WireguardInterfaceID) ConfFile() string {
return fmt.Sprintf("wg%v.conf", wi)
}
// Files returns all wgN.ext file names.
func (wi WireguardInterfaceID) Files() (keyFile, pubFile, confFile string) {
prefix := "wg" + strconv.Itoa(int(wi))
return prefix + ".key", prefix + ".pub", prefix + ".conf"
}
// RingInfo contains represents the Wireguard endpoint details // RingInfo contains represents the Wireguard endpoint details
// for a Machine on a particular ring // for a Machine on a particular ring
type RingInfo struct { type RingInfo struct {
Ring int Ring WireguardInterfaceID
Enabled bool Enabled bool
Keys wireguard.KeyPair Keys wireguard.KeyPair
} }
// RingID returns the [rings.RingID] for this [RingInfo].
func (ri *RingInfo) RingID() rings.RingID {
return rings.RingID(ri.Ring + 1)
}
// Merge attempts to combine two RingInfo structs // Merge attempts to combine two RingInfo structs
func (ri *RingInfo) Merge(alter *RingInfo) error { func (ri *RingInfo) Merge(alter *RingInfo) error {
switch { switch {
@ -51,7 +109,7 @@ func (ri *RingInfo) unsafeMerge(alter *RingInfo) error {
ri.Enabled = true ri.Enabled = true
} }
// fill the gaps on our keypair // fill the gaps on our key pair
if ri.Keys.PrivateKey.IsZero() { if ri.Keys.PrivateKey.IsZero() {
ri.Keys.PrivateKey = alter.Keys.PrivateKey ri.Keys.PrivateKey = alter.Keys.PrivateKey
} }
@ -76,100 +134,34 @@ func canMergeKeyPairs(p1, p2 wireguard.KeyPair) bool {
// RingAddressEncoder provides encoder/decoder access for a particular // RingAddressEncoder provides encoder/decoder access for a particular
// Wireguard ring // Wireguard ring
type RingAddressEncoder struct { type RingAddressEncoder struct {
ID int ID rings.RingID
Port uint16 Port uint16
Encode func(zoneID rings.ZoneID, nodeID rings.NodeID) (netip.Addr, bool) Encode func(rings.RegionID, rings.ZoneID, rings.NodeID) (netip.Addr, error)
Decode func(addr netip.Addr) (zoneID rings.ZoneID, nodeID rings.NodeID, ok bool) Decode func(addr netip.Addr) (rings.RegionID, rings.ZoneID, rings.NodeID, bool)
} }
var ( var (
// RingZero is a wg0 address encoder/decoder // RingZero is a wg0 address encoder/decoder
RingZero = RingAddressEncoder{ RingZero = RingAddressEncoder{
ID: 0, ID: rings.RingZeroID,
Port: RingZeroPort, Port: RingZeroPort,
Decode: ParseRingZeroAddress, Decode: rings.DecodeRingZeroAddress,
Encode: RingZeroAddress, Encode: rings.RingZeroAddress,
} }
// RingOne is a wg1 address encoder/decoder // RingOne is a wg1 address encoder/decoder
RingOne = RingAddressEncoder{ RingOne = RingAddressEncoder{
ID: 1, ID: rings.RingOneID,
Port: RingOnePort, Port: RingOnePort,
Decode: ParseRingOneAddress, Decode: rings.DecodeRingOneAddress,
Encode: RingOneAddress, Encode: rings.RingOneAddress,
} }
// Rings provides indexed access to the ring address encoders // Rings provides indexed access to the ring address encoders
Rings = [RingsCount]RingAddressEncoder{ Rings = []RingAddressEncoder{
RingZero, RingZero,
RingOne, 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 ( var (
_ MachineIterator = (*Ring)(nil) _ MachineIterator = (*Ring)(nil)
_ ZoneIterator = (*Ring)(nil) _ ZoneIterator = (*Ring)(nil)
@ -192,14 +184,15 @@ func (r *Ring) AddPeer(p *Machine) bool {
nodeID := p.ID nodeID := p.ID
zoneID := p.Zone() zoneID := p.Zone()
addr, _ := r.Encode(zoneID, nodeID) regionID := p.Region()
addr, _ := r.Encode(regionID, zoneID, nodeID)
rp := &RingPeer{ rp := &RingPeer{
Node: p, Node: p,
Address: addr, Address: addr,
PrivateKey: ri.Keys.PrivateKey, PrivateKey: ri.Keys.PrivateKey,
PeerConfig: wireguard.PeerConfig{ PeerConfig: wireguard.PeerConfig{
Name: fmt.Sprintf("%s-%v", p.Name, r.ID), Name: fmt.Sprintf("%s-%v", p.Name, ri.Ring),
PublicKey: ri.Keys.PublicKey, PublicKey: ri.Keys.PublicKey,
Endpoint: wireguard.EndpointAddress{ Endpoint: wireguard.EndpointAddress{
Host: p.FullName(), Host: p.FullName(),
@ -222,27 +215,27 @@ func (r *Ring) AddPeer(p *Machine) bool {
} }
func (r *Ring) setRingZeroAllowedIPs(rp *RingPeer) { 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 // everyone on ring0 is a gateway to ring1
addr, _ := RingOneAddress(zoneID, 0) subnet, _ := rings.RingOnePrefix(regionID, zoneID)
rp.AllowCIDR(addr, 12) rp.AllowSubnet(subnet)
// peer // peer
rp.AllowCIDR(rp.Address, 32) rp.AllowCIDR(rp.Address, 32)
} }
func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) { func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) {
zoneID, _, _ := r.Decode(rp.Address) regionID, zoneID, _, _ := r.Decode(rp.Address)
// peer // peer
rp.AllowCIDR(rp.Address, 32) rp.AllowCIDR(rp.Address, 32)
// ring1 gateways connect to all other ring1 networks // ring1 gateways connect to all other ring1 networks
r.ForEachZone(func(z *Zone) bool { r.ForEachZone(func(z *Zone) bool {
if z.ID != zoneID { if !z.Is(regionID, zoneID) {
addr, _ := r.Encode(z.ID, 0) subnet := z.RingOnePrefix()
rp.AllowCIDR(addr, 12) rp.AllowSubnet(subnet)
} }
return false return false
}) })
@ -251,7 +244,7 @@ func (r *Ring) setRingOneGatewayAllowedIPs(rp *RingPeer) {
r.ForEachZone(func(z *Zone) bool { r.ForEachZone(func(z *Zone) bool {
z.ForEachMachine(func(p *Machine) bool { z.ForEachMachine(func(p *Machine) bool {
if p.IsGateway() { if p.IsGateway() {
addr, _ := RingZeroAddress(z.ID, p.ID) addr, _ := p.RingZeroAddress()
rp.AllowCIDR(addr, 32) rp.AllowCIDR(addr, 32)
} }
return false return false
@ -318,15 +311,29 @@ type RingPeer struct {
// AllowCIDR allows an IP range via this peer // AllowCIDR allows an IP range via this peer
func (rp *RingPeer) AllowCIDR(addr netip.Addr, bits int) { func (rp *RingPeer) AllowCIDR(addr netip.Addr, bits int) {
cidr := netip.PrefixFrom(addr, bits) rp.AllowSubnet(netip.PrefixFrom(addr, bits))
rp.PeerConfig.AllowedIPs = append(rp.PeerConfig.AllowedIPs, cidr) }
// 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 // NewRing composes a new Ring for Wireguard setup
func NewRing(z ZoneIterator, m MachineIterator, ring int) (*Ring, error) { func NewRing(z ZoneIterator, m MachineIterator, ringID rings.RingID) (*Ring, error) {
r := &Ring{ var r *Ring
RingAddressEncoder: Rings[ring], for _, ring := range Rings {
ZoneIterator: z, if ringID == ring.ID {
r = &Ring{
RingAddressEncoder: ring,
ZoneIterator: z,
}
break
}
}
if r == nil {
return nil, ErrInvalidRing(ringID)
} }
m.ForEachMachine(func(p *Machine) bool { m.ForEachMachine(func(p *Machine) bool {

6
pkg/cluster/sync.go

@ -35,13 +35,13 @@ func (m *Cluster) SyncMkdirAll() error {
func (m *Cluster) SyncAllWireguard() error { func (m *Cluster) SyncAllWireguard() error {
var err error var err error
for ring := 0; ring < RingsCount; ring++ { for _, ring := range Rings {
err = m.WriteWireguardKeys(ring) err = m.WriteWireguardKeys(ring.ID)
if err != nil { if err != nil {
return err return err
} }
err = m.SyncWireguardConfig(ring) err = m.SyncWireguardConfig(ring.ID)
if err != nil { if err != nil {
return err return err
} }

80
pkg/cluster/wireguard.go

@ -3,6 +3,8 @@ package cluster
import ( import (
"io/fs" "io/fs"
"os" "os"
"git.jpi.io/amery/jpictl/pkg/rings"
) )
var ( var (
@ -26,22 +28,22 @@ var (
// A WireguardConfigPruner deletes wgN.conf on all machines under // A WireguardConfigPruner deletes wgN.conf on all machines under
// its scope with the specified ring disabled // its scope with the specified ring disabled
type WireguardConfigPruner interface { type WireguardConfigPruner interface {
PruneWireguardConfig(ring int) error PruneWireguardConfig(ring rings.RingID) error
} }
// PruneWireguardConfig removes wgN.conf files of machines with // PruneWireguardConfig removes wgN.conf files of machines with
// the corresponding ring disabled on all zones // 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) return pruneWireguardConfig(m, ring)
} }
// PruneWireguardConfig removes wgN.conf files of machines with // PruneWireguardConfig removes wgN.conf files of machines with
// the corresponding ring disabled. // the corresponding ring disabled.
func (z *Zone) PruneWireguardConfig(ring int) error { func (z *Zone) PruneWireguardConfig(ring rings.RingID) error {
return pruneWireguardConfig(z, ring) return pruneWireguardConfig(z, ring)
} }
func pruneWireguardConfig(m MachineIterator, ring int) error { func pruneWireguardConfig(m MachineIterator, ring rings.RingID) error {
var err error var err error
m.ForEachMachine(func(p *Machine) bool { 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 // PruneWireguardConfig deletes the wgN.conf file if its
// presence on the ring is disabled // 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) _, ok := m.getRingInfo(ring)
if !ok { if !ok {
return m.RemoveWireguardConfig(ring) return m.RemoveWireguardConfig(ring)
@ -71,16 +73,16 @@ func (m *Machine) PruneWireguardConfig(ring int) error {
// A WireguardConfigWriter rewrites all wgN.conf on all machines under // A WireguardConfigWriter rewrites all wgN.conf on all machines under
// its scope attached to that ring // its scope attached to that ring
type WireguardConfigWriter interface { type WireguardConfigWriter interface {
WriteWireguardConfig(ring int) error WriteWireguardConfig(ring rings.RingID) error
} }
// WriteWireguardConfig rewrites all wgN.conf on all machines // WriteWireguardConfig rewrites all wgN.conf on all machines
// attached to that ring // attached to that ring
func (m *Cluster) WriteWireguardConfig(ring int) error { func (m *Cluster) WriteWireguardConfig(ring rings.RingID) error {
switch ring { switch ring {
case 0: case rings.RingZeroID:
return writeWireguardConfig(m, m, ring) return writeWireguardConfig(m, m, ring)
case 1: case rings.RingOneID:
var err error var err error
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
err = writeWireguardConfig(m, z, ring) err = writeWireguardConfig(m, z, ring)
@ -88,24 +90,24 @@ func (m *Cluster) WriteWireguardConfig(ring int) error {
}) })
return err return err
default: default:
return fs.ErrInvalid return ErrInvalidRing(ring)
} }
} }
// WriteWireguardConfig rewrites all wgN.conf on all machines // WriteWireguardConfig rewrites all wgN.conf on all machines
// on the Zone attached to that ring // on the Zone attached to that ring
func (z *Zone) WriteWireguardConfig(ring int) error { func (z *Zone) WriteWireguardConfig(ring rings.RingID) error {
switch ring { switch ring {
case 0: case rings.RingZeroID:
return writeWireguardConfig(z.zones, z.zones, ring) return writeWireguardConfig(z.zones, z.zones, ring)
case 1: case rings.RingOneID:
return writeWireguardConfig(z.zones, z, ring) return writeWireguardConfig(z.zones, z, ring)
default: default:
return fs.ErrInvalid return ErrInvalidRing(ring)
} }
} }
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) r, err := NewRing(z, m, ring)
if err != nil { if err != nil {
return err return err
@ -121,7 +123,7 @@ func writeWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error {
// WriteWireguardConfig rewrites the wgN.conf file of this Machine // WriteWireguardConfig rewrites the wgN.conf file of this Machine
// if enabled // 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) r, err := NewRing(m.zone.zones, m.zone, ring)
if err != nil { if err != nil {
return err return err
@ -131,12 +133,17 @@ func (m *Machine) WriteWireguardConfig(ring int) error {
} }
func (m *Machine) writeWireguardRingConfig(r *Ring) error { func (m *Machine) writeWireguardRingConfig(r *Ring) error {
ring, err := AsWireguardInterfaceID(r.ID)
if err != nil {
return err
}
wg, err := r.ExportConfig(m) wg, err := r.ExportConfig(m)
if err != nil { if err != nil {
return nil return nil
} }
f, err := m.CreateTruncFile("wg%v.conf", r.ID) f, err := m.CreateTruncFile(ring.ConfFile())
if err != nil { if err != nil {
return err return err
} }
@ -149,16 +156,16 @@ func (m *Machine) writeWireguardRingConfig(r *Ring) error {
// A WireguardConfigSyncer updates all wgN.conf on all machines under // A WireguardConfigSyncer updates all wgN.conf on all machines under
// its scope reflecting the state of the ring // its scope reflecting the state of the ring
type WireguardConfigSyncer interface { type WireguardConfigSyncer interface {
SyncWireguardConfig(ring int) error SyncWireguardConfig(ring rings.RingID) error
} }
// SyncWireguardConfig updates all wgN.conf files for the specified // SyncWireguardConfig updates all wgN.conf files for the specified
// ring // ring
func (m *Cluster) SyncWireguardConfig(ring int) error { func (m *Cluster) SyncWireguardConfig(ring rings.RingID) error {
switch ring { switch ring {
case 0: case rings.RingZeroID:
return syncWireguardConfig(m, m, ring) return syncWireguardConfig(m, m, ring)
case 1: case rings.RingOneID:
var err error var err error
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
err = syncWireguardConfig(m, z, ring) err = syncWireguardConfig(m, z, ring)
@ -166,24 +173,24 @@ func (m *Cluster) SyncWireguardConfig(ring int) error {
}) })
return err return err
default: default:
return fs.ErrInvalid return ErrInvalidRing(ring)
} }
} }
// SyncWireguardConfig updates all wgN.conf files for the specified // SyncWireguardConfig updates all wgN.conf files for the specified
// ring // ring
func (z *Zone) SyncWireguardConfig(ring int) error { func (z *Zone) SyncWireguardConfig(ring rings.RingID) error {
switch ring { switch ring {
case 0: case rings.RingZeroID:
return syncWireguardConfig(z.zones, z.zones, ring) return syncWireguardConfig(z.zones, z.zones, ring)
case 1: case rings.RingOneID:
return syncWireguardConfig(z.zones, z, ring) return syncWireguardConfig(z.zones, z, ring)
default: default:
return fs.ErrInvalid return ErrInvalidRing(ring)
} }
} }
func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error { func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring rings.RingID) error {
r, err := NewRing(z, m, ring) r, err := NewRing(z, m, ring)
if err != nil { if err != nil {
return err return err
@ -203,27 +210,27 @@ func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error {
// SyncWireguardConfig updates all wgN.conf files for the specified // SyncWireguardConfig updates all wgN.conf files for the specified
// ring // ring
func (m *Machine) SyncWireguardConfig(ring int) error { func (m *Machine) SyncWireguardConfig(ring rings.RingID) error {
return m.zone.SyncWireguardConfig(ring) return m.zone.SyncWireguardConfig(ring)
} }
// A WireguardKeysWriter writes the Wireguard Keys for all machines // A WireguardKeysWriter writes the Wireguard Keys for all machines
// under its scope for the specified ring // under its scope for the specified ring
type WireguardKeysWriter interface { type WireguardKeysWriter interface {
WriteWireguardKeys(ring int) error WriteWireguardKeys(ring rings.RingID) error
} }
// WriteWireguardKeys rewrites all wgN.{key,pub} files // 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) return writeWireguardKeys(m, ring)
} }
// WriteWireguardKeys rewrites all wgN.{key,pub} files on this zone // 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) return writeWireguardKeys(z, ring)
} }
func writeWireguardKeys(m MachineIterator, ring int) error { func writeWireguardKeys(m MachineIterator, ring rings.RingID) error {
var err error var err error
m.ForEachMachine(func(p *Machine) bool { m.ForEachMachine(func(p *Machine) bool {
@ -240,12 +247,12 @@ func writeWireguardKeys(m MachineIterator, ring int) error {
} }
// WriteWireguardKeys writes the wgN.key/wgN.pub files // 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 err error
var key, pub string var key, pub string
var ri *RingInfo var ri *RingInfo
ri, _ = m.getRingInfo(ring) ri, _ = m.getRingInfo(ringID)
if ri != nil { if ri != nil {
key = ri.Keys.PrivateKey.String() key = ri.Keys.PrivateKey.String()
pub = ri.Keys.PublicKey.String() pub = ri.Keys.PublicKey.String()
@ -258,12 +265,13 @@ func (m *Machine) WriteWireguardKeys(ring int) error {
pub = ri.Keys.PrivateKey.Public().String() pub = ri.Keys.PrivateKey.Public().String()
} }
err = m.WriteStringFile(key+"\n", "wg%v.key", ring) keyFile, pubFile, _ := ri.Ring.Files()
err = m.WriteStringFile(key+"\n", keyFile)
if err != nil { if err != nil {
return err return err
} }
err = m.WriteStringFile(pub+"\n", "wg%v.pub", ring) err = m.WriteStringFile(pub+"\n", pubFile)
if err != nil { if err != nil {
return err return err
} }

35
pkg/cluster/zones.go

@ -70,3 +70,38 @@ func (z *Zone) GatewayIDs() ([]rings.NodeID, int) {
return out, len(out) 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
}
}

Loading…
Cancel
Save