-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #1006: Keep track of project status even if plans have been deleted #1005
Changes from 5 commits
c85155e
c5eda27
ce1d577
ade9b31
cb26355
421b247
9689dbb
f546b69
036e1f6
8a905f9
ad512f1
abd2161
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,23 @@ import ( | |
bolt "go.etcd.io/bbolt" | ||
) | ||
|
||
// BoltDB is a database using BoltDB | ||
type BoltDB struct { | ||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_boltdb.go BoltDB | ||
|
||
// BoltDB interface defines the set of methods the DB implements. Use this to allow DB mocking when testing | ||
type BoltDB interface { | ||
TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, error) | ||
Unlock(p models.Project, workspace string) (*models.ProjectLock, error) | ||
List() ([]models.ProjectLock, error) | ||
UnlockByPull(repoFullName string, pullNum int) ([]models.ProjectLock, error) | ||
GetLock(p models.Project, workspace string) (*models.ProjectLock, error) | ||
GetPullStatus(pull models.PullRequest) (*models.PullStatus, error) | ||
UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) | ||
DeletePullStatus(pull models.PullRequest) error | ||
UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error | ||
} | ||
|
||
// DefaultBoltDB is a database using BoltDB | ||
type DefaultBoltDB struct { | ||
db *bolt.DB | ||
locksBucketName []byte | ||
pullsBucketName []byte | ||
|
@@ -30,7 +45,7 @@ const ( | |
|
||
// New returns a valid locker. We need to be able to write to dataDir | ||
// since bolt stores its data as a file | ||
func New(dataDir string) (*BoltDB, error) { | ||
func New(dataDir string) (*DefaultBoltDB, error) { | ||
if err := os.MkdirAll(dataDir, 0700); err != nil { | ||
return nil, errors.Wrap(err, "creating data dir") | ||
} | ||
|
@@ -56,19 +71,19 @@ func New(dataDir string) (*BoltDB, error) { | |
return nil, errors.Wrap(err, "starting BoltDB") | ||
} | ||
// todo: close BoltDB when server is sigtermed | ||
return &BoltDB{db: db, locksBucketName: []byte(locksBucketName), pullsBucketName: []byte(pullsBucketName)}, nil | ||
return &DefaultBoltDB{db: db, locksBucketName: []byte(locksBucketName), pullsBucketName: []byte(pullsBucketName)}, nil | ||
} | ||
|
||
// NewWithDB is used for testing. | ||
func NewWithDB(db *bolt.DB, bucket string) (*BoltDB, error) { | ||
return &BoltDB{db: db, locksBucketName: []byte(bucket), pullsBucketName: []byte(pullsBucketName)}, nil | ||
func NewWithDB(db *bolt.DB, bucket string) (*DefaultBoltDB, error) { | ||
return &DefaultBoltDB{db: db, locksBucketName: []byte(bucket), pullsBucketName: []byte(pullsBucketName)}, nil | ||
} | ||
|
||
// TryLock attempts to create a new lock. If the lock is | ||
// acquired, it will return true and the lock returned will be newLock. | ||
// If the lock is not acquired, it will return false and the current | ||
// lock that is preventing this lock from being acquired. | ||
func (b *BoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, error) { | ||
func (b *DefaultBoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, error) { | ||
var lockAcquired bool | ||
var currLock models.ProjectLock | ||
key := b.lockKey(newLock.Project, newLock.Workspace) | ||
|
@@ -105,7 +120,7 @@ func (b *BoltDB) TryLock(newLock models.ProjectLock) (bool, models.ProjectLock, | |
// If there is no lock, then it will return a nil pointer. | ||
// If there is a lock, then it will delete it, and then return a pointer | ||
// to the deleted lock. | ||
func (b *BoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock, error) { | ||
func (b *DefaultBoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock, error) { | ||
var lock models.ProjectLock | ||
foundLock := false | ||
key := b.lockKey(p, workspace) | ||
|
@@ -128,7 +143,7 @@ func (b *BoltDB) Unlock(p models.Project, workspace string) (*models.ProjectLock | |
} | ||
|
||
// List lists all current locks. | ||
func (b *BoltDB) List() ([]models.ProjectLock, error) { | ||
func (b *DefaultBoltDB) List() ([]models.ProjectLock, error) { | ||
var locks []models.ProjectLock | ||
var locksBytes [][]byte | ||
err := b.db.View(func(tx *bolt.Tx) error { | ||
|
@@ -156,7 +171,7 @@ func (b *BoltDB) List() ([]models.ProjectLock, error) { | |
} | ||
|
||
// UnlockByPull deletes all locks associated with that pull request and returns them. | ||
func (b *BoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.ProjectLock, error) { | ||
func (b *DefaultBoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.ProjectLock, error) { | ||
var locks []models.ProjectLock | ||
err := b.db.View(func(tx *bolt.Tx) error { | ||
c := tx.Bucket(b.locksBucketName).Cursor() | ||
|
@@ -188,7 +203,7 @@ func (b *BoltDB) UnlockByPull(repoFullName string, pullNum int) ([]models.Projec | |
|
||
// GetLock returns a pointer to the lock for that project and workspace. | ||
// If there is no lock, it returns a nil pointer. | ||
func (b *BoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLock, error) { | ||
func (b *DefaultBoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLock, error) { | ||
key := b.lockKey(p, workspace) | ||
var lockBytes []byte | ||
err := b.db.View(func(tx *bolt.Tx) error { | ||
|
@@ -216,7 +231,7 @@ func (b *BoltDB) GetLock(p models.Project, workspace string) (*models.ProjectLoc | |
|
||
// UpdatePullWithResults updates pull's status with the latest project results. | ||
// It returns the new PullStatus object. | ||
func (b *BoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) { | ||
func (b *DefaultBoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []models.ProjectResult) (models.PullStatus, error) { | ||
key, err := b.pullKey(pull) | ||
if err != nil { | ||
return models.PullStatus{}, err | ||
|
@@ -281,7 +296,7 @@ func (b *BoltDB) UpdatePullWithResults(pull models.PullRequest, newResults []mod | |
|
||
// GetPullStatus returns the status for pull. | ||
// If there is no status, returns a nil pointer. | ||
func (b *BoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, error) { | ||
func (b *DefaultBoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, error) { | ||
key, err := b.pullKey(pull) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -297,7 +312,7 @@ func (b *BoltDB) GetPullStatus(pull models.PullRequest) (*models.PullStatus, err | |
} | ||
|
||
// DeletePullStatus deletes the status for pull. | ||
func (b *BoltDB) DeletePullStatus(pull models.PullRequest) error { | ||
func (b *DefaultBoltDB) DeletePullStatus(pull models.PullRequest) error { | ||
key, err := b.pullKey(pull) | ||
if err != nil { | ||
return err | ||
|
@@ -309,9 +324,9 @@ func (b *BoltDB) DeletePullStatus(pull models.PullRequest) error { | |
return errors.Wrap(err, "DB transaction failed") | ||
} | ||
|
||
// DeleteProjectStatus deletes all project statuses under pull that match | ||
// UpdateProjectStatus updates all project statuses under pull that match | ||
// workspace and repoRelDir. | ||
func (b *BoltDB) DeleteProjectStatus(pull models.PullRequest, workspace string, repoRelDir string) error { | ||
func (b *DefaultBoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, targetStatus models.ProjectPlanStatus) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggested rewrite. // UpdateProjectStatus updates project status.
func (b *DefaultBoltDB) UpdateProjectStatus(pull models.PullRequest, workspace string, repoRelDir string, newStatus models.ProjectPlanStatus) error {
key, err := b.pullKey(pull)
if err != nil {
return err
}
err = b.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(b.pullsBucketName)
currStatusPtr, err := b.getPullFromBucket(bucket, key)
if err != nil {
return err
}
if currStatusPtr == nil {
return nil
}
currStatus := *currStatusPtr
// Update the status.
for i := range currStatus.Projects {
// NOTE: We're using a reference here because we are
// in-place updating its Status field.
proj := &currStatus.Projects[i]
if proj.Workspace == workspace && proj.RepoRelDir == repoRelDir {
proj.Status = newStatus
break
}
}
return b.writePullToBucket(bucket, key, currStatus)
})
return errors.Wrap(err, "DB transaction failed")
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
key, err := b.pullKey(pull) | ||
if err != nil { | ||
return err | ||
|
@@ -332,7 +347,7 @@ func (b *BoltDB) DeleteProjectStatus(pull models.PullRequest, workspace string, | |
var newProjects []models.ProjectStatus | ||
for _, p := range currStatus.Projects { | ||
if p.Workspace == workspace && p.RepoRelDir == repoRelDir { | ||
continue | ||
p.Status = targetStatus | ||
} | ||
newProjects = append(newProjects, p) | ||
} | ||
|
@@ -344,7 +359,7 @@ func (b *BoltDB) DeleteProjectStatus(pull models.PullRequest, workspace string, | |
return errors.Wrap(err, "DB transaction failed") | ||
} | ||
|
||
func (b *BoltDB) pullKey(pull models.PullRequest) ([]byte, error) { | ||
func (b *DefaultBoltDB) pullKey(pull models.PullRequest) ([]byte, error) { | ||
hostname := pull.BaseRepo.VCSHost.Hostname | ||
if strings.Contains(hostname, pullKeySeparator) { | ||
return nil, fmt.Errorf("vcs hostname %q contains illegal string %q", hostname, pullKeySeparator) | ||
|
@@ -358,11 +373,11 @@ func (b *BoltDB) pullKey(pull models.PullRequest) ([]byte, error) { | |
nil | ||
} | ||
|
||
func (b *BoltDB) lockKey(p models.Project, workspace string) string { | ||
func (b *DefaultBoltDB) lockKey(p models.Project, workspace string) string { | ||
return fmt.Sprintf("%s/%s/%s", p.RepoFullName, p.Path, workspace) | ||
} | ||
|
||
func (b *BoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.PullStatus, error) { | ||
func (b *DefaultBoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.PullStatus, error) { | ||
serialized := bucket.Get(key) | ||
if serialized == nil { | ||
return nil, nil | ||
|
@@ -375,15 +390,15 @@ func (b *BoltDB) getPullFromBucket(bucket *bolt.Bucket, key []byte) (*models.Pul | |
return &p, nil | ||
} | ||
|
||
func (b *BoltDB) writePullToBucket(bucket *bolt.Bucket, key []byte, pull models.PullStatus) error { | ||
func (b *DefaultBoltDB) writePullToBucket(bucket *bolt.Bucket, key []byte, pull models.PullStatus) error { | ||
serialized, err := json.Marshal(pull) | ||
if err != nil { | ||
return errors.Wrap(err, "serializing") | ||
} | ||
return bucket.Put(key, serialized) | ||
} | ||
|
||
func (b *BoltDB) projectResultToProject(p models.ProjectResult) models.ProjectStatus { | ||
func (b *DefaultBoltDB) projectResultToProject(p models.ProjectResult) models.ProjectStatus { | ||
return models.ProjectStatus{ | ||
Workspace: p.Workspace, | ||
RepoRelDir: p.RepoRelDir, | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment here is not describing the test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot, fixed