Compare commits

...

8 Commits

Author SHA1 Message Date
amery f62a47003d Merge pull request 'cluster: introduce Regions to group zones' (#22)
Reviewed-on: #22
2023-09-12 14:45:01 +02:00
amery 5abaed9047 Merge pull request 'jpictl: fix verbosity handling' (#21)
Reviewed-on: #21
2023-09-12 14:43:40 +02:00
amery c702d649e0 cluster: introduce Regions to group zones
only available via config-file

Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-12 02:17:50 +00:00
amery e9f9d474dc jpictl: fix verbosity handling
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-12 01:38:12 +00:00
amery e2941cf2c0 Merge pull request 'jpictl: introduce --config-file/-f as alternative to scanning m/' (#19)
Reviewed-on: #19
2023-09-11 23:44:39 +02:00
amery ea755113a8 Merge pull request 'hosts: update all hosts files on jpictl write' (#20)
Reviewed-on: #20
2023-09-11 23:42:48 +02:00
amery 1c199ed923 jpictl: update all hosts files on jpictl write
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-11 18:57:26 +00:00
amery 5dc5c95aa1 hosts: add generators for hosts files
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-11 18:56:52 +00:00
7 changed files with 224 additions and 4 deletions
+1 -1
View File
@@ -37,5 +37,5 @@ func setVerbosity(_ *cobra.Command, _ []string) {
if desired > 6 {
desired = 6
}
log = log.WithLevel(slog.LogLevel(desired))
log = zerolog.New(nil, slog.LogLevel(desired))
}
+2 -1
View File
@@ -27,7 +27,8 @@ type Cluster struct {
Domain string `json:"domain,omitempty" yaml:"domain,omitempty"`
CephFSID uuid.UUID `json:"ceph_fsid,omitempty" yaml:"ceph_fsid,omitempty"`
Zones []*Zone `json:"zones,omitempty" yaml:"zones,omitempty"`
Regions []Region `json:",omitempty" yaml:",omitempty"`
Zones []*Zone `json:",omitempty" yaml:",omitempty"`
}
// revive:enable:line-length-limit
+1
View File
@@ -14,6 +14,7 @@ func (m *Cluster) init(opts *ScanOptions) error {
m.scanZoneIDs,
m.scanSort,
m.scanGateways,
m.initRegions,
} {
if err := fn(opts); err != nil {
return err
+128
View File
@@ -0,0 +1,128 @@
package cluster
import (
"bytes"
"fmt"
"strings"
"text/template"
)
type hostsFile struct {
Ring0 []hostsEntry
Ring1 []hostsEntry
}
type hostsEntry struct {
Addr string
Names []string
}
var hostsTemplate = template.Must(template.New("hosts").Funcs(template.FuncMap{
"StringsJoin": strings.Join,
}).Parse(`127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
{{range .Ring1 -}}
{{.Addr}} {{StringsJoin .Names " "}}
{{end}}
{{range .Ring0 -}}
{{.Addr}} {{StringsJoin .Names " "}}
{{end -}}
`))
// WriteHosts rewrites all hosts files on the tree
func (m *Cluster) WriteHosts() error {
var err error
m.ForEachZone(func(z *Zone) bool {
err = z.WriteHosts()
return err != nil
})
return err
}
// WriteHosts rewrites all hosts files in the zone
func (z *Zone) WriteHosts() error {
var err error
s := z.Hosts()
z.ForEachMachine(func(p *Machine) bool {
err = p.WriteStringFile(s, "hosts")
return err != nil
})
return err
}
// WriteHosts rewrites the hosts file
func (p *Machine) WriteHosts() error {
s := p.zone.Hosts()
return p.WriteStringFile(s, "hosts")
}
func (z *Zone) genHosts(out *hostsFile, p *Machine) {
var names []string
ip, _ := RingOneAddress(p.zone.ID, p.ID)
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 {
names = append(names, "ceph")
names = append(names, "k3s")
}
}
entry := hostsEntry{
Addr: ip.String(),
Names: names,
}
out.Ring1 = append(out.Ring1, entry)
if p.IsGateway() {
var s string
ip, _ = RingZeroAddress(p.zone.ID, p.ID)
s = fmt.Sprintf("%s-%v", p.Name, 0)
entry = hostsEntry{
Addr: ip.String(),
Names: []string{s},
}
out.Ring0 = append(out.Ring0, entry)
}
}
// Hosts renders the /etc/hosts to be used on this zone
func (z *Zone) Hosts() string {
var buf bytes.Buffer
var out hostsFile
z.zones.ForEachZone(func(z2 *Zone) bool {
z2.ForEachMachine(func(p *Machine) bool {
z.genHosts(&out, p)
return false
})
return false
})
if err := hostsTemplate.Execute(&buf, &out); err != nil {
panic(err)
}
return buf.String()
}
+88
View File
@@ -0,0 +1,88 @@
package cluster
// Region represents a group of zones geographically related
type Region struct {
m *Cluster
zones []*Zone
Name string
Regions []string `json:",omitempty" yaml:",omitempty"`
}
func (m *Cluster) initRegions(_ *ScanOptions) error {
regions := make(map[string][]*Zone)
// first regions defined by zones
m.ForEachZone(func(z *Zone) bool {
for _, region := range z.Regions {
regions[region] = append(regions[region], z)
}
return false
})
// bind first level regions and their zones
for name, zones := range regions {
m.syncRegions(name, zones...)
}
// and combine zones to produce larger regions
for i := range m.Regions {
r := &m.Regions[i]
m.finishRegion(r)
}
return nil
}
func (m *Cluster) syncRegions(name string, zones ...*Zone) {
for _, r := range m.Regions {
if r.Name == name {
// found
r.m = m
r.zones = zones
return
}
}
// new
m.Regions = append(m.Regions, Region{
m: m,
zones: zones,
Name: name,
})
}
func (m *Cluster) finishRegion(r *Region) {
if r.m != nil {
// ready
return
}
r.m = m
sub := []string{}
for _, name := range r.Regions {
r2, ok := m.getRegion(name)
if !ok {
m.warn(nil).WithField("region", name).Print("unknown region")
continue
}
sub = append(sub, r2.Name)
r.zones = append(r.zones, r2.zones...)
}
r.Regions = sub
}
func (m *Cluster) getRegion(name string) (*Region, bool) {
for i := range m.Regions {
r := &m.Regions[i]
if name == r.Name {
m.finishRegion(r)
return r, true
}
}
return nil, false
}
+1
View File
@@ -5,6 +5,7 @@ func (m *Cluster) SyncAll() error {
for _, fn := range []func() error{
m.SyncAllWireguard,
m.SyncAllCeph,
m.WriteHosts,
} {
if err := fn(); err != nil {
return err
+3 -2
View File
@@ -19,8 +19,9 @@ type Zone struct {
zones *Cluster
logger `json:"-" yaml:"-"`
ID int
Name string
ID int
Name string
Regions []string `json:",omitempty" yaml:",omitempty"`
Machines
}