package store import ( "context" "time" ) // FacilityHold is a domain taken out of placement: rack:r12-07. type FacilityHold struct { Domain string Reason string Drain bool At time.Time By string } // FacilityHolds are every domain held, oldest first. func (s *DB) FacilityHolds(ctx context.Context) ([]FacilityHold, error) { rows, err := s.db.QueryContext(ctx, `SELECT domain, reason, drain, held_at, held_by FROM facility_holds ORDER BY held_at, domain`) if err != nil { return nil, err } defer rows.Close() var out []FacilityHold for rows.Next() { var h FacilityHold var drain, at int64 if err := rows.Scan(&h.Domain, &h.Reason, &drain, &at, &h.By); err != nil { return nil, err } h.Drain, h.At = drain != 0, time.UnixMilli(at) out = append(out, h) } return out, rows.Err() } // SetFacilityHold holds a domain, or changes the reason it is held for. func (s *DB) SetFacilityHold(ctx context.Context, h FacilityHold) error { drain := 0 if h.Drain { drain = 1 } _, err := s.db.ExecContext(ctx, `INSERT INTO facility_holds (domain, reason, drain, held_at, held_by) VALUES (?, ?, ?, ?, ?) ON CONFLICT (domain) DO UPDATE SET reason = excluded.reason, drain = excluded.drain, held_at = excluded.held_at, held_by = excluded.held_by`, h.Domain, h.Reason, drain, h.At.UnixMilli(), h.By) return err } // DeleteFacilityHold releases a domain; releasing one not held is no error. func (s *DB) DeleteFacilityHold(ctx context.Context, domain string) error { _, err := s.db.ExecContext(ctx, `DELETE FROM facility_holds WHERE domain = ?`, domain) return err } // NodeLabels are the labels set through the control plane: node id, then // key, then value. func (s *DB) NodeLabels(ctx context.Context) (map[string]map[string]string, error) { rows, err := s.db.QueryContext(ctx, `SELECT node_id, key, value FROM node_labels`) if err != nil { return nil, err } defer rows.Close() out := map[string]map[string]string{} for rows.Next() { var node, key, value string if err := rows.Scan(&node, &key, &value); err != nil { return nil, err } if out[node] == nil { out[node] = map[string]string{} } out[node][key] = value } return out, rows.Err() } // SetNodeLabel sets one label on a node; an empty value removes it, which // leaves the agent's own value for the key, if it has one, in force again. func (s *DB) SetNodeLabel(ctx context.Context, node, key, value, by string) error { if value == "" { _, err := s.db.ExecContext(ctx, `DELETE FROM node_labels WHERE node_id = ? AND key = ?`, node, key) return err } _, err := s.db.ExecContext(ctx, `INSERT INTO node_labels (node_id, key, value, updated_at, updated_by) VALUES (?, ?, ?, ?, ?) ON CONFLICT (node_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at, updated_by = excluded.updated_by`, node, key, value, time.Now().UnixMilli(), by) return err }