This commit is contained in:
@@ -1106,15 +1106,6 @@ const docTemplate = `{
|
|||||||
"name": "id",
|
"name": "id",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Agent reference",
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/internal_handlers.WaitJobIn"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -2923,17 +2914,6 @@ const docTemplate = `{
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"internal_handlers.WaitJobIn": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"agent_id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"agent_id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
|||||||
@@ -1095,15 +1095,6 @@
|
|||||||
"name": "id",
|
"name": "id",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Agent reference",
|
|
||||||
"name": "body",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/internal_handlers.WaitJobIn"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -2912,17 +2903,6 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"internal_handlers.WaitJobIn": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"agent_id"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"agent_id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
|||||||
@@ -513,13 +513,6 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- token
|
- token
|
||||||
type: object
|
type: object
|
||||||
internal_handlers.WaitJobIn:
|
|
||||||
properties:
|
|
||||||
agent_id:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- agent_id
|
|
||||||
type: object
|
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
paths:
|
paths:
|
||||||
@@ -1165,12 +1158,6 @@ paths:
|
|||||||
name: id
|
name: id
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
- description: Agent reference
|
|
||||||
in: body
|
|
||||||
name: body
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/internal_handlers.WaitJobIn'
|
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|||||||
@@ -51,11 +51,6 @@ type JobResult struct {
|
|||||||
Status int32 `json:"status"`
|
Status int32 `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitJobIn is the request body for waiting on a job.
|
|
||||||
type WaitJobIn struct {
|
|
||||||
AgentID string `json:"agent_id" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddJob submits a job to an agent and returns a wait_url for the result.
|
// AddJob submits a job to an agent and returns a wait_url for the result.
|
||||||
// @Summary Submit a job to an agent
|
// @Summary Submit a job to an agent
|
||||||
// @Description Sends a command to the specified agent and returns a URL to wait for the result
|
// @Description Sends a command to the specified agent and returns a URL to wait for the result
|
||||||
@@ -118,14 +113,14 @@ func (h *JobsHandlers) runCommand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WaitJob waits for a submitted job to complete (long-poll).
|
// WaitJob waits for a submitted job to complete (long-poll).
|
||||||
// If the job is already done, returns immediately.
|
// First checks the database; if already finished, returns immediately.
|
||||||
|
// Otherwise waits on the agent for the result.
|
||||||
// @Summary Wait for job result
|
// @Summary Wait for job result
|
||||||
// @Description Long-polls for a job result. Returns immediately if the job is already finished.
|
// @Description Long-polls for a job result. Returns immediately if the job is already finished.
|
||||||
// @Tags jobs
|
// @Tags jobs
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "Job ID"
|
// @Param id path int true "Job ID"
|
||||||
// @Param body body WaitJobIn true "Agent reference"
|
|
||||||
// @Success 200 {object} JobResult
|
// @Success 200 {object} JobResult
|
||||||
// @Failure 400 {object} map[string]string
|
// @Failure 400 {object} map[string]string
|
||||||
// @Failure 404 {object} map[string]string
|
// @Failure 404 {object} map[string]string
|
||||||
@@ -137,32 +132,50 @@ func (h *JobsHandlers) WaitJob(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var in WaitJobIn
|
// Check database first
|
||||||
if err := c.Bind(&in); err != nil {
|
job, err := h.jobRepo.GetJobByID(c.Request.Context(), jid)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
if err != nil {
|
||||||
|
if errors.Is(err, repository.ErrNotFound) {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "job not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
agent, ok := h.tracker.GetAgent(in.AgentID)
|
// If job is already completed (has output or non-zero status), return immediately
|
||||||
|
if job.Status != 0 || job.Stdout != "" || job.Stderr != "" {
|
||||||
|
c.JSON(http.StatusOK, JobResult{
|
||||||
|
ID: job.ID,
|
||||||
|
Command: job.Command,
|
||||||
|
Stdin: job.Stdin,
|
||||||
|
Stdout: job.Stdout,
|
||||||
|
Stderr: job.Stderr,
|
||||||
|
Status: job.Status,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job is still pending — wait on the agent
|
||||||
|
agent, ok := h.tracker.GetAgent(job.AgentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
c.Status(http.StatusNotFound)
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
||||||
c.Error(fmt.Errorf("agent not found"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
job, err := agent.WaitJob(jid)
|
ajob, err := agent.WaitJob(jid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, JobResult{
|
c.JSON(http.StatusOK, JobResult{
|
||||||
ID: job.ID,
|
ID: ajob.ID,
|
||||||
Command: job.Command,
|
Command: ajob.Command,
|
||||||
Stdin: job.Stdin,
|
Stdin: ajob.Stdin,
|
||||||
Stdout: job.Stdout,
|
Stdout: ajob.Stdout,
|
||||||
Stderr: job.Stderr,
|
Stderr: ajob.Stderr,
|
||||||
Status: job.Status,
|
Status: ajob.Status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type Job struct {
|
type Job struct {
|
||||||
ID int64
|
ID int64
|
||||||
|
AgentID string
|
||||||
JobForInsert
|
JobForInsert
|
||||||
JobForUpdate
|
JobForUpdate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ func (r *JobRepository) GetJobByID(ctx context.Context, jid int64) (models.Job,
|
|||||||
var stdinVal *string
|
var stdinVal *string
|
||||||
|
|
||||||
err := r.DB.QueryRowContext(ctx,
|
err := r.DB.QueryRowContext(ctx,
|
||||||
`SELECT id, command, stdin, stdout, stderr, status FROM jobs WHERE id = ?`,
|
`SELECT id, agent_id, command, stdin, stdout, stderr, status FROM jobs WHERE id = ?`,
|
||||||
jid,
|
jid,
|
||||||
).Scan(&job.ID, &commandJSON, &stdinVal, &job.Stdout, &job.Stderr, &job.Status)
|
).Scan(&job.ID, &job.AgentID, &commandJSON, &stdinVal, &job.Stdout, &job.Stderr, &job.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return models.Job{}, ErrNotFound
|
return models.Job{}, ErrNotFound
|
||||||
|
|||||||
Reference in New Issue
Block a user