2017-10-29 22:06:41 +00:00
|
|
|
package db
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/juju/errors"
|
|
|
|
|
|
|
|
"github.com/localhots/cmdui/backend/commands"
|
|
|
|
)
|
|
|
|
|
|
|
|
type JobState string
|
|
|
|
|
|
|
|
const (
|
|
|
|
JobStateNew JobState = "new"
|
|
|
|
JobStateCreated JobState = "created"
|
|
|
|
JobStateStarted JobState = "started"
|
|
|
|
JobStateAborted JobState = "aborted"
|
|
|
|
JobStateFailed JobState = "failed"
|
|
|
|
JobStateFinished JobState = "finished"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Job struct {
|
|
|
|
ID string `json:"id" db:"id"`
|
|
|
|
Command string `json:"command" db:"command"`
|
|
|
|
Args string `json:"args" db:"args"`
|
|
|
|
Flags string `json:"flags" db:"flags"`
|
|
|
|
UserID string `json:"user_id" db:"user_id"`
|
|
|
|
User *User `json:"user" db:"-"`
|
|
|
|
State string `json:"state" db:"state"`
|
|
|
|
CreatedAt *time.Time `json:"created_at" db:"created_at"`
|
|
|
|
StartedAt *time.Time `json:"started_at" db:"started_at"`
|
|
|
|
FinishedAt *time.Time `json:"finished_at" db:"finished_at"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewJob(c commands.Command, u User) Job {
|
|
|
|
return Job{
|
|
|
|
ID: newID(),
|
|
|
|
Command: c.Name,
|
|
|
|
Args: c.Args,
|
|
|
|
Flags: c.FlagsString(),
|
|
|
|
UserID: u.ID,
|
|
|
|
User: &u,
|
|
|
|
State: string(JobStateNew),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func FindAllJobs(p Page) ([]Job, error) {
|
|
|
|
return findJobsWhere(jobsIndexQuery("", p))
|
|
|
|
}
|
|
|
|
|
|
|
|
func FindCommandJobs(name string, p Page) ([]Job, error) {
|
|
|
|
return findJobsWhere(jobsIndexQuery("WHERE command = ?", p), name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func FindUserJobs(id string, p Page) ([]Job, error) {
|
|
|
|
return findJobsWhere(jobsIndexQuery("WHERE user_id = ?", p), id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func jobsIndexQuery(where string, p Page) string {
|
|
|
|
p = p.normalize()
|
|
|
|
return fmt.Sprintf("SELECT * FROM jobs %s ORDER BY created_at DESC LIMIT %d, %d",
|
|
|
|
where, p.Offset, p.Limit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func findJobsWhere(query string, args ...interface{}) ([]Job, error) {
|
|
|
|
var jobs []Job
|
|
|
|
err := db.Select(&jobs, query, args...)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return nil, errors.Annotate(err, "Failed to load Jobs list")
|
|
|
|
}
|
|
|
|
|
|
|
|
userIDs := stringSet{}
|
|
|
|
for _, r := range jobs {
|
|
|
|
userIDs.add(r.UserID)
|
|
|
|
}
|
|
|
|
users, err := FindUsers(userIDs.items()...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "Failed to find users to embed into jobs")
|
|
|
|
}
|
|
|
|
for i, r := range jobs {
|
|
|
|
if u, ok := users[r.UserID]; ok {
|
|
|
|
jobs[i].User = &u
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return jobs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func FindJob(id string) (*Job, error) {
|
|
|
|
var r Job
|
|
|
|
err := db.Get(&r, "SELECT * FROM jobs WHERE id = ?", id)
|
|
|
|
if err != nil {
|
|
|
|
if err != sql.ErrNoRows {
|
|
|
|
return nil, errors.Annotate(err, "Failed to load Job details")
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.UserID != "" {
|
|
|
|
r.User, err = FindUser(r.UserID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Annotate(err, "Failed to find a user to embed into a job")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Job) UpdateState(s JobState) error {
|
|
|
|
r.State = string(s)
|
|
|
|
ts := time.Now().UTC()
|
|
|
|
switch s {
|
|
|
|
case JobStateStarted:
|
|
|
|
r.StartedAt = &ts
|
|
|
|
case JobStateFinished, JobStateAborted, JobStateFailed:
|
|
|
|
r.FinishedAt = &ts
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.Update()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Job) Create() error {
|
|
|
|
ts := time.Now().UTC()
|
|
|
|
r.CreatedAt = &ts
|
|
|
|
r.State = string(JobStateCreated)
|
|
|
|
|
|
|
|
_, err := db.NamedExec(`
|
2017-11-06 23:12:06 +00:00
|
|
|
INSERT INTO jobs (id, command, args, flags, user_id, state, created_at)
|
|
|
|
VALUES (:id, :command, :args, :flags, :user_id, :state, :created_at)
|
|
|
|
`, r)
|
2017-10-29 22:06:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Annotate(err, "Failed to create a job")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r Job) Update() error {
|
|
|
|
_, err := db.NamedExec(`
|
|
|
|
UPDATE jobs
|
|
|
|
SET
|
|
|
|
state = :state,
|
|
|
|
started_at = :started_at,
|
|
|
|
finished_at = :finished_at
|
|
|
|
WHERE
|
|
|
|
id = :id
|
|
|
|
`, r)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Annotate(err, "Failed to update a job")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jobsSliceToMap(s []Job) map[string]Job {
|
|
|
|
m := make(map[string]Job, len(s))
|
|
|
|
for _, r := range s {
|
|
|
|
m[r.ID] = r
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|