2023-08-29 00:51:04 +02:00
|
|
|
package tasks
|
|
|
|
|
|
|
|
import (
|
2023-09-16 23:47:06 +02:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2023-08-29 00:51:04 +02:00
|
|
|
"fmt"
|
|
|
|
"github.com/ansible-semaphore/semaphore/db"
|
2023-09-23 17:12:35 +02:00
|
|
|
"github.com/ansible-semaphore/semaphore/db_lib"
|
2023-08-29 00:51:04 +02:00
|
|
|
"github.com/ansible-semaphore/semaphore/lib"
|
2023-12-25 00:17:12 +01:00
|
|
|
"github.com/ansible-semaphore/semaphore/util"
|
2023-09-16 23:47:06 +02:00
|
|
|
"net/http"
|
2023-08-29 00:51:04 +02:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RemoteJob struct {
|
|
|
|
Task db.Task
|
|
|
|
Template db.Template
|
|
|
|
Inventory db.Inventory
|
|
|
|
Repository db.Repository
|
|
|
|
Environment db.Environment
|
2023-09-23 17:12:35 +02:00
|
|
|
Playbook *db_lib.AnsiblePlaybook
|
2023-08-29 00:51:04 +02:00
|
|
|
Logger lib.Logger
|
|
|
|
|
|
|
|
taskPool *TaskPool
|
|
|
|
}
|
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
type runnerWebhookPayload struct {
|
|
|
|
Action string `json:"action"`
|
|
|
|
ProjectID int `json:"project_id"`
|
|
|
|
TaskID int `json:"task_id"`
|
|
|
|
TemplateID int `json:"template_id"`
|
|
|
|
RunnerID int `json:"runner_id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func callRunnerWebhook(runner *db.Runner, tsk *TaskRunner, action string) (err error) {
|
|
|
|
if runner.Webhook == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var jsonBytes []byte
|
|
|
|
jsonBytes, err = json.Marshal(runnerWebhookPayload{
|
|
|
|
Action: action,
|
|
|
|
ProjectID: tsk.Task.ProjectID,
|
|
|
|
TaskID: tsk.Task.ID,
|
|
|
|
TemplateID: tsk.Template.ID,
|
|
|
|
RunnerID: runner.ID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
client := &http.Client{}
|
|
|
|
|
|
|
|
var req *http.Request
|
|
|
|
req, err = http.NewRequest("POST", runner.Webhook, bytes.NewBuffer(jsonBytes))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-17 01:09:08 +02:00
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
var resp *http.Response
|
|
|
|
resp, err = client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 204 {
|
|
|
|
err = fmt.Errorf("webhook returned incorrect status")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-08-29 00:51:04 +02:00
|
|
|
func (t *RemoteJob) Run(username string, incomingVersion *string) (err error) {
|
|
|
|
|
|
|
|
tsk := t.taskPool.GetTask(t.Task.ID)
|
|
|
|
|
|
|
|
if tsk == nil {
|
|
|
|
return fmt.Errorf("task not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
tsk.IncomingVersion = incomingVersion
|
|
|
|
tsk.Username = username
|
|
|
|
|
|
|
|
var runners []db.Runner
|
|
|
|
db.StoreSession(t.taskPool.store, "run remote job", func() {
|
|
|
|
runners, err = t.taskPool.store.GetGlobalRunners()
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(runners) == 0 {
|
2023-09-10 23:18:25 +02:00
|
|
|
err = fmt.Errorf("no runners available")
|
2023-08-29 00:51:04 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
var runner *db.Runner
|
2023-08-29 00:51:04 +02:00
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
for _, r := range runners {
|
|
|
|
n := t.taskPool.GetNumberOfRunningTasksOfRunner(r.ID)
|
|
|
|
if n < r.MaxParallelTasks {
|
|
|
|
runner = &r
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if runner == nil {
|
|
|
|
err = fmt.Errorf("no runners available")
|
2023-08-29 00:51:04 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
err = callRunnerWebhook(runner, tsk, "start")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
2023-09-10 23:18:25 +02:00
|
|
|
}
|
|
|
|
|
2023-08-29 00:51:04 +02:00
|
|
|
tsk.RunnerID = runner.ID
|
|
|
|
|
2023-12-25 00:17:12 +01:00
|
|
|
startTime := time.Now()
|
|
|
|
|
|
|
|
taskTimedOut := false
|
|
|
|
|
2023-08-29 00:51:04 +02:00
|
|
|
for {
|
2023-12-25 00:17:12 +01:00
|
|
|
if util.Config.MaxTaskDurationSec > 0 && int(time.Now().Sub(startTime).Seconds()) > util.Config.MaxTaskDurationSec {
|
|
|
|
taskTimedOut = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2023-09-10 23:18:25 +02:00
|
|
|
time.Sleep(1_000_000_000)
|
2023-08-29 00:51:04 +02:00
|
|
|
tsk = t.taskPool.GetTask(t.Task.ID)
|
2023-09-23 17:12:35 +02:00
|
|
|
if tsk.Task.Status == lib.TaskSuccessStatus ||
|
|
|
|
tsk.Task.Status == lib.TaskStoppedStatus ||
|
|
|
|
tsk.Task.Status == lib.TaskFailStatus {
|
2023-08-29 00:51:04 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-16 23:47:06 +02:00
|
|
|
err = callRunnerWebhook(runner, tsk, "finish")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
2023-09-10 23:18:25 +02:00
|
|
|
}
|
|
|
|
|
2023-09-23 17:12:35 +02:00
|
|
|
if tsk.Task.Status == lib.TaskFailStatus {
|
2023-09-10 23:18:25 +02:00
|
|
|
err = fmt.Errorf("task failed")
|
2023-12-25 00:17:12 +01:00
|
|
|
} else if taskTimedOut {
|
|
|
|
err = fmt.Errorf("task timed out")
|
2023-09-10 23:18:25 +02:00
|
|
|
}
|
|
|
|
|
2023-08-29 00:51:04 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *RemoteJob) Kill() {
|
2023-09-10 23:18:25 +02:00
|
|
|
// Do nothing because you can't kill remote process
|
2023-08-29 00:51:04 +02:00
|
|
|
}
|