1package tasks
2
3import (
4	"net/http"
5	"strconv"
6	"time"
7
8	log "github.com/Sirupsen/logrus"
9	"github.com/ansible-semaphore/semaphore/db"
10
11	"github.com/ansible-semaphore/semaphore/util"
12	"github.com/gorilla/context"
13	"github.com/masterminds/squirrel"
14)
15
16// AddTask inserts a task into the database and returns a header or returns error
17func AddTask(w http.ResponseWriter, r *http.Request) {
18	project := context.Get(r, "project").(db.Project)
19	user := context.Get(r, "user").(*db.User)
20
21	var taskObj db.Task
22	if err := util.Bind(w, r, &taskObj); err != nil {
23		return
24	}
25
26	taskObj.Created = time.Now()
27	taskObj.Status = "waiting"
28	taskObj.UserID = &user.ID
29
30	if err := db.Mysql.Insert(&taskObj); err != nil {
31		util.LogErrorWithFields(err, log.Fields{"error": "Bad request. Cannot create new task"})
32		w.WriteHeader(http.StatusBadRequest)
33		return
34	}
35
36	pool.register <- &task{
37		task:      taskObj,
38		projectID: project.ID,
39	}
40
41	objType := taskTypeID
42	desc := "Task ID " + strconv.Itoa(taskObj.ID) + " queued for running"
43	if err := (db.Event{
44		ProjectID:   &project.ID,
45		ObjectType:  &objType,
46		ObjectID:    &taskObj.ID,
47		Description: &desc,
48	}.Insert()); err != nil {
49		util.LogErrorWithFields(err, log.Fields{"error": "Cannot write new event to database"})
50	}
51
52	util.WriteJSON(w, http.StatusCreated, taskObj)
53}
54
55// GetTasksList returns a list of tasks for the current project in desc order to limit or error
56func GetTasksList(w http.ResponseWriter, r *http.Request, limit uint64) {
57	project := context.Get(r, "project").(db.Project)
58
59	q := squirrel.Select("task.*, tpl.playbook as tpl_playbook, user.name as user_name, tpl.alias as tpl_alias").
60		From("task").
61		Join("project__template as tpl on task.template_id=tpl.id").
62		LeftJoin("user on task.user_id=user.id");
63
64	if tpl := context.Get(r, "template"); tpl != nil {
65		q = q.Where("tpl.project_id=? AND task.template_id=?", project.ID, tpl.(db.Template).ID)
66	} else {
67		q = q.Where("tpl.project_id=?", project.ID)
68	}
69
70	q = q.OrderBy("task.created desc, id desc")
71
72	if limit > 0 {
73		q = q.Limit(limit)
74	}
75
76	query, args, _ := q.ToSql()
77
78	var tasks []struct {
79		db.Task
80
81		TemplatePlaybook string  `db:"tpl_playbook" json:"tpl_playbook"`
82		TemplateAlias    string  `db:"tpl_alias" json:"tpl_alias"`
83		UserName         *string `db:"user_name" json:"user_name"`
84	}
85	if _, err := db.Mysql.Select(&tasks, query, args...); err != nil {
86		util.LogErrorWithFields(err, log.Fields{"error": "Bad request. Cannot get tasks list from database"})
87		w.WriteHeader(http.StatusBadRequest)
88		return
89	}
90
91	util.WriteJSON(w, http.StatusOK, tasks)
92}
93
94// GetAllTasks returns all tasks for the current project
95func GetAllTasks(w http.ResponseWriter, r *http.Request) {
96	GetTasksList(w, r, 0)
97}
98
99// GetLastTasks returns the hundred most recent tasks
100func GetLastTasks(w http.ResponseWriter, r *http.Request) {
101	GetTasksList(w, r, 200)
102}
103
104// GetTask returns a task based on its id
105func GetTask(w http.ResponseWriter, r *http.Request) {
106	task := context.Get(r, taskTypeID).(db.Task)
107	util.WriteJSON(w, http.StatusOK, task)
108}
109
110// GetTaskMiddleware is middleware that gets a task by id and sets the context to it or panics
111func GetTaskMiddleware(next http.Handler) http.Handler {
112	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
113		taskID, err := util.GetIntParam("task_id", w, r)
114		if err != nil {
115			panic(err)
116		}
117
118		var task db.Task
119		if err := db.Mysql.SelectOne(&task, "select * from task where id=?", taskID); err != nil {
120			panic(err)
121		}
122
123		context.Set(r, taskTypeID, task)
124		next.ServeHTTP(w, r)
125	})
126}
127
128// GetTaskOutput returns the logged task output by id and writes it as json or returns error
129func GetTaskOutput(w http.ResponseWriter, r *http.Request) {
130	task := context.Get(r, taskTypeID).(db.Task)
131
132	var output []db.TaskOutput
133	if _, err := db.Mysql.Select(&output, "select task_id, task, time, output from task__output where task_id=? order by time asc", task.ID); err != nil {
134		util.LogErrorWithFields(err, log.Fields{"error": "Bad request. Cannot get task output from database"})
135		w.WriteHeader(http.StatusBadRequest)
136		return
137	}
138
139	util.WriteJSON(w, http.StatusOK, output)
140}
141
142// RemoveTask removes a task from the database
143func RemoveTask(w http.ResponseWriter, r *http.Request) {
144	task := context.Get(r, taskTypeID).(db.Task)
145	editor := context.Get(r, "user").(*db.User)
146
147	if !editor.Admin {
148		log.Warn(editor.Username + " is not permitted to delete task logs")
149		w.WriteHeader(http.StatusUnauthorized)
150		return
151	}
152
153	statements := []string{
154		"delete from task__output where task_id=?",
155		"delete from task where id=?",
156	}
157
158	for _, statement := range statements {
159		_, err := db.Mysql.Exec(statement, task.ID)
160		if err != nil {
161			util.LogErrorWithFields(err, log.Fields{"error": "Bad request. Cannot delete task from database"})
162			w.WriteHeader(http.StatusBadRequest)
163			return
164		}
165	}
166
167	w.WriteHeader(http.StatusNoContent)
168}
169