diff --git a/server/ghostrunner-server b/server/ghostrunner-server index d1defe4..1ccaa50 100755 Binary files a/server/ghostrunner-server and b/server/ghostrunner-server differ diff --git a/server/src/go.mod b/server/src/go.mod index 3ba5459..74c7489 100644 --- a/server/src/go.mod +++ b/server/src/go.mod @@ -3,6 +3,7 @@ module ghostrunner-server go 1.24.3 require ( + github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/mattn/go-sqlite3 v1.14.28 gopkg.in/ini.v1 v1.67.0 diff --git a/server/src/go.sum b/server/src/go.sum index bba4684..ce9a7cc 100644 --- a/server/src/go.sum +++ b/server/src/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= diff --git a/server/src/modules/database/handlers.go b/server/src/modules/database/handlers.go index 97bb57f..7d75461 100644 --- a/server/src/modules/database/handlers.go +++ b/server/src/modules/database/handlers.go @@ -92,7 +92,7 @@ func RetrieveTokenNames() []string { return tokenNames } -func InsertTask(name, command string, nodeids []string, date string) error { +func InsertTask(name, uuid, command string, nodeids []string, date string) error { for _, singleNodeid := range nodeids { var count int err := db.QueryRow(declStat.CountDuplTasks, command, singleNodeid).Scan(&count) @@ -104,7 +104,7 @@ func InsertTask(name, command string, nodeids []string, date string) error { log.Println(utilities.WarnTag, "Skipped creation of a task because its a duplicate.") log.Println(utilities.WarnTag, fmt.Sprintf("Details: NodeID: %s, Task name: %s", singleNodeid, name)) } else { - _, err = db.Exec(declStat.CreateTask, name, command, singleNodeid, date) + _, err = db.Exec(declStat.CreateTask, name, uuid, command, singleNodeid, date) if err != nil { return fmt.Errorf("failed to create task: %w", err) } @@ -134,20 +134,20 @@ func RemoveTask(name, nodeid string) error { return nil } -func RetrieveTasks() []utilities.InternalQTaskData { +func RetrieveTasks() []utilities.InternalQueueTaskData { rows, err := db.Query(declStat.ListAllTasks) if err != nil { log.Println("Query error:", err) - return []utilities.InternalQTaskData{} + return []utilities.InternalQueueTaskData{} } defer rows.Close() - var tasks []utilities.InternalQTaskData + var tasks []utilities.InternalQueueTaskData for rows.Next() { - var task utilities.InternalQTaskData + var task utilities.InternalQueueTaskData - err := rows.Scan(&task.Name, &task.Command, &task.Nodeid, &task.Creation) + err := rows.Scan(&task.Name, &task.UUID, &task.Command, &task.Nodeid, &task.Creation, &task.Expire) if err != nil { log.Println("Row scan error:", err) continue diff --git a/server/src/modules/database/statements.go b/server/src/modules/database/statements.go index 247edf0..b95391b 100644 --- a/server/src/modules/database/statements.go +++ b/server/src/modules/database/statements.go @@ -26,13 +26,16 @@ var declStat = Statements{ ); CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT NOT NULL, name TEXT NOT NULL, command TEXT NOT NULL, nodeid TEXT NOT NULL, - creation TEXT NOT NULL + creation TEXT NOT NULL, + expire TEXT DEFAULT 'never' ); CREATE TABLE IF NOT EXISTS completed ( id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT NOT NULL, name TEXT NOT NULL, command TEXT NOT NULL, nodeid TEXT NOT NULL, @@ -58,12 +61,12 @@ var declStat = Statements{ SELECT name FROM tokens`, CreateTask: ` - INSERT INTO tasks (name, command, nodeid, creation) - VALUES (?, ?, ?, ?);`, + INSERT INTO tasks (name, uuid, command, nodeid, creation) + VALUES (?, ?, ?, ?, ?);`, DeleteTask: ` DELETE FROM tasks WHERE name = ? AND nodeid = ?;`, ListAllTasks: ` - Select name, command, nodeid, creation from tasks;`, + Select name, uuid, command, nodeid, creation, expire from tasks;`, CountTasks: ` SELECT COUNT(*) FROM tasks diff --git a/server/src/modules/restapi/handlers.go b/server/src/modules/restapi/handlers.go index 8b974c2..06f0de7 100644 --- a/server/src/modules/restapi/handlers.go +++ b/server/src/modules/restapi/handlers.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "github.com/google/uuid" + "slices" ) @@ -82,7 +84,7 @@ func deleteTokenHandler(hmacKey []byte) http.HandlerFunc { return } - if err := deleteToken(data.Details.Name, hmacKey); err != nil { + if err := deleteToken(data.Details.Name); err != nil { log.Println(utilities.ErrTag, "deleteToken failed:", err) http.Error(w, "Token deletion failed", http.StatusInternalServerError) return @@ -140,7 +142,7 @@ func createToken(tokenName string, hmacKey []byte) (string, error) { return randomString, nil } -func deleteToken(tokenName string, hmacKey []byte) error { +func deleteToken(tokenName string) error { return database.RemoveToken(tokenName) } @@ -234,10 +236,11 @@ func flushTaskListHandler(hmacKey []byte) http.HandlerFunc { } func createTask(taskName, command string, nodeids []string) error { - creationDate := time.Now().Format("02-01-2006 15:04:05") + uuid := uuid.New().String() taskName = strings.ToLower(taskName) + creationDate := time.Now().Format("02-01-2006 15:04:05") - return database.InsertTask(taskName, command, nodeids, creationDate) + return database.InsertTask(taskName, uuid, command, nodeids, creationDate) } func deleteTask(taskName, nodeid string) error { diff --git a/server/src/modules/timekeeper/expire.go b/server/src/modules/timekeeper/expire.go index 9758110..bc2e2ce 100644 --- a/server/src/modules/timekeeper/expire.go +++ b/server/src/modules/timekeeper/expire.go @@ -1,10 +1,18 @@ package timekeeper import ( + "ghostrunner-server/modules/database" "ghostrunner-server/modules/utilities" "log" ) func expireRoutine() { log.Println(utilities.InfoTag, "Checking for expired tasks.") + + tasks := database.RetrieveTasks() + for idx, task := range tasks { + log.Println(idx, task) + } + + log.Println(utilities.InfoTag, "Done") } diff --git a/server/src/modules/timekeeper/task.go b/server/src/modules/timekeeper/task.go index b31d4c9..19bde5d 100644 --- a/server/src/modules/timekeeper/task.go +++ b/server/src/modules/timekeeper/task.go @@ -1,13 +1,13 @@ package timekeeper import ( + "encoding/json" "fmt" "ghostrunner-server/modules/database" "ghostrunner-server/modules/utilities" "ghostrunner-server/modules/wrapper" "log" "slices" - "strings" "sync" ) @@ -31,22 +31,25 @@ func taskRoutine(venvName string, pyListArgs []string) { wg.Add(1) - go func(task utilities.InternalQTaskData) { + go func(task utilities.InternalQueueTaskData) { defer wg.Done() log.Println(utilities.InfoTag, fmt.Sprintf("Node online. NodeID: %s", task.Nodeid)) rawResult := forgeAndExec(venvName, task.Nodeid, task.Command) - pResult := generateResult(rawResult) + jsonResult, err := generateResult(rawResult) + if err != nil { + log.Println(utilities.ErrTag, "Unable to parse result.") + } - insertResult(pResult) + insertResult(jsonResult) log.Println(utilities.InfoTag, "Removing Task from database...") database.RemoveTask(task.Name, task.Nodeid) }(task) } else { - log.Println(utilities.InfoTag, fmt.Sprintf("Node offline all further tasks with this NodeID will be defered. NodeID: %s", relevantNodeid)) // Just a debug line to tell the user that the node is offline. + log.Println(utilities.InfoTag, fmt.Sprintf("Node offline, all further tasks with this NodeID will be defered. NodeID: %s", relevantNodeid)) // Just a debug line to tell the user that the node is offline. offDevices = append(offDevices, relevantNodeid) } } @@ -77,32 +80,18 @@ func forgeAndExec(venvName string, nodeid string, cmdStr string) string { // Example args: ["--node", nodeid, "--command", cmdStr] pyArgs := []string{"--run", "--nodeid", nodeid, "--command", cmdStr} - //log.Printf("[DEBUG] forgeAndExec: Preparing to run ExecTask for node %s", nodeid) output := wrapper.ExecTask(venvName, pyArgs) - //log.Printf("[DEBUG] forgeAndExec: Output received for node %s", nodeid) return output } -func generateResult(rawOut string) []string { - // Trim the brackets if they exist - rawOut = strings.TrimSpace(rawOut) - rawOut = strings.TrimPrefix(rawOut, "[") - rawOut = strings.TrimSuffix(rawOut, "]") - - // Split on comma - items := strings.Split(rawOut, ",") - +func generateResult(rawOut string) ([]string, error) { var result []string - for _, item := range items { - item = strings.TrimSpace(item) - item = strings.Trim(item, `' "`) // Remove surrounding single quotes and extra spaces - if item != "" { - result = append(result, item) - } + err := json.Unmarshal([]byte(rawOut), &result) + if err != nil { + return nil, err } - - return result + return result, nil } func insertResult(result []string) error { diff --git a/server/src/modules/utilities/structs.go b/server/src/modules/utilities/structs.go index 289cbd1..a0a1c93 100644 --- a/server/src/modules/utilities/structs.go +++ b/server/src/modules/utilities/structs.go @@ -42,14 +42,16 @@ type RequestTaskData struct { Status string `json:"status"` } -type InternalQTaskData struct { +type InternalQueueTaskData struct { Name string `json:"name"` + UUID string `json:"uuid"` Command string `json:"command"` Nodeid string `json:"nodeid"` Creation string `json:"creation"` + Expire string `json:"expire"` } -type InternalCTaskData struct { +type InternalCompTaskData struct { Name string `json:"name"` Command string `json:"command"` Nodeid string `json:"nodeid"`