Compare commits

...

11 Commits

Author SHA1 Message Date
amery 4402555f04 Merge pull request 'cluster: ensure ceph monitors are set when loading a config file' (#23)
Reviewed-on: #23
2023-09-12 15:21:25 +02:00
amery 6e7f24f491 cluster: ensure ceph monitors are set when loading a config file
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-12 12:47:24 +00:00
amery 54b302c6d5 vscode: add asciigoat, cyclomatic and Wrapf to the dictionary
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-09-12 12:47:18 +00:00
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
9 changed files with 233 additions and 5 deletions
+3
View File
@@ -1,9 +1,12 @@
{ {
"cSpell.words": [ "cSpell.words": [
"asciigoat",
"ceph", "ceph",
"cyclomatic",
"darvaza", "darvaza",
"gofrs", "gofrs",
"jpictl", "jpictl",
"Wrapf",
"zerolog" "zerolog"
] ]
} }
+1 -1
View File
@@ -37,5 +37,5 @@ func setVerbosity(_ *cobra.Command, _ []string) {
if desired > 6 { if desired > 6 {
desired = 6 desired = 6
} }
log = log.WithLevel(slog.LogLevel(desired)) log = zerolog.New(nil, slog.LogLevel(desired))
} }
+5 -1
View File
@@ -71,7 +71,7 @@ func newCephScanTODO(cfg *ceph.Config) *cephScanTODO {
return todo return todo
} }
func (m *Cluster) scanCephMonitors(_ *ScanOptions) error { func (m *Cluster) scanCephMonitors(opts *ScanOptions) error {
cfg, err := m.GetCephConfig() cfg, err := m.GetCephConfig()
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
@@ -94,6 +94,10 @@ func (m *Cluster) scanCephMonitors(_ *ScanOptions) error {
todo.LogMissing(m.log) todo.LogMissing(m.log)
} }
return m.initCephMonitors(opts)
}
func (m *Cluster) initCephMonitors(_ *ScanOptions) error {
// make sure every zone has one // make sure every zone has one
m.ForEachZone(func(z *Zone) bool { m.ForEachZone(func(z *Zone) bool {
_ = z.GetCephMonitors() _ = z.GetCephMonitors()
+2 -1
View File
@@ -27,7 +27,8 @@ type Cluster struct {
Domain string `json:"domain,omitempty" yaml:"domain,omitempty"` Domain string `json:"domain,omitempty" yaml:"domain,omitempty"`
CephFSID uuid.UUID `json:"ceph_fsid,omitempty" yaml:"ceph_fsid,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 // revive:enable:line-length-limit
+2
View File
@@ -14,6 +14,8 @@ func (m *Cluster) init(opts *ScanOptions) error {
m.scanZoneIDs, m.scanZoneIDs,
m.scanSort, m.scanSort,
m.scanGateways, m.scanGateways,
m.initCephMonitors,
m.initRegions,
} { } {
if err := fn(opts); err != nil { if err := fn(opts); err != nil {
return err 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{ for _, fn := range []func() error{
m.SyncAllWireguard, m.SyncAllWireguard,
m.SyncAllCeph, m.SyncAllCeph,
m.WriteHosts,
} { } {
if err := fn(); err != nil { if err := fn(); err != nil {
return err return err
+3 -2
View File
@@ -19,8 +19,9 @@ type Zone struct {
zones *Cluster zones *Cluster
logger `json:"-" yaml:"-"` logger `json:"-" yaml:"-"`
ID int ID int
Name string Name string
Regions []string `json:",omitempty" yaml:",omitempty"`
Machines Machines
} }