diff --git a/model/log.go b/model/log.go deleted file mode 100644 index b8886fd96..000000000 --- a/model/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package model - -// type Log struct { -// ID int64 `meddler:"log_id,pk"` -// JobID int64 `meddler:"log_job_id"` -// Data []byte `meddler:"log_data"` -// } diff --git a/router/router.go b/router/router.go index c6fcc12c3..366c2e3db 100644 --- a/router/router.go +++ b/router/router.go @@ -104,7 +104,7 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { repo.GET("", server.GetRepo) repo.GET("/builds", server.GetBuilds) repo.GET("/builds/:number", server.GetBuild) - repo.GET("/logs/:number/:job", server.GetBuildLogs) + repo.GET("/logs/:number/:ppid/:proc", server.GetBuildLogs) repo.POST("/sign", session.MustPush, server.Sign) repo.GET("/secrets", session.MustPush, server.GetSecrets) diff --git a/server/build.go b/server/build.go index c754b81a4..89b34506e 100644 --- a/server/build.go +++ b/server/build.go @@ -77,7 +77,8 @@ func GetBuildLogs(c *gin.Context) { // parse the build number and job sequence number from // the repquest parameter. num, _ := strconv.Atoi(c.Params.ByName("number")) - seq, _ := strconv.Atoi(c.Params.ByName("job")) + ppid, _ := strconv.Atoi(c.Params.ByName("ppid")) + name := c.Params.ByName("proc") build, err := store.GetBuildNumber(c, repo, num) if err != nil { @@ -85,13 +86,13 @@ func GetBuildLogs(c *gin.Context) { return } - proc, err := store.FromContext(c).ProcFind(build, seq) + proc, err := store.FromContext(c).ProcChild(build, ppid, name) if err != nil { c.AbortWithError(404, err) return } - rc, err := store.FromContext(c).FileRead(proc, "logs.json") + rc, err := store.FromContext(c).LogFind(proc) if err != nil { c.AbortWithError(404, err) return diff --git a/server/rpc.go b/server/rpc.go index 9d5271920..0848e8403 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -111,15 +111,21 @@ func (s *RPC) Update(c context.Context, id string, state rpc.State) error { return err } - proc, err := s.store.ProcLoad(procID) + pproc, err := s.store.ProcLoad(procID) if err != nil { - log.Printf("error: rpc.update: cannot find proc with id %d: %s", procID, err) + log.Printf("error: rpc.update: cannot find pproc with id %d: %s", procID, err) return err } - build, err := s.store.GetBuild(proc.BuildID) + build, err := s.store.GetBuild(pproc.BuildID) if err != nil { - log.Printf("error: cannot find build with id %d: %s", proc.BuildID, err) + log.Printf("error: cannot find build with id %d: %s", pproc.BuildID, err) + return err + } + + proc, err := s.store.ProcChild(build, pproc.PID, state.Proc) + if err != nil { + log.Printf("error: cannot find proc with name %s: %s", state.Proc, err) return err } @@ -133,6 +139,10 @@ func (s *RPC) Update(c context.Context, id string, state rpc.State) error { proc.Stopped = state.Finished proc.ExitCode = state.ExitCode proc.Error = state.Error + proc.State = model.StatusSuccess + if state.ExitCode != 0 || state.Error != "" { + proc.State = model.StatusFailure + } } else { proc.Started = state.Started proc.State = model.StatusRunning @@ -165,12 +175,31 @@ func (s *RPC) Upload(c context.Context, id string, file *rpc.File) error { return err } - proc, err := s.store.ProcLoad(procID) + pproc, err := s.store.ProcLoad(procID) if err != nil { - log.Printf("error: cannot find proc with id %d: %s", procID, err) + log.Printf("error: cannot find parent proc with id %d: %s", procID, err) return err } + build, err := s.store.GetBuild(pproc.BuildID) + if err != nil { + log.Printf("error: cannot find build with id %d: %s", pproc.BuildID, err) + return err + } + + proc, err := s.store.ProcChild(build, pproc.PID, file.Proc) + if err != nil { + log.Printf("error: cannot find child proc with name %s: %s", file.Proc, err) + return err + } + + if file.Mime == "application/json+logs" { + return s.store.LogSave( + proc, + bytes.NewBuffer(file.Data), + ) + } + return s.store.FileCreate(&model.File{ BuildID: proc.BuildID, ProcID: proc.ID, @@ -261,19 +290,13 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { return err } - if build.Status == model.StatusPending { - build.Status = model.StatusRunning - build.Started = state.Started - if err := s.store.UpdateBuild(build); err != nil { - log.Printf("error: done: cannot update build_id %d state: %s", build.ID, err) - } - } - - proc.Started = state.Started - proc.State = model.StatusRunning proc.Stopped = state.Finished proc.Error = state.Error proc.ExitCode = state.ExitCode + proc.State = model.StatusSuccess + if proc.ExitCode != 0 || proc.Error != "" { + proc.State = model.StatusFailure + } if err := s.store.ProcUpdate(proc); err != nil { log.Printf("error: done: cannot update proc_id %d state: %s", procID, err) } @@ -287,7 +310,7 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { // TODO handle this error procs, _ := s.store.ProcList(build) for _, p := range procs { - if !proc.Running() && p.PPID == proc.PID { + if p.Running() && p.PPID == proc.PID { p.State = model.StatusSkipped if p.Started != 0 { p.State = model.StatusKilled @@ -297,12 +320,11 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { log.Printf("error: done: cannot update proc_id %d child state: %s", p.ID, err) } } - if !proc.Running() && p.PPID == 0 { + if !p.Running() && p.PPID == 0 { done = true if p.Failing() { status = model.StatusFailure } - continue } } if done { diff --git a/store/datastore/logs.go b/store/datastore/logs.go index 8957affe1..b39ad3b6a 100644 --- a/store/datastore/logs.go +++ b/store/datastore/logs.go @@ -1,37 +1,42 @@ package datastore -// -// import ( -// "bytes" -// "io" -// "io/ioutil" -// -// "github.com/drone/drone/model" -// "github.com/russross/meddler" -// ) -// -// func (db *datastore) ReadLog(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 *datastore) WriteLog(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 -// ` +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/drone/drone/model" + "github.com/russross/meddler" +) + +func (db *datastore) LogFind(proc *model.Proc) (io.ReadCloser, error) { + var log = new(logData) + var err = meddler.QueryRow(db, log, rebind(logQuery), proc.ID) + var buf = bytes.NewBuffer(log.Data) + return ioutil.NopCloser(buf), err +} + +func (db *datastore) LogSave(proc *model.Proc, r io.Reader) error { + var log = new(logData) + var err = meddler.QueryRow(db, log, rebind(logQuery), proc.ID) + if err != nil { + log = &logData{ProcID: proc.ID} + } + log.Data, _ = ioutil.ReadAll(r) + return meddler.Save(db, logTable, log) +} + +type logData struct { + ID int64 `meddler:"log_id,pk"` + ProcID int64 `meddler:"log_job_id"` + Data []byte `meddler:"log_data"` +} + +const logTable = "logs" + +const logQuery = ` +SELECT * +FROM logs +WHERE log_job_id=? +LIMIT 1 +` diff --git a/store/datastore/logs_test.go b/store/datastore/logs_test.go index 5ea31bf76..cd57b762d 100644 --- a/store/datastore/logs_test.go +++ b/store/datastore/logs_test.go @@ -1,61 +1,60 @@ package datastore -// -// import ( -// "bytes" -// "io/ioutil" -// "testing" -// -// "github.com/drone/drone/model" -// "github.com/franela/goblin" -// ) -// -// func TestLogs(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.WriteLog(&job, buf) -// g.Assert(err == nil).IsTrue() -// -// rc, err := s.ReadLog(&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.WriteLog(&job, buf1) -// err2 := s.WriteLog(&job, buf2) -// g.Assert(err1 == nil).IsTrue() -// g.Assert(err2 == nil).IsTrue() -// -// rc, err := s.ReadLog(&job) -// g.Assert(err == nil).IsTrue() -// defer rc.Close() -// out, _ := ioutil.ReadAll(rc) -// g.Assert(string(out)).Equal("echo allo?") -// }) -// -// }) -// } +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/drone/drone/model" + "github.com/franela/goblin" +) + +func TestLogs(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() { + proc := model.Proc{ + ID: 1, + } + buf := bytes.NewBufferString("echo hi") + err := s.LogSave(&proc, buf) + g.Assert(err == nil).IsTrue() + + rc, err := s.LogFind(&proc) + 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() { + proc := model.Proc{ + ID: 1, + } + buf1 := bytes.NewBufferString("echo hi") + buf2 := bytes.NewBufferString("echo allo?") + err1 := s.LogSave(&proc, buf1) + err2 := s.LogSave(&proc, buf2) + g.Assert(err1 == nil).IsTrue() + g.Assert(err2 == nil).IsTrue() + + rc, err := s.LogFind(&proc) + g.Assert(err == nil).IsTrue() + defer rc.Close() + out, _ := ioutil.ReadAll(rc) + g.Assert(string(out)).Equal("echo allo?") + }) + + }) +} diff --git a/store/store.go b/store/store.go index 6e28da6bc..1dfa9935c 100644 --- a/store/store.go +++ b/store/store.go @@ -152,6 +152,9 @@ type Store interface { ProcCreate([]*model.Proc) error ProcUpdate(*model.Proc) error + LogFind(*model.Proc) (io.ReadCloser, error) + LogSave(*model.Proc, io.Reader) error + FileList(*model.Build) ([]*model.File, error) FileFind(*model.Proc, string) (*model.File, error) FileRead(*model.Proc, string) (io.ReadCloser, error)