Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1655ce85bc | |||
| 9c4f6d987d | |||
| fb82a7f358 | |||
| f63ce6c4e7 | |||
| 1885c76198 | |||
| 2224e70638 | |||
| 6ee848e6ca | |||
| 864eb02f9d | |||
| 9da2f8711f | |||
| 2a14205e7e | |||
| a5d9466fb8 | |||
| 3d638e9a85 | |||
| 60d3a2c290 | |||
| af90825f13 | |||
| b4f1d2e4d9 |
+10
-1
@@ -16,10 +16,19 @@ var envCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
return m.WriteEnv(os.Stdout)
|
||||
_, err = m.Env(*envExport).WriteTo(os.Stdout)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
// Command Flags
|
||||
var (
|
||||
envExport *bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(envCmd)
|
||||
|
||||
envExport = envCmd.PersistentFlags().BoolP("export", "e", false,
|
||||
"export generated variables")
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ func (pub PublicKey) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the key for JSON, omiting empty.
|
||||
// MarshalJSON encodes the key for JSON, omitting empty.
|
||||
func (key PrivateKey) MarshalJSON() ([]byte, error) {
|
||||
return encodeKeyJSON(key.String())
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the key for JSON, omiting empty.
|
||||
// MarshalJSON encodes the key for JSON, omitting empty.
|
||||
func (pub PublicKey) MarshalJSON() ([]byte, error) {
|
||||
return encodeKeyJSON(pub.String())
|
||||
}
|
||||
@@ -70,12 +70,12 @@ func encodeKeyJSON(s string) ([]byte, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MarshalYAML encodes the key for YAML, omiting empty.
|
||||
// MarshalYAML encodes the key for YAML, omitting empty.
|
||||
func (key PrivateKey) MarshalYAML() (any, error) {
|
||||
return encodeKeyYAML(key.String())
|
||||
}
|
||||
|
||||
// MarshalYAML encodes the key for YAML, omiting empty.
|
||||
// MarshalYAML encodes the key for YAML, omitting empty.
|
||||
func (pub PublicKey) MarshalYAML() (any, error) {
|
||||
return encodeKeyYAML(pub.String())
|
||||
}
|
||||
|
||||
+43
-27
@@ -7,8 +7,23 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WriteEnv generates environment variables for shell scripts
|
||||
func (m *Zones) WriteEnv(w io.Writer) error {
|
||||
// Env is a shell environment factory for this cluster
|
||||
type Env struct {
|
||||
ZoneIterator
|
||||
|
||||
export bool
|
||||
}
|
||||
|
||||
// Env returns a shell environment factory
|
||||
func (m *Zones) Env(export bool) *Env {
|
||||
return &Env{
|
||||
ZoneIterator: m,
|
||||
export: export,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo generates environment variables for shell scripts
|
||||
func (m *Env) WriteTo(w io.Writer) (int64, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
m.writeEnvVarFn(&buf, genEnvZones, "ZONES")
|
||||
@@ -17,11 +32,10 @@ func (m *Zones) WriteEnv(w io.Writer) error {
|
||||
return false
|
||||
})
|
||||
|
||||
_, err := buf.WriteTo(w)
|
||||
return err
|
||||
return buf.WriteTo(w)
|
||||
}
|
||||
|
||||
func (m *Zones) writeEnvZone(w io.Writer, z *Zone) {
|
||||
func (m *Env) writeEnvZone(w io.Writer, z *Zone) {
|
||||
zoneID := z.ID
|
||||
|
||||
// ZONE{zoneID}
|
||||
@@ -32,14 +46,17 @@ func (m *Zones) writeEnvZone(w io.Writer, z *Zone) {
|
||||
|
||||
// ZONE{zoneID}_GW
|
||||
gatewayID := getRingZeroGatewayID(z)
|
||||
m.writeEnvVar(w, fmt.Sprintf("%v", gatewayID), "ZONE%v_%s", zoneID, "GW")
|
||||
if gatewayID > 0 {
|
||||
m.writeEnvVar(w, fmt.Sprintf("%v", gatewayID), "ZONE%v_%s", zoneID, "GW")
|
||||
|
||||
// ZONE{zoneID}_IP
|
||||
ip, _ := RingZeroAddress(zoneID, gatewayID)
|
||||
m.writeEnvVar(w, ip.String(), "ZONE%v_%s", zoneID, "IP")
|
||||
// ZONE{zoneID}_IP
|
||||
if ip, ok := RingZeroAddress(zoneID, gatewayID); ok {
|
||||
m.writeEnvVar(w, ip.String(), "ZONE%v_%s", zoneID, "IP")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Zones) writeEnvVarFn(w io.Writer, fn func(*Zones) string, name string, args ...any) {
|
||||
func (m *Env) writeEnvVarFn(w io.Writer, fn func(*Env) string, name string, args ...any) {
|
||||
var value string
|
||||
|
||||
if fn != nil {
|
||||
@@ -49,7 +66,13 @@ func (m *Zones) writeEnvVarFn(w io.Writer, fn func(*Zones) string, name string,
|
||||
m.writeEnvVar(w, value, name, args...)
|
||||
}
|
||||
|
||||
func (*Zones) 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
|
||||
|
||||
if m.export {
|
||||
prefix = "export "
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
name = fmt.Sprintf(name, args...)
|
||||
}
|
||||
@@ -57,15 +80,17 @@ func (*Zones) writeEnvVar(w io.Writer, value string, name string, args ...any) {
|
||||
if name != "" {
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s=%q\n", name, value)
|
||||
_, _ = fmt.Fprintf(w, "%s%s=%q\n", prefix, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
func genEnvZones(m *Zones) string {
|
||||
s := make([]string, 0, len(m.Zones))
|
||||
for _, z := range m.Zones {
|
||||
func genEnvZones(m *Env) string {
|
||||
var s []string
|
||||
|
||||
m.ForEachZone(func(z *Zone) bool {
|
||||
s = append(s, fmt.Sprintf("%v", z.ID))
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return strings.Join(s, " ")
|
||||
}
|
||||
@@ -79,13 +104,9 @@ func genEnvZoneNodes(z *Zone) string {
|
||||
}
|
||||
|
||||
func getRingZeroGatewayID(z *Zone) int {
|
||||
var firstNodeID, gatewayID int
|
||||
var gatewayID int
|
||||
|
||||
z.ForEachMachine(func(p *Machine) bool {
|
||||
if firstNodeID == 0 {
|
||||
firstNodeID = p.ID
|
||||
}
|
||||
|
||||
if p.IsGateway() {
|
||||
gatewayID = p.ID
|
||||
}
|
||||
@@ -93,10 +114,5 @@ func getRingZeroGatewayID(z *Zone) int {
|
||||
return gatewayID != 0
|
||||
})
|
||||
|
||||
switch {
|
||||
case gatewayID == 0:
|
||||
return firstNodeID
|
||||
default:
|
||||
return gatewayID
|
||||
}
|
||||
return gatewayID
|
||||
}
|
||||
|
||||
@@ -43,6 +43,24 @@ func (m *Machine) IsGateway() bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// SetGateway enables/disables a Machine ring0 integration
|
||||
func (m *Machine) SetGateway(enabled bool) error {
|
||||
ri, found := m.getRingInfo(0)
|
||||
switch {
|
||||
case !found && !enabled:
|
||||
return nil
|
||||
case !found:
|
||||
var err error
|
||||
|
||||
if ri, err = m.createRingInfo(0, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ri.Enabled = enabled
|
||||
return m.SyncWireguardConfig(0)
|
||||
}
|
||||
|
||||
// Zone indicates the [Zone] this machine belongs to
|
||||
func (m *Machine) Zone() int {
|
||||
return m.zone.ID
|
||||
|
||||
@@ -228,3 +228,23 @@ func (m *Machine) RemoveWireguardConfig(ring int) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Machine) createRingInfo(ring int, enabled bool) (*RingInfo, error) {
|
||||
keys, err := wireguard.NewKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ri := &RingInfo{
|
||||
Ring: ring,
|
||||
Enabled: enabled,
|
||||
Keys: keys,
|
||||
}
|
||||
|
||||
err = m.applyRingInfo(ring, ri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ri, nil
|
||||
}
|
||||
|
||||
+50
-6
@@ -11,6 +11,7 @@ func (m *Zones) scan() error {
|
||||
m.scanMachines,
|
||||
m.scanZoneIDs,
|
||||
m.scanSort,
|
||||
m.scanGateways,
|
||||
} {
|
||||
if err := fn(); err != nil {
|
||||
return err
|
||||
@@ -92,12 +93,7 @@ func (m *Zones) scanSort() error {
|
||||
})
|
||||
|
||||
m.ForEachZone(func(z *Zone) bool {
|
||||
sort.SliceStable(z.Machines, func(i, j int) bool {
|
||||
id1 := z.Machines[i].ID
|
||||
id2 := z.Machines[j].ID
|
||||
return id1 < id2
|
||||
})
|
||||
|
||||
sort.Sort(z)
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -115,6 +111,16 @@ func (m *Zones) scanSort() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Zones) scanGateways() error {
|
||||
var err error
|
||||
|
||||
m.ForEachZone(func(z *Zone) bool {
|
||||
_, _, err = z.GetGateway()
|
||||
return err != nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (z *Zone) scan() error {
|
||||
// each directory is a machine
|
||||
entries, err := fs.ReadDir(z.zones.dir, z.Name)
|
||||
@@ -139,3 +145,41 @@ func (z *Zone) scan() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGateway returns the first gateway found, if none
|
||||
// files will be created to enable the first [Machine] to
|
||||
// be one
|
||||
func (z *Zone) GetGateway() (*Machine, bool, error) {
|
||||
var first *Machine
|
||||
var gateway *Machine
|
||||
|
||||
z.zones.ForEachMachine(func(p *Machine) bool {
|
||||
switch {
|
||||
case p.IsGateway():
|
||||
// found
|
||||
gateway = p
|
||||
case first == nil:
|
||||
// remember
|
||||
first = p
|
||||
default:
|
||||
// keep looking
|
||||
}
|
||||
|
||||
return gateway != nil
|
||||
})
|
||||
|
||||
switch {
|
||||
case gateway != nil:
|
||||
// found one
|
||||
return gateway, false, nil
|
||||
case first != nil:
|
||||
// make one
|
||||
if err := first.SetGateway(true); err != nil {
|
||||
return first, false, err
|
||||
}
|
||||
return first, true, nil
|
||||
default:
|
||||
// Zone without nodes?
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ var (
|
||||
|
||||
_ WireguardConfigSyncer = (*Zones)(nil)
|
||||
_ WireguardConfigSyncer = (*Zone)(nil)
|
||||
_ WireguardConfigSyncer = (*Machine)(nil)
|
||||
|
||||
_ WireguardKeysWriter = (*Zones)(nil)
|
||||
_ WireguardKeysWriter = (*Zone)(nil)
|
||||
@@ -44,7 +45,7 @@ func pruneWireguardConfig(m MachineIterator, ring int) error {
|
||||
var err error
|
||||
|
||||
m.ForEachMachine(func(p *Machine) bool {
|
||||
err = p.zone.PruneWireguardConfig(ring)
|
||||
err = p.PruneWireguardConfig(ring)
|
||||
if os.IsNotExist(err) {
|
||||
// ignore
|
||||
err = nil
|
||||
@@ -200,6 +201,12 @@ func syncWireguardConfig(z ZoneIterator, m MachineIterator, ring int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// SyncWireguardConfig updates all wgN.conf files for the specified
|
||||
// ring
|
||||
func (m *Machine) SyncWireguardConfig(ring int) 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 {
|
||||
|
||||
+95
-7
@@ -4,6 +4,7 @@ package zones
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/hack-pad/hackpadfs/os"
|
||||
|
||||
@@ -11,6 +12,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ MachineIterator = Machines(nil)
|
||||
_ sort.Interface = Machines(nil)
|
||||
|
||||
_ MachineIterator = (*Zone)(nil)
|
||||
_ MachineIterator = (*Zones)(nil)
|
||||
_ ZoneIterator = (*Zones)(nil)
|
||||
@@ -26,6 +30,62 @@ type ZoneIterator interface {
|
||||
ForEachZone(func(*Zone) bool)
|
||||
}
|
||||
|
||||
// Machines is a list of Machine objects
|
||||
type Machines []*Machine
|
||||
|
||||
// ForEachMachine calls a function for each Machine in the list
|
||||
// until instructed to terminate the loop
|
||||
func (m Machines) ForEachMachine(fn func(*Machine) bool) {
|
||||
for _, p := range m {
|
||||
if fn(p) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of machines in the list
|
||||
func (m Machines) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
// Less implements sort.Interface to sort the list
|
||||
func (m Machines) Less(i, j int) bool {
|
||||
a, b := m[i], m[j]
|
||||
za, zb := a.Zone(), b.Zone()
|
||||
|
||||
switch {
|
||||
case za == zb:
|
||||
return a.ID < b.ID
|
||||
default:
|
||||
return za < zb
|
||||
}
|
||||
}
|
||||
|
||||
// Swap implements sort.Interface to sort the list
|
||||
func (m Machines) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
|
||||
// FilterMachines produces a subset of the machines offered by the given
|
||||
// iterator fulfilling a condition
|
||||
func FilterMachines(m MachineIterator, cond func(*Machine) bool) (Machines, int) {
|
||||
var out []*Machine
|
||||
|
||||
if cond == nil {
|
||||
// unconditional
|
||||
cond = func(*Machine) bool { return true }
|
||||
}
|
||||
|
||||
m.ForEachMachine(func(p *Machine) bool {
|
||||
if cond(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return out, len(out)
|
||||
}
|
||||
|
||||
// Zone represents one zone in a cluster
|
||||
type Zone struct {
|
||||
zones *Zones
|
||||
@@ -33,23 +93,51 @@ type Zone struct {
|
||||
ID int `toml:"id"`
|
||||
Name string `toml:"name"`
|
||||
|
||||
Machines []*Machine `toml:"machines"`
|
||||
Machines `toml:"machines"`
|
||||
}
|
||||
|
||||
func (z *Zone) String() string {
|
||||
return z.Name
|
||||
}
|
||||
|
||||
// ForEachMachine calls a function for each Machine in the zone
|
||||
// until instructed to terminate the loop
|
||||
func (z *Zone) ForEachMachine(fn func(*Machine) bool) {
|
||||
for _, p := range z.Machines {
|
||||
if fn(p) {
|
||||
return
|
||||
// SetGateway configures a machine to be the zone's ring0 gateway
|
||||
func (z *Zone) SetGateway(gatewayID int, enabled bool) error {
|
||||
var err error
|
||||
var found bool
|
||||
|
||||
z.ForEachMachine(func(p *Machine) bool {
|
||||
if p.ID == gatewayID {
|
||||
found = true
|
||||
err = p.SetGateway(enabled)
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !found:
|
||||
return fs.ErrNotExist
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GatewayIDs returns the list of IDs of machines that act as ring0 gateways
|
||||
func (z *Zone) GatewayIDs() ([]int, int) {
|
||||
var out []int
|
||||
z.ForEachMachine(func(p *Machine) bool {
|
||||
if p.IsGateway() {
|
||||
out = append(out, p.ID)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return out, len(out)
|
||||
}
|
||||
|
||||
// Zones represents all zones in a cluster
|
||||
type Zones struct {
|
||||
dir fs.FS
|
||||
|
||||
Reference in New Issue
Block a user