blob: d083c4221fae92de28e5178acd553a546b59b08c [file] [log] [blame]
package recent_rolls
import (
"fmt"
"sync"
"go.skia.org/infra/go/autoroll"
)
const RECENT_ROLLS_LENGTH = 10
// RecentRolls is a struct used for storing and retrieving recent DEPS rolls.
type RecentRolls struct {
db *db
recent []*autoroll.AutoRollIssue
mtx sync.RWMutex
}
// NewRecentRolls returns a new RecentRolls instance.
func NewRecentRolls(dbFile string) (*RecentRolls, error) {
d, err := openDB(dbFile)
if err != nil {
return nil, err
}
recentRolls := &RecentRolls{
db: d,
}
if err := recentRolls.refreshRecentRolls(); err != nil {
return nil, err
}
return recentRolls, nil
}
// Close closes the database held by the RecentRolls.
func (r *RecentRolls) Close() error {
return r.db.Close()
}
// Add adds a DEPS roll to the recent rolls list.
func (r *RecentRolls) Add(roll *autoroll.AutoRollIssue) error {
if err := roll.Validate(); err != nil {
return err
}
r.mtx.Lock()
defer r.mtx.Unlock()
// Refuse to insert more rolls if we already have an active roll.
if r.currentRoll() != nil {
return fmt.Errorf("There is already an active roll. Cannot add another.")
}
// Validate the new roll.
if roll.Closed {
return fmt.Errorf("Cannot insert a new roll which is already closed.")
}
if err := r.db.InsertRoll(roll); err != nil {
return err
}
return r.refreshRecentRolls()
}
// Update updates the given DEPS roll in the recent rolls list.
func (r *RecentRolls) Update(roll *autoroll.AutoRollIssue) error {
if err := roll.Validate(); err != nil {
return err
}
r.mtx.Lock()
defer r.mtx.Unlock()
if err := r.db.UpdateRoll(roll); err != nil {
return err
}
return r.refreshRecentRolls()
}
// GetRecentRolls returns a copy of the recent rolls list.
func (r *RecentRolls) GetRecentRolls() []*autoroll.AutoRollIssue {
r.mtx.RLock()
defer r.mtx.RUnlock()
recent := make([]*autoroll.AutoRollIssue, 0, len(r.recent))
for _, r := range r.recent {
elem := new(autoroll.AutoRollIssue)
*elem = *r
recent = append(recent, elem)
}
return recent
}
// currentRoll returns the currently-active DEPS roll, or nil if none exists.
// Does not copy the roll. Expects that the caller holds a lock.
func (r *RecentRolls) currentRoll() *autoroll.AutoRollIssue {
if len(r.recent) == 0 {
return nil
}
if r.recent[0].Closed {
return nil
}
return r.recent[0]
}
// CurrentRoll returns a copy of the currently-active DEPS roll, or nil if none
// exists.
func (r *RecentRolls) CurrentRoll() *autoroll.AutoRollIssue {
r.mtx.RLock()
defer r.mtx.RUnlock()
current := r.currentRoll()
if current != nil {
rv := new(autoroll.AutoRollIssue)
*rv = *current
return rv
}
return nil
}
// LastRoll returns a copy of the last DEPS roll, if one exists, and nil
// otherwise.
func (r *RecentRolls) LastRoll() *autoroll.AutoRollIssue {
r.mtx.RLock()
defer r.mtx.RUnlock()
if len(r.recent) > 0 && r.recent[0].Closed {
rv := new(autoroll.AutoRollIssue)
*rv = *r.recent[0]
return rv
} else if len(r.recent) > 1 {
rv := new(autoroll.AutoRollIssue)
*rv = *r.recent[1]
return rv
}
return nil
}
// refreshRecentRolls refreshes the list of recent DEPS rolls. Assumes the
// caller holds a write lock.
func (r *RecentRolls) refreshRecentRolls() error {
// Load the last N rolls.
recent, err := r.db.GetRecentRolls(RECENT_ROLLS_LENGTH)
if err != nil {
return err
}
r.recent = recent
return nil
}