didn't realize gin supports net.Context. Change to support Context pattern!

This commit is contained in:
Brad Rydzewski
2015-10-21 16:14:02 -07:00
parent af2ef2347a
commit cfdfbcfd3b
56 changed files with 1495 additions and 1051 deletions

71
store/builds.go Normal file
View File

@@ -0,0 +1,71 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type BuildStore interface {
// Get gets a build by unique ID.
Get(int64) (*model.Build, error)
// GetNumber gets a build by number.
GetNumber(*model.Repo, int) (*model.Build, error)
// GetRef gets a build by its ref.
GetRef(*model.Repo, string) (*model.Build, error)
// GetCommit gets a build by its commit sha.
GetCommit(*model.Repo, string, string) (*model.Build, error)
// GetLast gets the last build for the branch.
GetLast(*model.Repo, string) (*model.Build, error)
// GetLastBefore gets the last build before build number N.
GetLastBefore(*model.Repo, string, int64) (*model.Build, error)
// GetList gets a list of builds for the repository
GetList(*model.Repo) ([]*model.Build, error)
// Create creates a new build and jobs.
Create(*model.Build, ...*model.Job) error
// Update updates a build.
Update(*model.Build) error
}
func GetBuild(c context.Context, id int64) (*model.Build, error) {
return FromContext(c).Builds().Get(id)
}
func GetBuildNumber(c context.Context, repo *model.Repo, num int) (*model.Build, error) {
return FromContext(c).Builds().GetNumber(repo, num)
}
func GetBuildRef(c context.Context, repo *model.Repo, ref string) (*model.Build, error) {
return FromContext(c).Builds().GetRef(repo, ref)
}
func GetBuildCommit(c context.Context, repo *model.Repo, sha, branch string) (*model.Build, error) {
return FromContext(c).Builds().GetCommit(repo, sha, branch)
}
func GetBuildLast(c context.Context, repo *model.Repo, branch string) (*model.Build, error) {
return FromContext(c).Builds().GetLast(repo, branch)
}
func GetBuildLastBefore(c context.Context, repo *model.Repo, branch string, number int64) (*model.Build, error) {
return FromContext(c).Builds().GetLastBefore(repo, branch, number)
}
func GetBuildList(c context.Context, repo *model.Repo) ([]*model.Build, error) {
return FromContext(c).Builds().GetList(repo)
}
func CreateBuild(c context.Context, build *model.Build, jobs ...*model.Job) error {
return FromContext(c).Builds().Create(build, jobs...)
}
func UpdateBuild(c context.Context, build *model.Build) error {
return FromContext(c).Builds().Update(build)
}

23
store/context.go Normal file
View File

@@ -0,0 +1,23 @@
package store
import (
"golang.org/x/net/context"
)
const key = "store"
// Setter defines a context that enables setting values.
type Setter interface {
Set(string, interface{})
}
// FromContext returns the Store associated with this context.
func FromContext(c context.Context) Store {
return c.Value(key).(Store)
}
// ToContext adds the Store to this context if it supports
// the Setter interface.
func ToContext(c Setter, store Store) {
c.Set(key, store)
}

141
store/datastore/builds.go Normal file
View File

@@ -0,0 +1,141 @@
package datastore
import (
"database/sql"
"time"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type buildstore struct {
*sql.DB
}
func (db *buildstore) Get(id int64) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.Load(db, buildTable, build, id)
return build, err
}
func (db *buildstore) GetNumber(repo *model.Repo, num int) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.QueryRow(db, build, rebind(buildNumberQuery), repo.ID, num)
return build, err
}
func (db *buildstore) GetRef(repo *model.Repo, ref string) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.QueryRow(db, build, rebind(buildRefQuery), repo.ID, ref)
return build, err
}
func (db *buildstore) GetCommit(repo *model.Repo, sha, branch string) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.QueryRow(db, build, rebind(buildCommitQuery), repo.ID, sha, branch)
return build, err
}
func (db *buildstore) GetLast(repo *model.Repo, branch string) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.QueryRow(db, build, rebind(buildLastQuery), repo.ID, branch)
return build, err
}
func (db *buildstore) GetLastBefore(repo *model.Repo, branch string, num int64) (*model.Build, error) {
var build = new(model.Build)
var err = meddler.QueryRow(db, build, rebind(buildLastBeforeQuery), repo.ID, branch, num)
return build, err
}
func (db *buildstore) GetList(repo *model.Repo) ([]*model.Build, error) {
var builds = []*model.Build{}
var err = meddler.QueryAll(db, &builds, rebind(buildListQuery), repo.ID)
return builds, err
}
func (db *buildstore) Create(build *model.Build, jobs ...*model.Job) error {
var number int
db.QueryRow(rebind(buildNumberLast), build.RepoID).Scan(&number)
build.Number = number + 1
build.Created = time.Now().UTC().Unix()
build.Enqueued = build.Created
err := meddler.Insert(db, buildTable, build)
if err != nil {
return err
}
for i, job := range jobs {
job.BuildID = build.ID
job.Number = i + 1
job.Enqueued = build.Created
err = meddler.Insert(db, jobTable, job)
if err != nil {
return err
}
}
return nil
}
func (db *buildstore) Update(build *model.Build) error {
return meddler.Update(db, buildTable, build)
}
const buildTable = "builds"
const buildListQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
ORDER BY build_number DESC
LIMIT 50
`
const buildNumberQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
AND build_number = ?
LIMIT 1;
`
const buildLastQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
AND build_branch = ?
ORDER BY build_number DESC
LIMIT 1
`
const buildLastBeforeQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
AND build_branch = ?
AND build_id < ?
ORDER BY build_number DESC
LIMIT 1
`
const buildCommitQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
AND build_commit = ?
AND build_branch = ?
LIMIT 1
`
const buildRefQuery = `
SELECT *
FROM builds
WHERE build_repo_id = ?
AND build_ref = ?
LIMIT 1
`
const buildNumberLast = `
SELECT MAX(build_number)
FROM builds
WHERE build_repo_id = ?
`

View File

@@ -0,0 +1,243 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_buildstore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Builds", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM builds")
db.Exec("DELETE FROM jobs")
})
g.It("Should Post a Build", func() {
build := model.Build{
RepoID: 1,
Status: model.StatusSuccess,
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
}
err := s.Builds().Create(&build, []*model.Job{}...)
g.Assert(err == nil).IsTrue()
g.Assert(build.ID != 0).IsTrue()
g.Assert(build.Number).Equal(1)
g.Assert(build.Commit).Equal("85f8c029b902ed9400bc600bac301a0aadb144ac")
})
g.It("Should Put a Build", func() {
build := model.Build{
RepoID: 1,
Number: 5,
Status: model.StatusSuccess,
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
}
s.Builds().Create(&build, []*model.Job{}...)
build.Status = model.StatusRunning
err1 := s.Builds().Update(&build)
getbuild, err2 := s.Builds().Get(build.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(build.ID).Equal(getbuild.ID)
g.Assert(build.RepoID).Equal(getbuild.RepoID)
g.Assert(build.Status).Equal(getbuild.Status)
g.Assert(build.Number).Equal(getbuild.Number)
})
g.It("Should Get a Build", func() {
build := model.Build{
RepoID: 1,
Status: model.StatusSuccess,
}
s.Builds().Create(&build, []*model.Job{}...)
getbuild, err := s.Builds().Get(build.ID)
g.Assert(err == nil).IsTrue()
g.Assert(build.ID).Equal(getbuild.ID)
g.Assert(build.RepoID).Equal(getbuild.RepoID)
g.Assert(build.Status).Equal(getbuild.Status)
})
g.It("Should Get a Build by Number", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
getbuild, err3 := s.Builds().GetNumber(&model.Repo{ID: 1}, build2.Number)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
})
g.It("Should Get a Build by Ref", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Ref: "refs/pull/5",
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Ref: "refs/pull/6",
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
getbuild, err3 := s.Builds().GetRef(&model.Repo{ID: 1}, "refs/pull/6")
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
g.Assert(build2.Ref).Equal(getbuild.Ref)
})
g.It("Should Get a Build by Ref", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Ref: "refs/pull/5",
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Ref: "refs/pull/6",
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
getbuild, err3 := s.Builds().GetRef(&model.Repo{ID: 1}, "refs/pull/6")
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
g.Assert(build2.Ref).Equal(getbuild.Ref)
})
g.It("Should Get a Build by Commit", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusPending,
Branch: "dev",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144aa",
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
getbuild, err3 := s.Builds().GetCommit(&model.Repo{ID: 1}, build2.Commit, build2.Branch)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
g.Assert(build2.Commit).Equal(getbuild.Commit)
g.Assert(build2.Branch).Equal(getbuild.Branch)
})
g.It("Should Get the last Build", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusFailure,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusSuccess,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144aa",
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
getbuild, err3 := s.Builds().GetLast(&model.Repo{ID: 1}, build2.Branch)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
g.Assert(build2.Status).Equal(getbuild.Status)
g.Assert(build2.Branch).Equal(getbuild.Branch)
g.Assert(build2.Commit).Equal(getbuild.Commit)
})
g.It("Should Get the last Build Before Build N", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusFailure,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144ac",
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusSuccess,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144aa",
}
build3 := &model.Build{
RepoID: 1,
Status: model.StatusRunning,
Branch: "master",
Commit: "85f8c029b902ed9400bc600bac301a0aadb144aa",
}
err1 := s.Builds().Create(build1, []*model.Job{}...)
err2 := s.Builds().Create(build2, []*model.Job{}...)
err3 := s.Builds().Create(build3, []*model.Job{}...)
getbuild, err4 := s.Builds().GetLastBefore(&model.Repo{ID: 1}, build3.Branch, build3.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(err4 == nil).IsTrue()
g.Assert(build2.ID).Equal(getbuild.ID)
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
g.Assert(build2.Number).Equal(getbuild.Number)
g.Assert(build2.Status).Equal(getbuild.Status)
g.Assert(build2.Branch).Equal(getbuild.Branch)
g.Assert(build2.Commit).Equal(getbuild.Commit)
})
g.It("Should get recent Builds", func() {
build1 := &model.Build{
RepoID: 1,
Status: model.StatusFailure,
}
build2 := &model.Build{
RepoID: 1,
Status: model.StatusSuccess,
}
s.Builds().Create(build1, []*model.Job{}...)
s.Builds().Create(build2, []*model.Job{}...)
builds, err := s.Builds().GetList(&model.Repo{ID: 1})
g.Assert(err == nil).IsTrue()
g.Assert(len(builds)).Equal(2)
g.Assert(builds[0].ID).Equal(build2.ID)
g.Assert(builds[0].RepoID).Equal(build2.RepoID)
g.Assert(builds[0].Status).Equal(build2.Status)
})
})
}

55
store/datastore/jobs.go Normal file
View File

@@ -0,0 +1,55 @@
package datastore
import (
"database/sql"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type jobstore struct {
*sql.DB
}
func (db *jobstore) Get(id int64) (*model.Job, error) {
var job = new(model.Job)
var err = meddler.Load(db, jobTable, job, id)
return job, err
}
func (db *jobstore) GetNumber(build *model.Build, num int) (*model.Job, error) {
var job = new(model.Job)
var err = meddler.QueryRow(db, job, rebind(jobNumberQuery), build.ID, num)
return job, err
}
func (db *jobstore) GetList(build *model.Build) ([]*model.Job, error) {
var jobs = []*model.Job{}
var err = meddler.QueryAll(db, &jobs, rebind(jobListQuery), build.ID)
return jobs, err
}
func (db *jobstore) Create(job *model.Job) error {
return meddler.Insert(db, jobTable, job)
}
func (db *jobstore) Update(job *model.Job) error {
return meddler.Update(db, jobTable, job)
}
const jobTable = "jobs"
const jobListQuery = `
SELECT *
FROM jobs
WHERE job_build_id = ?
ORDER BY job_number ASC
`
const jobNumberQuery = `
SELECT *
FROM jobs
WHERE job_build_id = ?
AND job_number = ?
LIMIT 1
`

View File

@@ -0,0 +1,118 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_jobstore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Job", func() {
// before each test we purge the package table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM jobs")
db.Exec("DELETE FROM builds")
})
g.It("Should Set a job", func() {
job := &model.Job{
BuildID: 1,
Status: "pending",
ExitCode: 0,
Number: 1,
}
err1 := s.Jobs().Create(job)
g.Assert(err1 == nil).IsTrue()
g.Assert(job.ID != 0).IsTrue()
job.Status = "started"
err2 := s.Jobs().Update(job)
g.Assert(err2 == nil).IsTrue()
getjob, err3 := s.Jobs().Get(job.ID)
g.Assert(err3 == nil).IsTrue()
g.Assert(getjob.Status).Equal(job.Status)
})
g.It("Should Get a Job by ID", func() {
job := &model.Job{
BuildID: 1,
Status: "pending",
ExitCode: 1,
Number: 1,
Environment: map[string]string{"foo": "bar"},
}
err1 := s.Jobs().Create(job)
g.Assert(err1 == nil).IsTrue()
g.Assert(job.ID != 0).IsTrue()
getjob, err2 := s.Jobs().Get(job.ID)
g.Assert(err2 == nil).IsTrue()
g.Assert(getjob.ID).Equal(job.ID)
g.Assert(getjob.Status).Equal(job.Status)
g.Assert(getjob.ExitCode).Equal(job.ExitCode)
g.Assert(getjob.Environment).Equal(job.Environment)
g.Assert(getjob.Environment["foo"]).Equal("bar")
})
g.It("Should Get a Job by Number", func() {
job := &model.Job{
BuildID: 1,
Status: "pending",
ExitCode: 1,
Number: 1,
}
err1 := s.Jobs().Create(job)
g.Assert(err1 == nil).IsTrue()
g.Assert(job.ID != 0).IsTrue()
getjob, err2 := s.Jobs().GetNumber(&model.Build{ID: 1}, 1)
g.Assert(err2 == nil).IsTrue()
g.Assert(getjob.ID).Equal(job.ID)
g.Assert(getjob.Status).Equal(job.Status)
})
g.It("Should Get a List of Jobs by Commit", func() {
build := model.Build{
RepoID: 1,
Status: model.StatusSuccess,
}
jobs := []*model.Job{
&model.Job{
BuildID: 1,
Status: "success",
ExitCode: 0,
Number: 1,
},
&model.Job{
BuildID: 3,
Status: "error",
ExitCode: 1,
Number: 2,
},
&model.Job{
BuildID: 5,
Status: "pending",
ExitCode: 0,
Number: 3,
},
}
err1 := s.Builds().Create(&build, jobs...)
g.Assert(err1 == nil).IsTrue()
getjobs, err2 := s.Jobs().GetList(&build)
g.Assert(err2 == nil).IsTrue()
g.Assert(len(getjobs)).Equal(3)
g.Assert(getjobs[0].Number).Equal(1)
g.Assert(getjobs[0].Status).Equal(model.StatusSuccess)
})
})
}

37
store/datastore/keys.go Normal file
View File

@@ -0,0 +1,37 @@
package datastore
import (
"database/sql"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type keystore struct {
*sql.DB
}
func (db *keystore) Get(repo *model.Repo) (*model.Key, error) {
var key = new(model.Key)
var err = meddler.QueryRow(db, key, rebind(keyQuery), repo.ID)
return key, err
}
func (db *keystore) Create(key *model.Key) error {
return meddler.Save(db, keyTable, key)
}
func (db *keystore) Update(key *model.Key) error {
return meddler.Save(db, keyTable, key)
}
func (db *keystore) Delete(key *model.Key) error {
var _, err = db.Exec(rebind(keyDeleteStmt), key.ID)
return err
}
const keyTable = "keys"
const keyQuery = "SELECT * FROM `keys` WHERE key_repo_id=? LIMIT 1"
const keyDeleteStmt = "DELETE FROM `keys` WHERE key_id=?"

View File

@@ -0,0 +1,114 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_keystore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Keys", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec(rebind("DELETE FROM `keys`"))
})
g.It("Should create a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.Keys().Create(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
})
g.It("Should update a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.Keys().Create(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
key.Private = ""
key.Public = ""
err1 := s.Keys().Update(&key)
getkey, err2 := s.Keys().Get(&model.Repo{ID: 1})
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(key.ID).Equal(getkey.ID)
g.Assert(key.Public).Equal(getkey.Public)
g.Assert(key.Private).Equal(getkey.Private)
})
g.It("Should get a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err := s.Keys().Create(&key)
g.Assert(err == nil).IsTrue()
g.Assert(key.ID != 0).IsTrue()
getkey, err := s.Keys().Get(&model.Repo{ID: 1})
g.Assert(err == nil).IsTrue()
g.Assert(key.ID).Equal(getkey.ID)
g.Assert(key.Public).Equal(getkey.Public)
g.Assert(key.Private).Equal(getkey.Private)
})
g.It("Should delete a key", func() {
key := model.Key{
RepoID: 1,
Public: fakePublicKey,
Private: fakePrivateKey,
}
err1 := s.Keys().Create(&key)
err2 := s.Keys().Delete(&key)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
_, err := s.Keys().Get(&model.Repo{ID: 1})
g.Assert(err == nil).IsFalse()
})
})
}
var fakePublicKey = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0
FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/
3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB
-----END PUBLIC KEY-----
`
var fakePrivateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----
`

41
store/datastore/logs.go Normal file
View File

@@ -0,0 +1,41 @@
package datastore
import (
"bytes"
"database/sql"
"io"
"io/ioutil"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type logstore struct {
*sql.DB
}
func (db *logstore) Read(job *model.Job) (io.ReadCloser, error) {
var log = new(model.Log)
var err = meddler.QueryRow(db, log, rebind(logQuery), job.ID)
var buf = bytes.NewBuffer(log.Data)
return ioutil.NopCloser(buf), err
}
func (db *logstore) Write(job *model.Job, r io.Reader) error {
var log = new(model.Log)
var err = meddler.QueryRow(db, log, rebind(logQuery), job.ID)
if err != nil {
log = &model.Log{JobID: job.ID}
}
log.Data, _ = ioutil.ReadAll(r)
return meddler.Save(db, logTable, log)
}
const logTable = "logs"
const logQuery = `
SELECT *
FROM logs
WHERE log_job_id=?
LIMIT 1
`

View File

@@ -0,0 +1,60 @@
package datastore
import (
"bytes"
"io/ioutil"
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_logstore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Logs", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM logs")
})
g.It("Should create a log", func() {
job := model.Job{
ID: 1,
}
buf := bytes.NewBufferString("echo hi")
err := s.Logs().Write(&job, buf)
g.Assert(err == nil).IsTrue()
rc, err := s.Logs().Read(&job)
g.Assert(err == nil).IsTrue()
defer rc.Close()
out, _ := ioutil.ReadAll(rc)
g.Assert(string(out)).Equal("echo hi")
})
g.It("Should update a log", func() {
job := model.Job{
ID: 1,
}
buf1 := bytes.NewBufferString("echo hi")
buf2 := bytes.NewBufferString("echo allo?")
err1 := s.Logs().Write(&job, buf1)
err2 := s.Logs().Write(&job, buf2)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
rc, err := s.Logs().Read(&job)
g.Assert(err == nil).IsTrue()
defer rc.Close()
out, _ := ioutil.ReadAll(rc)
g.Assert(string(out)).Equal("echo allo?")
})
})
}

60
store/datastore/nodes.go Normal file
View File

@@ -0,0 +1,60 @@
package datastore
import (
"database/sql"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type nodestore struct {
*sql.DB
}
func (db *nodestore) Get(id int64) (*model.Node, error) {
var node = new(model.Node)
var err = meddler.Load(db, nodeTable, node, id)
return node, err
}
func (db *nodestore) GetList() ([]*model.Node, error) {
var nodes = []*model.Node{}
var err = meddler.QueryAll(db, &nodes, rebind(nodeListQuery))
return nodes, err
}
func (db *nodestore) Count() (int, error) {
var count int
var err = db.QueryRow(rebind(nodeCountQuery)).Scan(&count)
return count, err
}
func (db *nodestore) Create(node *model.Node) error {
return meddler.Insert(db, nodeTable, node)
}
func (db *nodestore) Update(node *model.Node) error {
return meddler.Update(db, nodeTable, node)
}
func (db *nodestore) Delete(node *model.Node) error {
var _, err = db.Exec(rebind(nodeDeleteStmt), node.ID)
return err
}
const nodeTable = "nodes"
const nodeListQuery = `
SELECT *
FROM nodes
ORDER BY node_addr
`
const nodeCountQuery = `
SELECT COUNT(*) FROM nodes
`
const nodeDeleteStmt = `
DELETE FROM nodes
WHERE node_id=?
`

View File

@@ -0,0 +1,101 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_nodestore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Nodes", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM nodes")
})
g.It("Should create a node", func() {
node := model.Node{
Addr: "unix:///var/run/docker/docker.sock",
Arch: "linux_amd64",
}
err := s.Nodes().Create(&node)
g.Assert(err == nil).IsTrue()
g.Assert(node.ID != 0).IsTrue()
})
g.It("Should update a node", func() {
node := model.Node{
Addr: "unix:///var/run/docker/docker.sock",
Arch: "linux_amd64",
}
err := s.Nodes().Create(&node)
g.Assert(err == nil).IsTrue()
g.Assert(node.ID != 0).IsTrue()
node.Addr = "unix:///var/run/docker.sock"
err1 := s.Nodes().Update(&node)
getnode, err2 := s.Nodes().Get(node.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(node.ID).Equal(getnode.ID)
g.Assert(node.Addr).Equal(getnode.Addr)
g.Assert(node.Arch).Equal(getnode.Arch)
})
g.It("Should get a node", func() {
node := model.Node{
Addr: "unix:///var/run/docker/docker.sock",
Arch: "linux_amd64",
}
err := s.Nodes().Create(&node)
g.Assert(err == nil).IsTrue()
g.Assert(node.ID != 0).IsTrue()
getnode, err := s.Nodes().Get(node.ID)
g.Assert(err == nil).IsTrue()
g.Assert(node.ID).Equal(getnode.ID)
g.Assert(node.Addr).Equal(getnode.Addr)
g.Assert(node.Arch).Equal(getnode.Arch)
})
g.It("Should get a node list", func() {
node1 := model.Node{
Addr: "unix:///var/run/docker/docker.sock",
Arch: "linux_amd64",
}
node2 := model.Node{
Addr: "unix:///var/run/docker.sock",
Arch: "linux_386",
}
s.Nodes().Create(&node1)
s.Nodes().Create(&node2)
nodes, err := s.Nodes().GetList()
g.Assert(err == nil).IsTrue()
g.Assert(len(nodes)).Equal(2)
})
g.It("Should delete a node", func() {
node := model.Node{
Addr: "unix:///var/run/docker/docker.sock",
Arch: "linux_amd64",
}
err1 := s.Nodes().Create(&node)
err2 := s.Nodes().Delete(&node)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
_, err := s.Nodes().Get(node.ID)
g.Assert(err == nil).IsFalse()
})
})
}

91
store/datastore/repos.go Normal file
View File

@@ -0,0 +1,91 @@
package datastore
import (
"database/sql"
"strings"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type repostore struct {
*sql.DB
}
func (db *repostore) Get(id int64) (*model.Repo, error) {
var repo = new(model.Repo)
var err = meddler.Load(db, repoTable, repo, id)
return repo, err
}
func (db *repostore) GetName(name string) (*model.Repo, error) {
var repo = new(model.Repo)
var err = meddler.QueryRow(db, repo, rebind(repoNameQuery), name)
return repo, err
}
func (db *repostore) GetListOf(listof []*model.RepoLite) ([]*model.Repo, error) {
var repos = []*model.Repo{}
var size = len(listof)
if size > 999 {
size = 999
listof = listof[:999]
}
var qs = make([]string, size, size)
var in = make([]interface{}, size, size)
for i, repo := range listof {
qs[i] = "?"
in[i] = repo.FullName
}
var stmt = "SELECT * FROM repos WHERE repo_full_name IN (" + strings.Join(qs, ",") + ") ORDER BY repo_name"
var err = meddler.QueryAll(db, &repos, rebind(stmt), in...)
return repos, err
}
func (db *repostore) Count() (int, error) {
var count int
var err = db.QueryRow(rebind(repoCountQuery)).Scan(&count)
return count, err
}
func (db *repostore) Create(repo *model.Repo) error {
return meddler.Insert(db, repoTable, repo)
}
func (db *repostore) Update(repo *model.Repo) error {
return meddler.Update(db, repoTable, repo)
}
func (db *repostore) Delete(repo *model.Repo) error {
var _, err = db.Exec(rebind(repoDeleteStmt), repo.ID)
return err
}
const repoTable = "repos"
const repoNameQuery = `
SELECT *
FROM repos
WHERE repo_full_name = ?
LIMIT 1;
`
const repoListQuery = `
SELECT *
FROM repos
WHERE repo_id IN (
SELECT DISTINCT build_repo_id
FROM builds
WHERE build_author = ?
)
ORDER BY repo_full_name
`
const repoCountQuery = `
SELECT COUNT(*) FROM repos
`
const repoDeleteStmt = `
DELETE FROM repos
WHERE repo_id = ?
`

View File

@@ -0,0 +1,150 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_repostore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Repo", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM builds")
db.Exec("DELETE FROM repos")
db.Exec("DELETE FROM users")
})
g.It("Should Set a Repo", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
err1 := s.Repos().Create(&repo)
err2 := s.Repos().Update(&repo)
getrepo, err3 := s.Repos().Get(repo.ID)
if err3 != nil {
println("Get Repo Error")
println(err3.Error())
}
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(repo.ID).Equal(getrepo.ID)
})
g.It("Should Add a Repo", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
err := s.Repos().Create(&repo)
g.Assert(err == nil).IsTrue()
g.Assert(repo.ID != 0).IsTrue()
})
g.It("Should Get a Repo by ID", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
s.Repos().Create(&repo)
getrepo, err := s.Repos().Get(repo.ID)
g.Assert(err == nil).IsTrue()
g.Assert(repo.ID).Equal(getrepo.ID)
g.Assert(repo.UserID).Equal(getrepo.UserID)
g.Assert(repo.Owner).Equal(getrepo.Owner)
g.Assert(repo.Name).Equal(getrepo.Name)
})
g.It("Should Get a Repo by Name", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
s.Repos().Create(&repo)
getrepo, err := s.Repos().GetName(repo.FullName)
g.Assert(err == nil).IsTrue()
g.Assert(repo.ID).Equal(getrepo.ID)
g.Assert(repo.UserID).Equal(getrepo.UserID)
g.Assert(repo.Owner).Equal(getrepo.Owner)
g.Assert(repo.Name).Equal(getrepo.Name)
})
// g.It("Should Get a Repo List by User", func() {
// repo1 := model.Repo{
// UserID: 1,
// FullName: "bradrydzewski/drone",
// Owner: "bradrydzewski",
// Name: "drone",
// }
// repo2 := model.Repo{
// UserID: 1,
// FullName: "bradrydzewski/drone-dart",
// Owner: "bradrydzewski",
// Name: "drone-dart",
// }
// s.Repos().Create(&repo1)
// s.Repos().Create(&repo2)
// CreateBuild(db, &Build{RepoID: repo1.ID, Author: "bradrydzewski"})
// CreateBuild(db, &Build{RepoID: repo1.ID, Author: "johnsmith"})
// repos, err := GetRepoList(db, &User{ID: 1, Login: "bradrydzewski"})
// g.Assert(err == nil).IsTrue()
// g.Assert(len(repos)).Equal(1)
// g.Assert(repos[0].UserID).Equal(repo1.UserID)
// g.Assert(repos[0].Owner).Equal(repo1.Owner)
// g.Assert(repos[0].Name).Equal(repo1.Name)
// })
g.It("Should Delete a Repo", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
s.Repos().Create(&repo)
_, err1 := s.Repos().Get(repo.ID)
err2 := s.Repos().Delete(&repo)
_, err3 := s.Repos().Get(repo.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsFalse()
})
g.It("Should Enforce Unique Repo Name", func() {
repo1 := model.Repo{
UserID: 1,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
repo2 := model.Repo{
UserID: 2,
FullName: "bradrydzewski/drone",
Owner: "bradrydzewski",
Name: "drone",
}
err1 := s.Repos().Create(&repo1)
err2 := s.Repos().Create(&repo2)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsFalse()
})
})
}

103
store/datastore/store.go Normal file
View File

@@ -0,0 +1,103 @@
package datastore
import (
"database/sql"
"os"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store"
"github.com/drone/drone/store/migration"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
"github.com/rubenv/sql-migrate"
"github.com/russross/meddler"
log "github.com/Sirupsen/logrus"
)
// From creates a datastore from an existing database connection.
func From(db *sql.DB) store.Store {
return store.New(
&nodestore{db},
&userstore{db},
&repostore{db},
&keystore{db},
&buildstore{db},
&jobstore{db},
&logstore{db},
)
}
// Load opens a new database connection with the specified driver
// and connection string specified in the environment variables.
func Load(env envconfig.Env) store.Store {
var (
driver = env.String("DATABASE_DRIVER", "sqlite3")
config = env.String("DATABASE_CONFIG", "drone.sqlite")
)
log.Infof("using database driver %s", driver)
log.Infof("using database config %s", config)
return From(
Open(driver, config),
)
}
// Open opens a new database connection with the specified
// driver and connection string and returns a store.
func Open(driver, config string) *sql.DB {
db, err := sql.Open(driver, config)
if err != nil {
log.Errorln(err)
log.Fatalln("database connection failed")
}
setupMeddler(driver)
if err := setupDatabase(driver, db); err != nil {
log.Errorln(err)
log.Fatalln("migration failed")
}
return db
}
// OpenTest opens a new database connection for testing purposes.
// The database driver and connection string are provided by
// environment variables, with fallback to in-memory sqlite.
func openTest() *sql.DB {
var (
driver = "sqlite3"
config = ":memory:"
)
if os.Getenv("DATABASE_DRIVER") != "" {
driver = os.Getenv("DATABASE_DRIVER")
config = os.Getenv("DATABASE_CONFIG")
}
return Open(driver, config)
}
// helper function to setup the databsae by performing
// automated database migration steps.
func setupDatabase(driver string, db *sql.DB) error {
var migrations = &migrate.AssetMigrationSource{
Asset: migration.Asset,
AssetDir: migration.AssetDir,
Dir: driver,
}
_, err := migrate.Exec(db, driver, migrations, migrate.Up)
return err
}
// helper function to setup the meddler default driver
// based on the selected driver name.
func setupMeddler(driver string) {
switch driver {
case "sqlite3":
meddler.Default = meddler.SQLite
case "mysql":
meddler.Default = meddler.MySQL
case "postgres":
meddler.Default = meddler.PostgreSQL
}
}

111
store/datastore/users.go Normal file
View File

@@ -0,0 +1,111 @@
package datastore
import (
"database/sql"
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
type userstore struct {
*sql.DB
}
func (db *userstore) Get(id int64) (*model.User, error) {
var usr = new(model.User)
var err = meddler.Load(db, userTable, usr, id)
return usr, err
}
func (db *userstore) GetLogin(login string) (*model.User, error) {
var usr = new(model.User)
var err = meddler.QueryRow(db, usr, rebind(userLoginQuery), login)
return usr, err
}
func (db *userstore) GetList() ([]*model.User, error) {
var users = []*model.User{}
var err = meddler.QueryAll(db, &users, rebind(userListQuery))
return users, err
}
func (db *userstore) GetFeed(user *model.User, limit, offset int) ([]*model.Feed, error) {
// var feed = []*Feed{}
// var err = meddler.QueryAll(db, &feed, rebind(userFeedQuery), user.Login, limit, offset)
// return feed, err
return nil, nil
}
func (db *userstore) Count() (int, error) {
var count int
var err = db.QueryRow(rebind(userCountQuery)).Scan(&count)
return count, err
}
func (db *userstore) Create(user *model.User) error {
return meddler.Insert(db, userTable, user)
}
func (db *userstore) Update(user *model.User) error {
return meddler.Update(db, userTable, user)
}
func (db *userstore) Delete(user *model.User) error {
var _, err = db.Exec(rebind(userDeleteStmt), user.ID)
return err
}
const userTable = "users"
const userLoginQuery = `
SELECT *
FROM users
WHERE user_login=?
LIMIT 1
`
const userListQuery = `
SELECT *
FROM users
ORDER BY user_login ASC
`
const userCountQuery = `
SELECT count(1)
FROM users
`
const userDeleteStmt = `
DELETE FROM users
WHERE user_id=?
`
const userFeedQuery = `
SELECT
repo_owner
,repo_name
,repo_full_name
,build_number
,build_event
,build_status
,build_created
,build_started
,build_finished
,build_commit
,build_branch
,build_ref
,build_refspec
,build_remote
,build_title
,build_message
,build_author
,build_email
,build_avatar
FROM
builds b
,repos r
WHERE b.build_repo_id = r.repo_id
AND b.build_author = ?
ORDER BY b.build_id DESC
LIMIT ? OFFSET ?
`

View File

@@ -0,0 +1,209 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func Test_userstore(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("User", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM users")
db.Exec("DELETE FROM repos")
db.Exec("DELETE FROM builds")
db.Exec("DELETE FROM jobs")
})
g.It("Should Update a User", func() {
user := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
err1 := s.Users().Create(&user)
err2 := s.Users().Update(&user)
getuser, err3 := s.Users().Get(user.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(user.ID).Equal(getuser.ID)
})
g.It("Should Add a new User", func() {
user := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
err := s.Users().Create(&user)
g.Assert(err == nil).IsTrue()
g.Assert(user.ID != 0).IsTrue()
})
g.It("Should Get a User", func() {
user := model.User{
Login: "joe",
Token: "f0b461ca586c27872b43a0685cbc2847",
Secret: "976f22a5eef7caacb7e678d6c52f49b1",
Email: "foo@bar.com",
Avatar: "b9015b0857e16ac4d94a0ffd9a0b79c8",
Active: true,
Admin: true,
}
s.Users().Create(&user)
getuser, err := s.Users().Get(user.ID)
g.Assert(err == nil).IsTrue()
g.Assert(user.ID).Equal(getuser.ID)
g.Assert(user.Login).Equal(getuser.Login)
g.Assert(user.Token).Equal(getuser.Token)
g.Assert(user.Secret).Equal(getuser.Secret)
g.Assert(user.Email).Equal(getuser.Email)
g.Assert(user.Avatar).Equal(getuser.Avatar)
g.Assert(user.Active).Equal(getuser.Active)
g.Assert(user.Admin).Equal(getuser.Admin)
})
g.It("Should Get a User By Login", func() {
user := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
s.Users().Create(&user)
getuser, err := s.Users().GetLogin(user.Login)
g.Assert(err == nil).IsTrue()
g.Assert(user.ID).Equal(getuser.ID)
g.Assert(user.Login).Equal(getuser.Login)
})
g.It("Should Enforce Unique User Login", func() {
user1 := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
user2 := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "ab20g0ddaf012c744e136da16aa21ad9",
}
err1 := s.Users().Create(&user1)
err2 := s.Users().Create(&user2)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsFalse()
})
g.It("Should Get a User List", func() {
user1 := model.User{
Login: "jane",
Email: "foo@bar.com",
Token: "ab20g0ddaf012c744e136da16aa21ad9",
}
user2 := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
s.Users().Create(&user1)
s.Users().Create(&user2)
users, err := s.Users().GetList()
g.Assert(err == nil).IsTrue()
g.Assert(len(users)).Equal(2)
g.Assert(users[0].Login).Equal(user1.Login)
g.Assert(users[0].Email).Equal(user1.Email)
g.Assert(users[0].Token).Equal(user1.Token)
})
g.It("Should Get a User Count", func() {
user1 := model.User{
Login: "jane",
Email: "foo@bar.com",
Token: "ab20g0ddaf012c744e136da16aa21ad9",
}
user2 := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
s.Users().Create(&user1)
s.Users().Create(&user2)
count, err := s.Users().Count()
g.Assert(err == nil).IsTrue()
g.Assert(count).Equal(2)
})
g.It("Should Get a User Count Zero", func() {
count, err := s.Users().Count()
g.Assert(err == nil).IsTrue()
g.Assert(count).Equal(0)
})
g.It("Should Del a User", func() {
user := model.User{
Login: "joe",
Email: "foo@bar.com",
Token: "e42080dddf012c718e476da161d21ad5",
}
s.Users().Create(&user)
_, err1 := s.Users().Get(user.ID)
err2 := s.Users().Delete(&user)
_, err3 := s.Users().Get(user.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsFalse()
})
// g.It("Should get the Build feed for a User", func() {
// repo1 := &Repo{
// UserID: 1,
// Owner: "bradrydzewski",
// Name: "drone",
// FullName: "bradrydzewski/drone",
// }
// repo2 := &Repo{
// UserID: 2,
// Owner: "drone",
// Name: "drone",
// FullName: "drone/drone",
// }
// CreateRepo(db, repo1)
// CreateRepo(db, repo2)
// build1 := &Build{
// RepoID: repo1.ID,
// Status: StatusFailure,
// Author: "bradrydzewski",
// }
// build2 := &Build{
// RepoID: repo1.ID,
// Status: StatusSuccess,
// Author: "bradrydzewski",
// }
// build3 := &Build{
// RepoID: repo2.ID,
// Status: StatusSuccess,
// Author: "octocat",
// }
// CreateBuild(db, build1)
// CreateBuild(db, build2)
// CreateBuild(db, build3)
// builds, err := GetUserFeed(db, &User{ID: 1, Login: "bradrydzewski"}, 20, 0)
// g.Assert(err == nil).IsTrue()
// g.Assert(len(builds)).Equal(2)
// g.Assert(builds[0].Owner).Equal("bradrydzewski")
// g.Assert(builds[0].Name).Equal("drone")
// })
})
}

35
store/datastore/utils.go Normal file
View File

@@ -0,0 +1,35 @@
package datastore
import (
"strconv"
"github.com/russross/meddler"
)
// rebind is a helper function that changes the sql
// bind type from ? to $ for postgres queries.
func rebind(query string) string {
if meddler.Default != meddler.PostgreSQL {
return query
}
qb := []byte(query)
// Add space enough for 5 params before we have to allocate
rqb := make([]byte, 0, len(qb)+5)
j := 1
for _, b := range qb {
switch b {
case '?':
rqb = append(rqb, '$')
for _, b := range strconv.Itoa(j) {
rqb = append(rqb, byte(b))
}
j++
case '`':
rqb = append(rqb, ' ')
default:
rqb = append(rqb, b)
}
}
return string(rqb)
}

43
store/jobs.go Normal file
View File

@@ -0,0 +1,43 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type JobStore interface {
// Get gets a job by unique ID.
Get(int64) (*model.Job, error)
// GetNumber gets a job by number.
GetNumber(*model.Build, int) (*model.Job, error)
// GetList gets a list of all users in the system.
GetList(*model.Build) ([]*model.Job, error)
// Create creates a job.
Create(*model.Job) error
// Update updates a job.
Update(*model.Job) error
}
func GetJob(c context.Context, id int64) (*model.Job, error) {
return FromContext(c).Jobs().Get(id)
}
func GetJobNumber(c context.Context, build *model.Build, num int) (*model.Job, error) {
return FromContext(c).Jobs().GetNumber(build, num)
}
func GetJobList(c context.Context, build *model.Build) ([]*model.Job, error) {
return FromContext(c).Jobs().GetList(build)
}
func CreateJob(c context.Context, job *model.Job) error {
return FromContext(c).Jobs().Create(job)
}
func UpdateJob(c context.Context, job *model.Job) error {
return FromContext(c).Jobs().Update(job)
}

36
store/keys.go Normal file
View File

@@ -0,0 +1,36 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type KeyStore interface {
// Get gets a key by unique repository ID.
Get(*model.Repo) (*model.Key, error)
// Create creates a new key.
Create(*model.Key) error
// Update updates a user key.
Update(*model.Key) error
// Delete deletes a user key.
Delete(*model.Key) error
}
func GetKey(c context.Context, repo *model.Repo) (*model.Key, error) {
return FromContext(c).Keys().Get(repo)
}
func CreateKey(c context.Context, key *model.Key) error {
return FromContext(c).Keys().Create(key)
}
func UpdateKey(c context.Context, key *model.Key) error {
return FromContext(c).Keys().Update(key)
}
func DeleteKey(c context.Context, key *model.Key) error {
return FromContext(c).Keys().Delete(key)
}

24
store/logs.go Normal file
View File

@@ -0,0 +1,24 @@
package store
import (
"io"
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type LogStore interface {
// Read reads the Job logs from the datastore.
Read(*model.Job) (io.ReadCloser, error)
// Write writes the job logs to the datastore.
Write(*model.Job, io.Reader) error
}
func ReadLog(c context.Context, job *model.Job) (io.ReadCloser, error) {
return FromContext(c).Logs().Read(job)
}
func WriteLog(c context.Context, job *model.Job, r io.Reader) error {
return FromContext(c).Logs().Write(job, r)
}

View File

@@ -0,0 +1,3 @@
package migration
//go:generate go-bindata -pkg migration -o migration_gen.go sqlite3/ mysql/ postgres/

View File

@@ -0,0 +1,125 @@
-- +migrate Up
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTO_INCREMENT
,user_login VARCHAR(500)
,user_token VARCHAR(500)
,user_secret VARCHAR(500)
,user_expiry INTEGER
,user_email VARCHAR(500)
,user_avatar VARCHAR(500)
,user_active BOOLEAN
,user_admin BOOLEAN
,user_hash VARCHAR(500)
,UNIQUE(user_login)
);
CREATE TABLE repos (
repo_id INTEGER PRIMARY KEY AUTO_INCREMENT
,repo_user_id INTEGER
,repo_owner VARCHAR(255)
,repo_name VARCHAR(255)
,repo_full_name VARCHAR(511)
,repo_avatar VARCHAR(500)
,repo_link VARCHAR(1000)
,repo_clone VARCHAR(1000)
,repo_branch VARCHAR(500)
,repo_timeout INTEGER
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_allow_pr BOOLEAN
,repo_allow_push BOOLEAN
,repo_allow_deploys BOOLEAN
,repo_allow_tags BOOLEAN
,repo_hash VARCHAR(500)
,UNIQUE(repo_full_name)
);
CREATE TABLE `keys` (
key_id INTEGER PRIMARY KEY AUTO_INCREMENT
,key_repo_id INTEGER
,key_public MEDIUMBLOB
,key_private MEDIUMBLOB
,UNIQUE(key_repo_id)
);
CREATE TABLE builds (
build_id INTEGER PRIMARY KEY AUTO_INCREMENT
,build_repo_id INTEGER
,build_number INTEGER
,build_event VARCHAR(500)
,build_status VARCHAR(500)
,build_enqueued INTEGER
,build_created INTEGER
,build_started INTEGER
,build_finished INTEGER
,build_commit VARCHAR(500)
,build_branch VARCHAR(500)
,build_ref VARCHAR(500)
,build_refspec VARCHAR(1000)
,build_remote VARCHAR(500)
,build_title VARCHAR(1000)
,build_message VARCHAR(2000)
,build_timestamp INTEGER
,build_author VARCHAR(500)
,build_avatar VARCHAR(1000)
,build_email VARCHAR(500)
,build_link VARCHAR(1000)
,UNIQUE(build_number, build_repo_id)
);
CREATE INDEX ix_build_repo ON builds (build_repo_id);
CREATE TABLE jobs (
job_id INTEGER PRIMARY KEY AUTO_INCREMENT
,job_node_id INTEGER
,job_build_id INTEGER
,job_number INTEGER
,job_status VARCHAR(500)
,job_exit_code INTEGER
,job_started INTEGER
,job_enqueued INTEGER
,job_finished INTEGER
,job_environment VARCHAR(2000)
,UNIQUE(job_build_id, job_number)
);
CREATE INDEX ix_job_build ON jobs (job_build_id);
CREATE INDEX ix_job_node ON jobs (job_node_id);
CREATE TABLE IF NOT EXISTS logs (
log_id INTEGER PRIMARY KEY AUTO_INCREMENT
,log_job_id INTEGER
,log_data MEDIUMBLOB
,UNIQUE(log_job_id)
);
CREATE TABLE IF NOT EXISTS nodes (
node_id INTEGER PRIMARY KEY AUTO_INCREMENT
,node_addr VARCHAR(1024)
,node_arch VARCHAR(50)
,node_cert MEDIUMBLOB
,node_key MEDIUMBLOB
,node_ca MEDIUMBLOB
);
INSERT INTO nodes VALUES(null, 'unix:///var/run/docker.sock', 'linux_amd64', '', '', '');
INSERT INTO nodes VALUES(null, 'unix:///var/run/docker.sock', 'linux_amd64', '', '', '');
-- +migrate Down
DROP TABLE nodes;
DROP TABLE logs;
DROP TABLE jobs;
DROP TABLE builds;
DROP TABLE `keys`;
DROP TABLE stars;
DROP TABLE repos;
DROP TABLE users;

View File

@@ -0,0 +1,126 @@
-- +migrate Up
CREATE TABLE users (
user_id SERIAL PRIMARY KEY
,user_login VARCHAR(40)
,user_token VARCHAR(128)
,user_secret VARCHAR(128)
,user_expiry INTEGER
,user_email VARCHAR(256)
,user_avatar VARCHAR(256)
,user_active BOOLEAN
,user_admin BOOLEAN
,user_hash VARCHAR(128)
,UNIQUE(user_login)
);
CREATE TABLE repos (
repo_id SERIAL PRIMARY KEY
,repo_user_id INTEGER
,repo_owner VARCHAR(255)
,repo_name VARCHAR(255)
,repo_full_name VARCHAR(511)
,repo_avatar VARCHAR(500)
,repo_link VARCHAR(1000)
,repo_clone VARCHAR(1000)
,repo_branch VARCHAR(500)
,repo_timeout INTEGER
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_allow_pr BOOLEAN
,repo_allow_push BOOLEAN
,repo_allow_deploys BOOLEAN
,repo_allow_tags BOOLEAN
,repo_hash VARCHAR(500)
,UNIQUE(repo_full_name)
);
CREATE TABLE keys (
key_id SERIAL PRIMARY KEY
,key_repo_id INTEGER
,key_public BYTEA
,key_private BYTEA
,UNIQUE(key_repo_id)
);
CREATE TABLE builds (
build_id SERIAL PRIMARY KEY
,build_repo_id INTEGER
,build_number INTEGER
,build_event VARCHAR(25)
,build_status VARCHAR(25)
,build_enqueued INTEGER
,build_created INTEGER
,build_started INTEGER
,build_finished INTEGER
,build_commit VARCHAR(40)
,build_branch VARCHAR(256)
,build_ref VARCHAR(512)
,build_refspec VARCHAR(512)
,build_remote VARCHAR(512)
,build_title VARCHAR(1000)
,build_message VARCHAR(2000)
,build_timestamp INTEGER
,build_author VARCHAR(40)
,build_avatar VARCHAR(1000)
,build_email VARCHAR(500)
,build_link VARCHAR(1000)
,UNIQUE(build_number, build_repo_id)
);
CREATE INDEX ix_build_repo ON builds (build_repo_id);
CREATE TABLE jobs (
job_id SERIAL PRIMARY KEY
,job_node_id INTEGER
,job_build_id INTEGER
,job_number INTEGER
,job_status VARCHAR(25)
,job_exit_code INTEGER
,job_started INTEGER
,job_enqueued INTEGER
,job_finished INTEGER
,job_environment VARCHAR(2000)
,UNIQUE(job_build_id, job_number)
);
CREATE INDEX ix_job_build ON jobs (job_build_id);
CREATE INDEX ix_job_node ON jobs (job_node_id);
CREATE TABLE IF NOT EXISTS logs (
log_id SERIAL PRIMARY KEY
,log_job_id INTEGER
,log_data BYTEA
,UNIQUE(log_job_id)
);
CREATE TABLE IF NOT EXISTS nodes (
node_id SERIAL PRIMARY KEY
,node_addr VARCHAR(1024)
,node_arch VARCHAR(50)
,node_cert BYTEA
,node_key BYTEA
,node_ca BYTEA
);
INSERT INTO nodes (node_addr, node_arch, node_cert, node_key, node_ca) VALUES
('unix:///var/run/docker.sock', 'linux_amd64', '', '', ''),
('unix:///var/run/docker.sock', 'linux_amd64', '', '', '');
-- +migrate Down
DROP TABLE nodes;
DROP TABLE logs;
DROP TABLE jobs;
DROP TABLE builds;
DROP TABLE keys;
DROP TABLE stars;
DROP TABLE repos;
DROP TABLE users;

View File

@@ -0,0 +1,135 @@
-- +migrate Up
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT
,user_login TEXT
,user_token TEXT
,user_secret TEXT
,user_expiry INTEGER
,user_email TEXT
,user_avatar TEXT
,user_active BOOLEAN
,user_admin BOOLEAN
,user_hash TEXT
,UNIQUE(user_login)
);
CREATE TABLE repos (
repo_id INTEGER PRIMARY KEY AUTOINCREMENT
,repo_user_id INTEGER
,repo_owner TEXT
,repo_name TEXT
,repo_full_name TEXT
,repo_avatar TEXT
,repo_link TEXT
,repo_clone TEXT
,repo_branch TEXT
,repo_timeout INTEGER
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_allow_pr BOOLEAN
,repo_allow_push BOOLEAN
,repo_allow_deploys BOOLEAN
,repo_allow_tags BOOLEAN
,repo_hash TEXT
,UNIQUE(repo_full_name)
);
CREATE TABLE stars (
star_id INTEGER PRIMARY KEY AUTOINCREMENT
,star_repo_id INTEGER
,star_user_id INTEGER
,UNIQUE(star_repo_id, star_user_id)
);
CREATE INDEX ix_star_user ON stars (star_user_id);
CREATE TABLE keys (
key_id INTEGER PRIMARY KEY AUTOINCREMENT
,key_repo_id INTEGER
,key_public BLOB
,key_private BLOB
,UNIQUE(key_repo_id)
);
CREATE TABLE builds (
build_id INTEGER PRIMARY KEY AUTOINCREMENT
,build_repo_id INTEGER
,build_number INTEGER
,build_event TEXT
,build_status TEXT
,build_enqueued INTEGER
,build_created INTEGER
,build_started INTEGER
,build_finished INTEGER
,build_commit TEXT
,build_branch TEXT
,build_ref TEXT
,build_refspec TEXT
,build_remote TEXT
,build_title TEXT
,build_message TEXT
,build_timestamp INTEGER
,build_author TEXT
,build_avatar TEXT
,build_email TEXT
,build_link TEXT
,UNIQUE(build_number, build_repo_id)
);
CREATE INDEX ix_build_repo ON builds (build_repo_id);
CREATE INDEX ix_build_author ON builds (build_author);
CREATE TABLE jobs (
job_id INTEGER PRIMARY KEY AUTOINCREMENT
,job_node_id INTEGER
,job_build_id INTEGER
,job_number INTEGER
,job_status TEXT
,job_exit_code INTEGER
,job_enqueued INTEGER
,job_started INTEGER
,job_finished INTEGER
,job_environment TEXT
,UNIQUE(job_build_id, job_number)
);
CREATE INDEX ix_job_build ON jobs (job_build_id);
CREATE INDEX ix_job_node ON jobs (job_node_id);
CREATE TABLE IF NOT EXISTS logs (
log_id INTEGER PRIMARY KEY AUTOINCREMENT
,log_job_id INTEGER
,log_data BLOB
,UNIQUE(log_job_id)
);
CREATE TABLE IF NOT EXISTS nodes (
node_id INTEGER PRIMARY KEY AUTOINCREMENT
,node_addr TEXT
,node_arch TEXT
,node_cert BLOB
,node_key BLOB
,node_ca BLOB
);
INSERT INTO nodes VALUES(null, 'unix:///var/run/docker.sock', 'linux_amd64', '', '', '');
INSERT INTO nodes VALUES(null, 'unix:///var/run/docker.sock', 'linux_amd64', '', '', '');
-- +migrate Down
DROP TABLE nodes;
DROP TABLE logs;
DROP TABLE jobs;
DROP TABLE builds;
DROP TABLE keys;
DROP TABLE stars;
DROP TABLE repos;
DROP TABLE users;

50
store/nodes.go Normal file
View File

@@ -0,0 +1,50 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type NodeStore interface {
// Get gets a user by unique ID.
Get(int64) (*model.Node, error)
// GetList gets a list of all nodes in the system.
GetList() ([]*model.Node, error)
// Count gets a count of all nodes in the system.
Count() (int, error)
// Create creates a node.
Create(*model.Node) error
// Update updates a node.
Update(*model.Node) error
// Delete deletes a node.
Delete(*model.Node) error
}
func GetNode(c context.Context, id int64) (*model.Node, error) {
return FromContext(c).Nodes().Get(id)
}
func GetNodeList(c context.Context) ([]*model.Node, error) {
return FromContext(c).Nodes().GetList()
}
func CountNodes(c context.Context) (int, error) {
return FromContext(c).Nodes().Count()
}
func CreateNode(c context.Context, node *model.Node) error {
return FromContext(c).Nodes().Create(node)
}
func UpdateNode(c context.Context, node *model.Node) error {
return FromContext(c).Nodes().Update(node)
}
func DeleteNode(c context.Context, node *model.Node) error {
return FromContext(c).Nodes().Delete(node)
}

61
store/repos.go Normal file
View File

@@ -0,0 +1,61 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type RepoStore interface {
// Get gets a repo by unique ID.
Get(int64) (*model.Repo, error)
// GetLogin gets a repo by its full name.
GetName(string) (*model.Repo, error)
// GetListOf gets the list of enumerated repos in the system.
GetListOf([]*model.RepoLite) ([]*model.Repo, error)
// Count gets a count of all repos in the system.
Count() (int, error)
// Create creates a new repository.
Create(*model.Repo) error
// Update updates a user repository.
Update(*model.Repo) error
// Delete deletes a user repository.
Delete(*model.Repo) error
}
func GetRepo(c context.Context, id int64) (*model.Repo, error) {
return FromContext(c).Repos().Get(id)
}
func GetRepoName(c context.Context, name string) (*model.Repo, error) {
return FromContext(c).Repos().GetName(name)
}
func GetRepoOwnerName(c context.Context, owner, name string) (*model.Repo, error) {
return FromContext(c).Repos().GetName(owner + "/" + name)
}
func GetRepoListOf(c context.Context, listof []*model.RepoLite) ([]*model.Repo, error) {
return FromContext(c).Repos().GetListOf(listof)
}
func CountRepos(c context.Context) (int, error) {
return FromContext(c).Repos().Count()
}
func CreateRepo(c context.Context, repo *model.Repo) error {
return FromContext(c).Repos().Create(repo)
}
func UpdateRepo(c context.Context, repo *model.Repo) error {
return FromContext(c).Repos().Update(repo)
}
func DeleteRepo(c context.Context, repo *model.Repo) error {
return FromContext(c).Repos().Update(repo)
}

49
store/store.go Normal file
View File

@@ -0,0 +1,49 @@
package store
type Store interface {
Nodes() NodeStore
Users() UserStore
Repos() RepoStore
Keys() KeyStore
Builds() BuildStore
Jobs() JobStore
Logs() LogStore
}
type store struct {
nodes NodeStore
users UserStore
repos RepoStore
keys KeyStore
builds BuildStore
jobs JobStore
logs LogStore
}
func (s *store) Nodes() NodeStore { return s.nodes }
func (s *store) Users() UserStore { return s.users }
func (s *store) Repos() RepoStore { return s.repos }
func (s *store) Keys() KeyStore { return s.keys }
func (s *store) Builds() BuildStore { return s.builds }
func (s *store) Jobs() JobStore { return s.jobs }
func (s *store) Logs() LogStore { return s.logs }
func New(
nodes NodeStore,
users UserStore,
repos RepoStore,
keys KeyStore,
builds BuildStore,
jobs JobStore,
logs LogStore,
) Store {
return &store{
nodes,
users,
repos,
keys,
builds,
jobs,
logs,
}
}

64
store/users.go Normal file
View File

@@ -0,0 +1,64 @@
package store
import (
"github.com/drone/drone/model"
"golang.org/x/net/context"
)
type UserStore interface {
// Get gets a user by unique ID.
Get(int64) (*model.User, error)
// GetLogin gets a user by unique Login name.
GetLogin(string) (*model.User, error)
// GetList gets a list of all users in the system.
GetList() ([]*model.User, error)
// GetFeed gets a user activity feed.
GetFeed(*model.User, int, int) ([]*model.Feed, error)
// Count gets a count of all users in the system.
Count() (int, error)
// Create creates a new user account.
Create(*model.User) error
// Update updates a user account.
Update(*model.User) error
// Delete deletes a user account.
Delete(*model.User) error
}
func GetUser(c context.Context, id int64) (*model.User, error) {
return FromContext(c).Users().Get(id)
}
func GetUserLogin(c context.Context, login string) (*model.User, error) {
return FromContext(c).Users().GetLogin(login)
}
func GetUserList(c context.Context) ([]*model.User, error) {
return FromContext(c).Users().GetList()
}
func GetUserFeed(c context.Context, user *model.User, limit, offset int) ([]*model.Feed, error) {
return FromContext(c).Users().GetFeed(user, limit, offset)
}
func CountUsers(c context.Context) (int, error) {
return FromContext(c).Users().Count()
}
func CreateUser(c context.Context, user *model.User) error {
return FromContext(c).Users().Create(user)
}
func UpdateUser(c context.Context, user *model.User) error {
return FromContext(c).Users().Update(user)
}
func DeleteUser(c context.Context, user *model.User) error {
return FromContext(c).Users().Delete(user)
}