package zones import ( "io/fs" "sort" ) func (m *Zones) scan() error { for _, fn := range []func() error{ m.scanDirectory, m.scanMachines, m.scanZoneIDs, m.scanSort, m.scanGateways, } { if err := fn(); err != nil { return err } } return nil } func (m *Zones) scanDirectory() 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 := &Zone{ zones: m, Name: e.Name(), } if err := z.scan(); err != nil { return err } m.Zones = append(m.Zones, z) } } return nil } func (m *Zones) scanMachines() error { var err error m.ForEachMachine(func(p *Machine) bool { err = p.scan() return err != nil }) return err } func (m *Zones) scanZoneIDs() 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 *Zones) scanSort() 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 *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) if err != nil { return err } for _, e := range entries { if e.IsDir() { m := &Machine{ zone: z, Name: e.Name(), } if err := m.init(); err != nil { 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") } }