package cluster import ( "io/fs" "sort" "darvaza.org/core" ) func (m *Cluster) scan(opts *ScanOptions) error { for _, fn := range []func(*ScanOptions) error{ m.scanDirectory, m.scanMachines, m.scanZoneIDs, m.scanSort, m.scanGateways, m.scanCephMonitors, } { if err := fn(opts); err != nil { return err } } return nil } func (m *Cluster) scanDirectory(_ *ScanOptions) error { // each directory is a zone entries, err := fs.ReadDir(m.dir, ".") if err != nil { return err } for _, e := range entries { if e.IsDir() { z, err := m.newZone(e.Name()) switch { case err != nil: return core.Wrap(err, e.Name()) case z.Machines.Len() == 0: z.warn(nil). WithField("zone", z.Name). Print("empty") default: m.Zones = append(m.Zones, z) } } } return nil } func (m *Cluster) newZone(name string) (*Zone, error) { z := &Zone{ zones: m, logger: m, Name: name, } z.debug(). WithField("zone", z.Name). Print("found") if err := z.scan(); err != nil { return nil, err } return z, nil } func (m *Cluster) scanMachines(opts *ScanOptions) error { var err error m.ForEachMachine(func(p *Machine) bool { err = p.scan(opts) return err != nil }) return err } func (m *Cluster) scanZoneIDs(_ *ScanOptions) error { var hasMissing bool var lastZoneID int m.ForEachZone(func(z *Zone) bool { switch { case z.ID == 0: hasMissing = true case z.ID > lastZoneID: lastZoneID = z.ID } return false }) if hasMissing { next := lastZoneID + 1 m.ForEachZone(func(z *Zone) bool { if z.ID == 0 { z.ID, next = next, next+1 } return false }) } return nil } func (m *Cluster) scanSort(_ *ScanOptions) error { sort.SliceStable(m.Zones, func(i, j int) bool { id1 := m.Zones[i].ID id2 := m.Zones[j].ID return id1 < id2 }) m.ForEachZone(func(z *Zone) bool { sort.Sort(z) return false }) m.ForEachMachine(func(p *Machine) bool { sort.SliceStable(p.Rings, func(i, j int) bool { ri1 := p.Rings[i] ri2 := p.Rings[j] return ri1.Ring < ri2.Ring }) return false }) return nil } func (m *Cluster) scanGateways(_ *ScanOptions) 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) if err != nil { return err } for _, e := range entries { if e.IsDir() { m := &Machine{ zone: z, logger: z, Name: e.Name(), } m.debug(). WithField("node", m.Name). WithField("zone", z.Name). Print("found") if err := m.init(); err != nil { m.error(err). WithField("node", m.Name). WithField("zone", z.Name). Print() return err } z.Machines = append(z.Machines, m) } } 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") } }