Skip to content
Merged
111 changes: 110 additions & 1 deletion api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ servers:

info:
title: opensvc collector api
version: 1.0.8
version: 1.0.9

paths:
/feed/daemon/ping:
Expand Down Expand Up @@ -197,6 +197,64 @@ paths:
tags:
- agent

/feed/action:
put:
description: |
End an action for a given object path
operationId: PutFeedActionEnd
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Action'
responses:
202:
description: action end accepted
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
403:
$ref: '#/components/responses/403'
500:
$ref: '#/components/responses/500'
security:
- basicAuth: [ ]
- bearerAuth: [ ]
tags:
- agent
post:
description: |
Begin an action for a given object path
operationId: PostFeedAction
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Action'
responses:
202:
description: action begin accepted
content:
application/json:
schema:
$ref: '#/components/schemas/ActionRequestAccepted'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
403:
$ref: '#/components/responses/403'
500:
$ref: '#/components/responses/500'
security:
- basicAuth: [ ]
- bearerAuth: [ ]
tags:
- agent

/version:
get:
operationId: GetVersion
Expand Down Expand Up @@ -292,6 +350,55 @@ components:
type: string
description: the opensvc client data version

ActionRequestAccepted:
type: object
required:
- uuid
properties:
uuid:
type: string

Action:
type: object
required:
- path
- action
- version
- begin
- cron
- session_uuid
- argv
- uuid
- end
- actionlogfile
- status
properties:
path:
type: string
action:
type: string
version:
type: string
description: the opensvc client data version
begin:
type: string
cron:
type: boolean
session_uuid:
type: string
argv:
type: array
items:
type: string
uuid:
type: string
end:
type: string
actionlogfile:
type: string
status:
type: string

NodeDisks:
type: object
properties:
Expand Down Expand Up @@ -558,3 +665,5 @@ components:
items:
type: string
description: object name


99 changes: 68 additions & 31 deletions api/codegen_server_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions api/codegen_type_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions apihandlers/post_feed_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package apihandlers

import (
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/google/uuid"
"github.com/labstack/echo/v4"

"github.com/opensvc/oc3/api"
"github.com/opensvc/oc3/cachekeys"
)

// {
// "action": "thaw",
// "argv": [
// "foo",
// "thaw",
// "--local"
// ],
// "begin": "2026-01-12 10:57:12",
// "cron": false,
// "path": "foo",
// "session_uuid": "b9d795bc-498e-4c20-aada-9feec2eaa947",
// "version": "2.1-1977"
// }

// PostFeedAction handles POST /action/begin
func (a *Api) PostFeedAction(c echo.Context) error {
keyH := cachekeys.FeedActionH
keyQ := cachekeys.FeedActionQ
keyPendingH := cachekeys.FeedActionPendingH

log := getLog(c)

nodeID := nodeIDFromContext(c)
if nodeID == "" {
log.Debug("node auth problem")
return JSONNodeAuthProblem(c)
}

ClusterID := clusterIDFromContext(c)
if ClusterID == "" {
return JSONProblemf(c, http.StatusConflict, "Refused", "authenticated node doesn't define cluster id")
}

var payload api.PostFeedActionJSONRequestBody
if err := c.Bind(&payload); err != nil {
return JSONProblem(c, http.StatusBadRequest, "Failed to json decode request body", err.Error())
}

if !strings.HasPrefix(payload.Version, "2.") && !strings.HasPrefix(payload.Version, "3.") {
log.Error(fmt.Sprintf("unexpected version %s", payload.Version))
return JSONProblemf(c, http.StatusBadRequest, "BadRequest", "unsupported data client version: %s", payload.Version)
}

b, err := json.Marshal(payload)
if err != nil {
return JSONProblem(c, http.StatusInternalServerError, "Failed to re-encode config", err.Error())
}

reqCtx := c.Request().Context()

uuid := uuid.New().String()
idx := fmt.Sprintf("%s@%s@%s:%s", payload.Path, nodeID, ClusterID, uuid)

s := fmt.Sprintf("HSET %s %s", keyH, idx)
if _, err := a.Redis.HSet(reqCtx, keyH, idx, b).Result(); err != nil {
s = fmt.Sprintf("%s: %s", s, err)
log.Error(s)
return JSONProblem(c, http.StatusInternalServerError, "", s)
}

if err := a.pushNotPending(reqCtx, keyPendingH, keyQ, idx); err != nil {
log.Error(fmt.Sprintf("can't push %s %s: %s", keyQ, idx, err))
return JSONProblemf(c, http.StatusInternalServerError, "redis operation", "can't push %s %s: %s", keyQ, idx, err)
}

log.Debug("action begin accepted")
return c.JSON(http.StatusAccepted, api.ActionRequestAccepted{Uuid: uuid})
}
Loading
Loading