Merge pull request #1992 from bradrydzewski/feature/steps

add registry management
This commit is contained in:
Brad Rydzewski 2017-04-06 22:00:48 +09:00 committed by GitHub
commit 7a98bf5398
24 changed files with 948 additions and 412 deletions

View file

@ -117,4 +117,19 @@ type Client interface {
// AgentList returns a list of build agents. // AgentList returns a list of build agents.
AgentList() ([]*model.Agent, error) AgentList() ([]*model.Agent, error)
// Registry returns a registry by hostname.
Registry(owner, name, hostname string) (*model.Registry, error)
// RegistryList returns a list of all repository registries.
RegistryList(owner, name string) ([]*model.Registry, error)
// RegistryCreate creates a registry.
RegistryCreate(owner, name string, registry *model.Registry) (*model.Registry, error)
// RegistryUpdate updates a registry.
RegistryUpdate(owner, name string, registry *model.Registry) (*model.Registry, error)
// RegistryDelete deletes a registry.
RegistryDelete(owner, name, hostname string) error
} }

View file

@ -26,32 +26,34 @@ const (
pathLogs = "%s/api/queue/logs/%d" pathLogs = "%s/api/queue/logs/%d"
pathLogsAuth = "%s/api/queue/logs/%d?access_token=%s" pathLogsAuth = "%s/api/queue/logs/%d?access_token=%s"
pathSelf = "%s/api/user" pathSelf = "%s/api/user"
pathFeed = "%s/api/user/feed" pathFeed = "%s/api/user/feed"
pathRepos = "%s/api/user/repos" pathRepos = "%s/api/user/repos"
pathRepo = "%s/api/repos/%s/%s" pathRepo = "%s/api/repos/%s/%s"
pathChown = "%s/api/repos/%s/%s/chown" pathChown = "%s/api/repos/%s/%s/chown"
pathEncrypt = "%s/api/repos/%s/%s/encrypt" pathEncrypt = "%s/api/repos/%s/%s/encrypt"
pathBuilds = "%s/api/repos/%s/%s/builds" pathBuilds = "%s/api/repos/%s/%s/builds"
pathBuild = "%s/api/repos/%s/%s/builds/%v" pathBuild = "%s/api/repos/%s/%s/builds/%v"
pathApprove = "%s/api/repos/%s/%s/builds/%d/approve" pathApprove = "%s/api/repos/%s/%s/builds/%d/approve"
pathDecline = "%s/api/repos/%s/%s/builds/%d/decline" pathDecline = "%s/api/repos/%s/%s/builds/%d/decline"
pathJob = "%s/api/repos/%s/%s/builds/%d/%d" pathJob = "%s/api/repos/%s/%s/builds/%d/%d"
pathLog = "%s/api/repos/%s/%s/logs/%d/%d" pathLog = "%s/api/repos/%s/%s/logs/%d/%d"
pathKey = "%s/api/repos/%s/%s/key" pathKey = "%s/api/repos/%s/%s/key"
pathSign = "%s/api/repos/%s/%s/sign" pathSign = "%s/api/repos/%s/%s/sign"
pathRepoSecrets = "%s/api/repos/%s/%s/secrets" pathRepoSecrets = "%s/api/repos/%s/%s/secrets"
pathRepoSecret = "%s/api/repos/%s/%s/secrets/%s" pathRepoSecret = "%s/api/repos/%s/%s/secrets/%s"
pathTeamSecrets = "%s/api/teams/%s/secrets" pathRepoRegistries = "%s/api/repos/%s/%s/registry"
pathTeamSecret = "%s/api/teams/%s/secrets/%s" pathRepoRegistry = "%s/api/repos/%s/%s/registry/%s"
pathGlobalSecrets = "%s/api/global/secrets" pathTeamSecrets = "%s/api/teams/%s/secrets"
pathGlobalSecret = "%s/api/global/secrets/%s" pathTeamSecret = "%s/api/teams/%s/secrets/%s"
pathNodes = "%s/api/nodes" pathGlobalSecrets = "%s/api/global/secrets"
pathNode = "%s/api/nodes/%d" pathGlobalSecret = "%s/api/global/secrets/%s"
pathUsers = "%s/api/users" pathNodes = "%s/api/nodes"
pathUser = "%s/api/users/%s" pathNode = "%s/api/nodes/%d"
pathBuildQueue = "%s/api/builds" pathUsers = "%s/api/users"
pathAgent = "%s/api/agents" pathUser = "%s/api/users/%s"
pathBuildQueue = "%s/api/builds"
pathAgent = "%s/api/agents"
) )
type client struct { type client struct {
@ -373,6 +375,44 @@ func (c *client) AgentList() ([]*model.Agent, error) {
return out, err return out, err
} }
// Registry returns a registry by hostname.
func (c *client) Registry(owner, name, hostname string) (*model.Registry, error) {
out := new(model.Registry)
uri := fmt.Sprintf(pathRepoRegistry, c.base, owner, name, hostname)
err := c.get(uri, out)
return out, err
}
// RegistryList returns a list of all repository registries.
func (c *client) RegistryList(owner string, name string) ([]*model.Registry, error) {
var out []*model.Registry
uri := fmt.Sprintf(pathRepoRegistries, c.base, owner, name)
err := c.get(uri, &out)
return out, err
}
// RegistryCreate creates a registry.
func (c *client) RegistryCreate(owner, name string, in *model.Registry) (*model.Registry, error) {
out := new(model.Registry)
uri := fmt.Sprintf(pathRepoRegistries, c.base, owner, name)
err := c.post(uri, in, out)
return out, err
}
// RegistryUpdate updates a registry.
func (c *client) RegistryUpdate(owner, name string, in *model.Registry) (*model.Registry, error) {
out := new(model.Registry)
uri := fmt.Sprintf(pathRepoRegistry, c.base, owner, name, in.Address)
err := c.patch(uri, in, out)
return out, err
}
// RegistryDelete deletes a registry.
func (c *client) RegistryDelete(owner, name, hostname string) error {
uri := fmt.Sprintf(pathRepoRegistry, c.base, owner, name, hostname)
return c.delete(uri)
}
// //
// http request helper functions // http request helper functions
// //

View file

@ -37,6 +37,7 @@ func main() {
deployCmd, deployCmd,
execCmd, execCmd,
infoCmd, infoCmd,
registryCmd,
secretCmd, secretCmd,
serverCmd, serverCmd,
signCmd, signCmd,

15
drone/registry.go Normal file
View file

@ -0,0 +1,15 @@
package main
import "github.com/urfave/cli"
var registryCmd = cli.Command{
Name: "registry",
Usage: "manage registries",
Subcommands: []cli.Command{
registryCreateCmd,
registryDeleteCmd,
registryUpdateCmd,
registryInfoCmd,
registryListCmd,
},
}

61
drone/registry_add.go Normal file
View file

@ -0,0 +1,61 @@
package main
import (
"github.com/drone/drone/model"
"github.com/urfave/cli"
)
var registryCreateCmd = cli.Command{
Name: "add",
Usage: "adds a registry",
Action: registryCreate,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repository",
Usage: "repository name (e.g. octocat/hello-world)",
},
cli.StringFlag{
Name: "hostname",
Usage: "registry hostname",
Value: "index.docker.io",
},
cli.StringFlag{
Name: "username",
Usage: "registry username",
},
cli.StringFlag{
Name: "password",
Usage: "registry password",
},
},
}
func registryCreate(c *cli.Context) error {
var (
hostname = c.String("hostname")
username = c.String("username")
password = c.String("password")
reponame = c.String("repository")
)
if reponame == "" {
reponame = c.Args().First()
}
owner, name, err := parseRepo(reponame)
if err != nil {
return err
}
client, err := newClient(c)
if err != nil {
return err
}
registry := &model.Registry{
Address: hostname,
Username: username,
Password: password,
}
_, err = client.RegistryCreate(owner, name, registry)
if err != nil {
return err
}
return nil
}

59
drone/registry_info.go Normal file
View file

@ -0,0 +1,59 @@
package main
import (
"html/template"
"os"
"github.com/urfave/cli"
)
var registryInfoCmd = cli.Command{
Name: "info",
Usage: "display registry info",
Action: registryInfo,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repository",
Usage: "repository name (e.g. octocat/hello-world)",
},
cli.StringFlag{
Name: "hostname",
Usage: "registry hostname",
Value: "index.docker.io",
},
cli.StringFlag{
Name: "format",
Usage: "repository name (e.g. octocat/hello-world)",
Value: tmplRegistryList,
Hidden: true,
},
},
}
func registryInfo(c *cli.Context) error {
var (
hostname = c.String("hostname")
reponame = c.String("repository")
format = c.String("format") + "\n"
)
if reponame == "" {
reponame = c.Args().First()
}
owner, name, err := parseRepo(reponame)
if err != nil {
return err
}
client, err := newClient(c)
if err != nil {
return err
}
registry, err := client.Registry(owner, name, hostname)
if err != nil {
return err
}
tmpl, err := template.New("_").Parse(format)
if err != nil {
return err
}
return tmpl.Execute(os.Stdout, registry)
}

63
drone/registry_list.go Normal file
View file

@ -0,0 +1,63 @@
package main
import (
"html/template"
"os"
"github.com/urfave/cli"
)
var registryListCmd = cli.Command{
Name: "ls",
Usage: "list regitries",
Action: registryList,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repository",
Usage: "repository name (e.g. octocat/hello-world)",
},
cli.StringFlag{
Name: "format",
Usage: "repository name (e.g. octocat/hello-world)",
Value: tmplRegistryList,
Hidden: true,
},
},
}
func registryList(c *cli.Context) error {
var (
format = c.String("format") + "\n"
reponame = c.String("repository")
)
if reponame == "" {
reponame = c.Args().First()
}
owner, name, err := parseRepo(reponame)
if err != nil {
return err
}
client, err := newClient(c)
if err != nil {
return err
}
list, err := client.RegistryList(owner, name)
if err != nil {
return err
}
tmpl, err := template.New("_").Parse(format)
if err != nil {
return err
}
for _, registry := range list {
tmpl.Execute(os.Stdout, registry)
}
return nil
}
// template for build list information
var tmplRegistryList = "\x1b[33m{{ .Address }} \x1b[0m" + `
Username: {{ .Username }}
Password: ********
Email: {{ .Email }}
`

39
drone/registry_rm.go Normal file
View file

@ -0,0 +1,39 @@
package main
import "github.com/urfave/cli"
var registryDeleteCmd = cli.Command{
Name: "rm",
Usage: "remove a registry",
Action: registryDelete,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repository",
Usage: "repository name (e.g. octocat/hello-world)",
},
cli.StringFlag{
Name: "hostname",
Usage: "registry hostname",
Value: "index.docker.io",
},
},
}
func registryDelete(c *cli.Context) error {
var (
hostname = c.String("hostname")
reponame = c.String("repository")
)
if reponame == "" {
reponame = c.Args().First()
}
owner, name, err := parseRepo(reponame)
if err != nil {
return err
}
client, err := newClient(c)
if err != nil {
return err
}
return client.RegistryDelete(owner, name, hostname)
}

61
drone/registry_set.go Normal file
View file

@ -0,0 +1,61 @@
package main
import (
"github.com/drone/drone/model"
"github.com/urfave/cli"
)
var registryUpdateCmd = cli.Command{
Name: "update",
Usage: "update a registry",
Action: registryUpdate,
Flags: []cli.Flag{
cli.StringFlag{
Name: "repository",
Usage: "repository name (e.g. octocat/hello-world)",
},
cli.StringFlag{
Name: "hostname",
Usage: "registry hostname",
Value: "index.docker.io",
},
cli.StringFlag{
Name: "username",
Usage: "registry username",
},
cli.StringFlag{
Name: "password",
Usage: "registry password",
},
},
}
func registryUpdate(c *cli.Context) error {
var (
hostname = c.String("hostname")
username = c.String("username")
password = c.String("password")
reponame = c.String("repository")
)
if reponame == "" {
reponame = c.Args().First()
}
owner, name, err := parseRepo(reponame)
if err != nil {
return err
}
client, err := newClient(c)
if err != nil {
return err
}
registry := &model.Registry{
Address: hostname,
Username: username,
Password: password,
}
_, err = client.RegistryUpdate(owner, name, registry)
if err != nil {
return err
}
return nil
}

23
model/limit.go Normal file
View file

@ -0,0 +1,23 @@
package model
// Limiter defines an interface for limiting repository creation.
// This could be used, for example, to limit repository creation to
// a specific organization or a specific set of users.
type Limiter interface {
LimitUser(*User) error
LimitRepo(*User, *Repo) error
LimitBuild(*User, *Repo, *Build) error
}
// NoLimit impliments the Limiter interface without enforcing any
// actual limits. All limiting functions are no-ops.
type NoLimit struct{}
// LimitUser is a no-op for limiting user creation.
func (NoLimit) LimitUser(*User) error { return nil }
// LimitRepo is a no-op for limiting repo creation.
func (NoLimit) LimitRepo(*User, *Repo) error { return nil }
// LimitBuild is a no-op for limiting build creation.
func (NoLimit) LimitBuild(*User, *Repo, *Build) error { return nil }

View file

@ -1,15 +1,56 @@
package model package model
import "errors"
var (
errRegistryAddressInvalid = errors.New("Invalid Registry Address")
errRegistryUsernameInvalid = errors.New("Invalid Registry Username")
errRegistryPasswordInvalid = errors.New("Invalid Registry Password")
)
// RegistryStore persists registry information to storage.
type RegistryStore interface {
RegistryFind(*Repo, string) (*Registry, error)
RegistryList(*Repo) ([]*Registry, error)
RegistryCreate(*Registry) error
RegistryUpdate(*Registry) error
RegistryDelete(*Registry) error
}
// Registry represents a docker registry with credentials.
// swagger:model registry
type Registry struct { type Registry struct {
ID int64 `json:"id" meddler:"registry_id,pk"` ID int64 `json:"id" meddler:"registry_id,pk"`
RepoID int64 `json:"-" meddler:"registry_repo_id"` RepoID int64 `json:"-" meddler:"registry_repo_id"`
Addr string `json:"addr" meddler:"registry_addr"` Address string `json:"address" meddler:"registry_addr"`
Username string `json:"username" meddler:"registry_username"` Username string `json:"username" meddler:"registry_username"`
Password string `json:"password" meddler:"registry_password"` Password string `json:"password" meddler:"registry_password"`
Email string `json:"email" meddler:"registry_email"` Email string `json:"email" meddler:"registry_email"`
Token string `json:"token" meddler:"registry_token"` Token string `json:"token" meddler:"registry_token"`
} }
// Validate validates the registry information.
func (r *Registry) Validate() error { func (r *Registry) Validate() error {
return nil switch {
case len(r.Address) == 0:
return errRegistryAddressInvalid
case len(r.Username) == 0:
return errRegistryUsernameInvalid
case len(r.Password) == 0:
return errRegistryPasswordInvalid
default:
return nil
}
}
// Copy makes a copy of the registry without the password.
func (r *Registry) Copy() *Registry {
return &Registry{
ID: r.ID,
RepoID: r.RepoID,
Address: r.Address,
Username: r.Username,
Email: r.Email,
Token: r.Token,
}
} }

View file

@ -111,6 +111,13 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
repo.POST("/secrets", session.MustPush, server.PostSecret) repo.POST("/secrets", session.MustPush, server.PostSecret)
repo.DELETE("/secrets/:secret", session.MustPush, server.DeleteSecret) repo.DELETE("/secrets/:secret", session.MustPush, server.DeleteSecret)
// requires push permissions
repo.GET("/registry", session.MustPush, server.GetRegistryList)
repo.POST("/registry", session.MustPush, server.PostRegistry)
repo.GET("/registry/:address", session.MustPush, server.GetRegistry)
repo.PATCH("/registry/:address", session.MustPush, server.PatchRegistry)
repo.DELETE("/registry/:address", session.MustPush, server.DeleteRegistry)
// requires push permissions // requires push permissions
repo.PATCH("", session.MustPush, server.PatchRepo) repo.PATCH("", session.MustPush, server.PatchRepo)
repo.DELETE("", session.MustRepoAdmin(), server.DeleteRepo) repo.DELETE("", session.MustRepoAdmin(), server.DeleteRepo)

133
server/registry.go Normal file
View file

@ -0,0 +1,133 @@
package server
import (
"net/http"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
// GetRegistry gets the name registry from the database and writes
// to the response in json format.
func GetRegistry(c *gin.Context) {
var (
repo = session.Repo(c)
name = c.Param("registry")
)
registry, err := store.FromContext(c).RegistryFind(repo, name)
if err != nil {
c.String(404, "Error getting registry %q. %s", name, err)
return
}
c.JSON(200, registry.Copy())
}
// PostRegistry persists the registry to the database.
func PostRegistry(c *gin.Context) {
repo := session.Repo(c)
in := new(model.Registry)
if err := c.Bind(in); err != nil {
c.String(http.StatusBadRequest, "Error parsing request. %s", err)
return
}
registry := &model.Registry{
RepoID: repo.ID,
Address: in.Address,
Username: in.Username,
Password: in.Password,
Token: in.Token,
Email: in.Email,
}
if err := registry.Validate(); err != nil {
c.String(400, "Error inserting registry. %s", err)
return
}
if err := store.FromContext(c).RegistryCreate(registry); err != nil {
c.String(500, "Error inserting registry %q. %s", in.Address, err)
return
}
c.JSON(200, in.Copy())
}
// PatchRegistry updates the registry in the database.
func PatchRegistry(c *gin.Context) {
var (
repo = session.Repo(c)
name = c.Param("registry")
)
in := new(model.Registry)
err := c.Bind(in)
if err != nil {
c.String(http.StatusBadRequest, "Error parsing request. %s", err)
return
}
registry, err := store.FromContext(c).RegistryFind(repo, name)
if err != nil {
c.String(404, "Error getting registry %q. %s", name, err)
return
}
if in.Username != "" {
registry.Username = in.Username
}
if in.Password != "" {
registry.Password = in.Password
}
if in.Token != "" {
registry.Token = in.Token
}
if in.Email != "" {
registry.Email = in.Email
}
if err := registry.Validate(); err != nil {
c.String(400, "Error updating registry. %s", err)
return
}
if err := store.FromContext(c).RegistryUpdate(registry); err != nil {
c.String(500, "Error updating registry %q. %s", in.Address, err)
return
}
c.JSON(200, in.Copy())
}
// GetRegistryList gets the registry list from the database and writes
// to the response in json format.
func GetRegistryList(c *gin.Context) {
repo := session.Repo(c)
list, err := store.FromContext(c).RegistryList(repo)
if err != nil {
c.String(500, "Error getting registry list. %s", err)
return
}
// copy the registry detail to remove the sensitive
// password and token fields.
for i, registry := range list {
list[i] = registry.Copy()
}
c.JSON(200, list)
}
// DeleteRegistry deletes the named registry from the database.
func DeleteRegistry(c *gin.Context) {
var (
repo = session.Repo(c)
name = c.Param("registry")
)
registry, err := store.FromContext(c).RegistryFind(repo, name)
if err != nil {
c.String(404, "Error getting registry %q. %s", name, err)
return
}
err = store.FromContext(c).RegistryDelete(registry)
if err != nil {
c.String(500, "Error deleting registry %q. %s", name, err)
return
}
c.String(204, "")
}

View file

@ -1,56 +0,0 @@
package datastore
import (
"github.com/drone/drone/model"
"github.com/russross/meddler"
)
func (db *datastore) GetAgent(id int64) (*model.Agent, error) {
var agent = new(model.Agent)
var err = meddler.Load(db, agentTable, agent, id)
return agent, err
}
func (db *datastore) GetAgentAddr(addr string) (*model.Agent, error) {
var agent = new(model.Agent)
var err = meddler.QueryRow(db, agent, rebind(agentAddrQuery), addr)
return agent, err
}
func (db *datastore) GetAgentList() ([]*model.Agent, error) {
var agents = []*model.Agent{}
var err = meddler.QueryAll(db, &agents, rebind(agentListQuery))
return agents, err
}
func (db *datastore) CreateAgent(agent *model.Agent) error {
return meddler.Insert(db, agentTable, agent)
}
func (db *datastore) UpdateAgent(agent *model.Agent) error {
return meddler.Update(db, agentTable, agent)
}
func (db *datastore) DeleteAgent(agent *model.Agent) error {
var _, err = db.Exec(rebind(agentDeleteStmt), agent.ID)
return err
}
const agentTable = "agents"
const agentAddrQuery = `
SELECT *
FROM agents
WHERE agent_addr=?
LIMIT 1
`
const agentListQuery = `
SELECT *
FROM agents
ORDER BY agent_addr ASC
`
const agentDeleteStmt = `
DELETE FROM agents WHERE agent_id = ?
`

View file

@ -1,126 +0,0 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
"github.com/franela/goblin"
)
func TestAgents(t *testing.T) {
db := openTest()
defer db.Close()
s := From(db)
g := goblin.Goblin(t)
g.Describe("Agents", func() {
// before each test be sure to purge the package
// table data from the database.
g.BeforeEach(func() {
db.Exec("DELETE FROM agents")
})
g.It("Should update", func() {
agent := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
err1 := s.CreateAgent(&agent)
agent.Platform = "windows/amd64"
err2 := s.UpdateAgent(&agent)
getagent, err3 := s.GetAgent(agent.ID)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue()
g.Assert(agent.ID).Equal(getagent.ID)
g.Assert(agent.Platform).Equal(getagent.Platform)
})
g.It("Should create", func() {
agent := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
err := s.CreateAgent(&agent)
g.Assert(err == nil).IsTrue()
g.Assert(agent.ID != 0).IsTrue()
})
g.It("Should get by ID", func() {
agent := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
s.CreateAgent(&agent)
getagent, err := s.GetAgent(agent.ID)
g.Assert(err == nil).IsTrue()
g.Assert(agent.ID).Equal(getagent.ID)
g.Assert(agent.Address).Equal(getagent.Address)
g.Assert(agent.Platform).Equal(getagent.Platform)
})
g.It("Should get by IP address", func() {
agent := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
s.CreateAgent(&agent)
getagent, err := s.GetAgentAddr(agent.Address)
g.Assert(err == nil).IsTrue()
g.Assert(agent.ID).Equal(getagent.ID)
g.Assert(agent.Address).Equal(getagent.Address)
g.Assert(agent.Platform).Equal(getagent.Platform)
})
g.It("Should enforce unique IP address", func() {
agent1 := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
agent2 := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
err1 := s.CreateAgent(&agent1)
err2 := s.CreateAgent(&agent2)
g.Assert(err1 == nil).IsTrue()
g.Assert(err2 == nil).IsFalse()
})
g.It("Should list", func() {
agent1 := model.Agent{
Address: "127.0.0.1",
Platform: "linux/amd64",
}
agent2 := model.Agent{
Address: "localhost",
Platform: "linux/amd64",
}
s.CreateAgent(&agent1)
s.CreateAgent(&agent2)
agents, err := s.GetAgentList()
g.Assert(err == nil).IsTrue()
g.Assert(len(agents)).Equal(2)
g.Assert(agents[0].Address).Equal(agent1.Address)
g.Assert(agents[0].Platform).Equal(agent1.Platform)
})
// g.It("Should delete", func() {
// user := model.User{
// Login: "joe",
// Email: "foo@bar.com",
// Token: "e42080dddf012c718e476da161d21ad5",
// }
// s.CreateUser(&user)
// _, err1 := s.GetUser(user.ID)
// err2 := s.DeleteUser(&user)
// _, err3 := s.GetUser(user.ID)
// g.Assert(err1 == nil).IsTrue()
// g.Assert(err2 == nil).IsTrue()
// g.Assert(err3 == nil).IsFalse()
// })
})
}

View file

@ -1,50 +0,0 @@
package datastore
//
// import (
// "github.com/drone/drone/model"
// "github.com/russross/meddler"
// )
//
// func (db *datastore) GetJob(id int64) (*model.Job, error) {
// var job = new(model.Job)
// var err = meddler.Load(db, jobTable, job, id)
// return job, err
// }
//
// func (db *datastore) GetJobNumber(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 *datastore) GetJobList(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 *datastore) CreateJob(job *model.Job) error {
// return meddler.Insert(db, jobTable, job)
// }
//
// func (db *datastore) UpdateJob(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

@ -1,119 +0,0 @@
package datastore
//
// import (
// "testing"
//
// "github.com/drone/drone/model"
// "github.com/franela/goblin"
// )
//
// func TestJobs(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.CreateJob(job)
// g.Assert(err1 == nil).IsTrue()
// g.Assert(job.ID != 0).IsTrue()
//
// job.Status = "started"
// err2 := s.UpdateJob(job)
// g.Assert(err2 == nil).IsTrue()
//
// getjob, err3 := s.GetJob(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.CreateJob(job)
// g.Assert(err1 == nil).IsTrue()
// g.Assert(job.ID != 0).IsTrue()
//
// getjob, err2 := s.GetJob(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.CreateJob(job)
// g.Assert(err1 == nil).IsTrue()
// g.Assert(job.ID != 0).IsTrue()
//
// getjob, err2 := s.GetJobNumber(&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{
// {
// BuildID: 1,
// Status: "success",
// ExitCode: 0,
// Number: 1,
// },
// {
// BuildID: 3,
// Status: "error",
// ExitCode: 1,
// Number: 2,
// },
// {
// BuildID: 5,
// Status: "pending",
// ExitCode: 0,
// Number: 3,
// },
// }
//
// err1 := s.CreateBuild(&build, jobs...)
// g.Assert(err1 == nil).IsTrue()
// getjobs, err2 := s.GetJobList(&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)
// })
// })
// }

View file

@ -0,0 +1,35 @@
package datastore
import (
"github.com/drone/drone/model"
"github.com/drone/drone/store/datastore/sql"
"github.com/russross/meddler"
)
func (db *datastore) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) {
stmt := sql.Lookup(db.driver, "registry-find-repo-addr")
data := new(model.Registry)
err := meddler.QueryRow(db, data, stmt, repo.ID, addr)
return data, err
}
func (db *datastore) RegistryList(repo *model.Repo) ([]*model.Registry, error) {
stmt := sql.Lookup(db.driver, "registry-find-repo")
data := []*model.Registry{}
err := meddler.QueryAll(db, &data, stmt, repo.ID)
return data, err
}
func (db *datastore) RegistryCreate(registry *model.Registry) error {
return meddler.Insert(db, "registry", registry)
}
func (db *datastore) RegistryUpdate(registry *model.Registry) error {
return meddler.Update(db, "registry", registry)
}
func (db *datastore) RegistryDelete(registry *model.Registry) error {
stmt := sql.Lookup(db.driver, "registry-delete")
_, err := db.Exec(stmt, registry.ID)
return err
}

View file

@ -0,0 +1,142 @@
package datastore
import (
"testing"
"github.com/drone/drone/model"
)
func TestRegistryFind(t *testing.T) {
s := newTest()
defer func() {
s.Exec("delete from registry")
s.Close()
}()
err := s.RegistryCreate(&model.Registry{
RepoID: 1,
Address: "index.docker.io",
Username: "foo",
Password: "bar",
Email: "foo@bar.com",
Token: "12345",
})
if err != nil {
t.Errorf("Unexpected error: insert registry: %s", err)
return
}
registry, err := s.RegistryFind(&model.Repo{ID: 1}, "index.docker.io")
if err != nil {
t.Error(err)
return
}
if got, want := registry.RepoID, int64(1); got != want {
t.Errorf("Want repo id %d, got %d", want, got)
}
if got, want := registry.Address, "index.docker.io"; got != want {
t.Errorf("Want registry address %s, got %s", want, got)
}
if got, want := registry.Username, "foo"; got != want {
t.Errorf("Want registry username %s, got %s", want, got)
}
if got, want := registry.Password, "bar"; got != want {
t.Errorf("Want registry password %s, got %s", want, got)
}
if got, want := registry.Email, "foo@bar.com"; got != want {
t.Errorf("Want registry email %s, got %s", want, got)
}
if got, want := registry.Token, "12345"; got != want {
t.Errorf("Want registry token %s, got %s", want, got)
}
}
func TestRegistryList(t *testing.T) {
s := newTest()
defer func() {
s.Exec("delete from registry")
s.Close()
}()
s.RegistryCreate(&model.Registry{
RepoID: 1,
Address: "index.docker.io",
Username: "foo",
Password: "bar",
})
s.RegistryCreate(&model.Registry{
RepoID: 1,
Address: "foo.docker.io",
Username: "foo",
Password: "bar",
})
list, err := s.RegistryList(&model.Repo{ID: 1})
if err != nil {
t.Error(err)
return
}
if got, want := len(list), 2; got != want {
t.Errorf("Want %d registries, got %d", want, got)
}
}
func TestRegistryUpdate(t *testing.T) {
s := newTest()
defer func() {
s.Exec("delete from registry")
s.Close()
}()
registry := &model.Registry{
RepoID: 1,
Address: "index.docker.io",
Username: "foo",
Password: "bar",
}
if err := s.RegistryCreate(registry); err != nil {
t.Errorf("Unexpected error: insert registry: %s", err)
return
}
registry.Password = "qux"
if err := s.RegistryUpdate(registry); err != nil {
t.Errorf("Unexpected error: update registry: %s", err)
return
}
updated, err := s.RegistryFind(&model.Repo{ID: 1}, "index.docker.io")
if err != nil {
t.Error(err)
return
}
if got, want := updated.Password, "qux"; got != want {
t.Errorf("Want registry password %s, got %s", want, got)
}
}
func TestRegistryIndexes(t *testing.T) {
s := newTest()
defer func() {
s.Exec("delete from registry")
s.Close()
}()
if err := s.RegistryCreate(&model.Registry{
RepoID: 1,
Address: "index.docker.io",
Username: "foo",
Password: "bar",
}); err != nil {
t.Errorf("Unexpected error: insert registry: %s", err)
return
}
// fail due to duplicate addr
if err := s.RegistryCreate(&model.Registry{
RepoID: 1,
Address: "index.docker.io",
Username: "baz",
Password: "qux",
}); err == nil {
t.Errorf("Unexpected error: dupliate address")
}
}

View file

@ -0,0 +1,34 @@
-- name: registry-find-repo
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = $1
-- name: registry-find-repo-addr
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = $1
AND registry_addr = $2
-- name: registry-delete-repo
DELETE FROM registry WHERE registry_repo_id = $1
-- name: registry-delete
DELETE FROM registry WHERE registry_id = $1

View file

@ -15,6 +15,10 @@ var index = map[string]string{
"procs-find-build-pid": procsFindBuildPid, "procs-find-build-pid": procsFindBuildPid,
"procs-find-build-ppid": procsFindBuildPpid, "procs-find-build-ppid": procsFindBuildPpid,
"procs-delete-build": procsDeleteBuild, "procs-delete-build": procsDeleteBuild,
"registry-find-repo": registryFindRepo,
"registry-find-repo-addr": registryFindRepoAddr,
"registry-delete-repo": registryDeleteRepo,
"registry-delete": registryDelete,
} }
var filesFindBuild = ` var filesFindBuild = `
@ -149,3 +153,38 @@ WHERE proc_build_id = $1
var procsDeleteBuild = ` var procsDeleteBuild = `
DELETE FROM procs WHERE proc_build_id = $1 DELETE FROM procs WHERE proc_build_id = $1
` `
var registryFindRepo = `
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = $1
`
var registryFindRepoAddr = `
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = $1
AND registry_addr = $2
`
var registryDeleteRepo = `
DELETE FROM registry WHERE registry_repo_id = $1
`
var registryDelete = `
DELETE FROM registry WHERE registry_id = $1
`

View file

@ -0,0 +1,34 @@
-- name: registry-find-repo
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = ?
-- name: registry-find-repo-addr
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = ?
AND registry_addr = ?
-- name: registry-delete-repo
DELETE FROM registry WHERE registry_repo_id = ?
-- name: registry-delete
DELETE FROM registry WHERE registry_id = ?

View file

@ -15,6 +15,10 @@ var index = map[string]string{
"procs-find-build-pid": procsFindBuildPid, "procs-find-build-pid": procsFindBuildPid,
"procs-find-build-ppid": procsFindBuildPpid, "procs-find-build-ppid": procsFindBuildPpid,
"procs-delete-build": procsDeleteBuild, "procs-delete-build": procsDeleteBuild,
"registry-find-repo": registryFindRepo,
"registry-find-repo-addr": registryFindRepoAddr,
"registry-delete-repo": registryDeleteRepo,
"registry-delete": registryDelete,
} }
var filesFindBuild = ` var filesFindBuild = `
@ -149,3 +153,38 @@ WHERE proc_build_id = ?
var procsDeleteBuild = ` var procsDeleteBuild = `
DELETE FROM procs WHERE proc_build_id = ? DELETE FROM procs WHERE proc_build_id = ?
` `
var registryFindRepo = `
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = ?
`
var registryFindRepoAddr = `
SELECT
registry_id
,registry_repo_id
,registry_addr
,registry_username
,registry_password
,registry_email
,registry_token
FROM registry
WHERE registry_repo_id = ?
AND registry_addr = ?
`
var registryDeleteRepo = `
DELETE FROM registry WHERE registry_repo_id = ?
`
var registryDelete = `
DELETE FROM registry WHERE registry_id = ?
`

View file

@ -133,17 +133,23 @@ type Store interface {
// // WriteLog writes the job logs to the datastore. // // WriteLog writes the job logs to the datastore.
// WriteLog(*model.Job, io.Reader) error // WriteLog(*model.Job, io.Reader) error
GetAgent(int64) (*model.Agent, error) // GetAgent(int64) (*model.Agent, error)
//
// GetAgentAddr(string) (*model.Agent, error)
//
// GetAgentList() ([]*model.Agent, error)
//
// CreateAgent(*model.Agent) error
//
// UpdateAgent(*model.Agent) error
//
// DeleteAgent(*model.Agent) error
GetAgentAddr(string) (*model.Agent, error) RegistryFind(*model.Repo, string) (*model.Registry, error)
RegistryList(*model.Repo) ([]*model.Registry, error)
GetAgentList() ([]*model.Agent, error) RegistryCreate(*model.Registry) error
RegistryUpdate(*model.Registry) error
CreateAgent(*model.Agent) error RegistryDelete(*model.Registry) error
UpdateAgent(*model.Agent) error
DeleteAgent(*model.Agent) error
ProcLoad(int64) (*model.Proc, error) ProcLoad(int64) (*model.Proc, error)
ProcFind(*model.Build, int) (*model.Proc, error) ProcFind(*model.Build, int) (*model.Proc, error)
@ -388,26 +394,26 @@ func UpdateBuild(c context.Context, build *model.Build) error {
// return FromContext(c).WriteLog(job, r) // return FromContext(c).WriteLog(job, r)
// } // }
func GetAgent(c context.Context, id int64) (*model.Agent, error) { // func GetAgent(c context.Context, id int64) (*model.Agent, error) {
return FromContext(c).GetAgent(id) // return FromContext(c).GetAgent(id)
} // }
//
func GetAgentAddr(c context.Context, addr string) (*model.Agent, error) { // func GetAgentAddr(c context.Context, addr string) (*model.Agent, error) {
return FromContext(c).GetAgentAddr(addr) // return FromContext(c).GetAgentAddr(addr)
} // }
//
func GetAgentList(c context.Context) ([]*model.Agent, error) { // func GetAgentList(c context.Context) ([]*model.Agent, error) {
return FromContext(c).GetAgentList() // return FromContext(c).GetAgentList()
} // }
//
func CreateAgent(c context.Context, agent *model.Agent) error { // func CreateAgent(c context.Context, agent *model.Agent) error {
return FromContext(c).CreateAgent(agent) // return FromContext(c).CreateAgent(agent)
} // }
//
func UpdateAgent(c context.Context, agent *model.Agent) error { // func UpdateAgent(c context.Context, agent *model.Agent) error {
return FromContext(c).UpdateAgent(agent) // return FromContext(c).UpdateAgent(agent)
} // }
//
func DeleteAgent(c context.Context, agent *model.Agent) error { // func DeleteAgent(c context.Context, agent *model.Agent) error {
return FromContext(c).DeleteAgent(agent) // return FromContext(c).DeleteAgent(agent)
} // }