draft by
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"oc-scheduler/infrastructure"
|
"oc-scheduler/infrastructure"
|
||||||
|
"strings"
|
||||||
|
|
||||||
oclib "cloud.o-forge.io/core/oc-lib"
|
oclib "cloud.o-forge.io/core/oc-lib"
|
||||||
"cloud.o-forge.io/core/oc-lib/dbs"
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
@@ -86,29 +87,19 @@ var wsUpgrader = gorillaws.Upgrader{
|
|||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title CheckStream
|
// CheckStreamHandler is a plain http.HandlerFunc (registered via beego.Handler
|
||||||
// @Description WebSocket stream of slot availability for a workflow.
|
// to avoid Beego's WriteHeader interference with the WebSocket upgrade).
|
||||||
// After the handshake the client sends one JSON frame containing the
|
// Path: /oc/:id/check → parts = ["", "oc", "<id>", "check"]
|
||||||
// WorkflowSchedule parameters (start, end, booking_mode, duration_s, …).
|
// Query params: as_possible=true, preemption=true
|
||||||
// The server responds with a CheckResult frame immediately and again each time
|
func CheckStreamHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// a planner for one of the workflow's storage/compute peers is updated.
|
parts := strings.Split(strings.TrimSuffix(r.URL.Path, "/"), "/")
|
||||||
// When the stream is interrupted the cache entries for those peers are evicted
|
wfID := parts[len(parts)-2] // second-to-last segment
|
||||||
// and a PB_CLOSE_PLANNER event is emitted on NATS.
|
|
||||||
// Query params:
|
|
||||||
// - as_possible=true ignore start date, search from now
|
|
||||||
// - preemption=true validate anyway, raise warnings
|
|
||||||
//
|
|
||||||
// @Param id path string true "workflow id"
|
|
||||||
// @Param as_possible query bool false "find nearest free slot from now"
|
|
||||||
// @Param preemption query bool false "validate anyway, raise warnings"
|
|
||||||
// @Success 101
|
|
||||||
// @router /:id/check [get]
|
|
||||||
func (o *WorkflowSchedulerController) CheckStream() {
|
|
||||||
wfID := o.Ctx.Input.Param(":id")
|
|
||||||
asap, _ := o.GetBool("as_possible", false)
|
|
||||||
preemption, _ := o.GetBool("preemption", false)
|
|
||||||
|
|
||||||
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
q := r.URL.Query()
|
||||||
|
asap := q.Get("as_possible") == "true"
|
||||||
|
preemption := q.Get("preemption") == "true"
|
||||||
|
|
||||||
|
user, peerID, groups := oclib.ExtractTokenInfo(*r)
|
||||||
req := &tools.APIRequest{
|
req := &tools.APIRequest{
|
||||||
Username: user,
|
Username: user,
|
||||||
PeerID: peerID,
|
PeerID: peerID,
|
||||||
@@ -120,15 +111,16 @@ func (o *WorkflowSchedulerController) CheckStream() {
|
|||||||
// Resolve the peer IDs concerned by this workflow before upgrading so we
|
// Resolve the peer IDs concerned by this workflow before upgrading so we
|
||||||
// can abort cleanly with a plain HTTP error if the workflow is not found.
|
// can abort cleanly with a plain HTTP error if the workflow is not found.
|
||||||
watchedPeers, err := infrastructure.GetWorkflowPeerIDs(wfID, req)
|
watchedPeers, err := infrastructure.GetWorkflowPeerIDs(wfID, req)
|
||||||
|
fmt.Println("Here my watched peers involved in workflow", watchedPeers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
o.Data["json"] = map[string]interface{}{"code": 404, "error": err.Error()}
|
http.Error(w, `{"code":404,"error":"`+err.Error()+`"}`, http.StatusNotFound)
|
||||||
o.ServeJSON()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade to WebSocket.
|
// Upgrade to WebSocket.
|
||||||
conn, err := wsUpgrader.Upgrade(o.Ctx.ResponseWriter, o.Ctx.Request, nil)
|
conn, err := wsUpgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// gorilla already wrote the error response
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +154,7 @@ func (o *WorkflowSchedulerController) CheckStream() {
|
|||||||
|
|
||||||
push := func() error {
|
push := func() error {
|
||||||
result, checkErr := ws.Check(wfID, asap, preemption, req)
|
result, checkErr := ws.Check(wfID, asap, preemption, req)
|
||||||
|
fmt.Println(result, checkErr)
|
||||||
if checkErr != nil {
|
if checkErr != nil {
|
||||||
return checkErr
|
return checkErr
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module oc-scheduler
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260224093610-a9ebad78f3a8
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260304145747-e03a0d3dd0aa
|
||||||
github.com/beego/beego/v2 v2.3.8
|
github.com/beego/beego/v2 v2.3.8
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/robfig/cron v1.2.0
|
github.com/robfig/cron v1.2.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -14,6 +14,8 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20260224092928-54aef164ba10 h1:9i8fDtGjg3JDn
|
|||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260224092928-54aef164ba10/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260224092928-54aef164ba10/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260224093610-a9ebad78f3a8 h1:xoC5PAz1469QxrNm8rrsq5+BtwshEt+L2Nhf90MrqrM=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260224093610-a9ebad78f3a8 h1:xoC5PAz1469QxrNm8rrsq5+BtwshEt+L2Nhf90MrqrM=
|
||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20260224093610-a9ebad78f3a8/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260224093610-a9ebad78f3a8/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
|
||||||
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260304145747-e03a0d3dd0aa h1:1wCpI4dwN1pj6MlpJ7/WifhHVHmCE4RU+9klwqgo/bk=
|
||||||
|
cloud.o-forge.io/core/oc-lib v0.0.0-20260304145747-e03a0d3dd0aa/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc=
|
github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc=
|
||||||
github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg=
|
github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg=
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*
|
|||||||
}
|
}
|
||||||
fmt.Println("Schedules")
|
fmt.Println("Schedules")
|
||||||
|
|
||||||
wf.GetAccessor(&tools.APIRequest{Admin: true}).UpdateOne(wf, wf.GetID())
|
wf.GetAccessor(&tools.APIRequest{Admin: true}).UpdateOne(wf.Serialize(wf), wf.GetID())
|
||||||
|
|
||||||
return ws, wf, executions, nil
|
return ws, wf, executions, nil
|
||||||
}
|
}
|
||||||
@@ -414,10 +414,10 @@ func (ws *WorkflowSchedule) Check(wfID string, asap bool, preemption bool, reque
|
|||||||
// 4. Extract booking-relevant (storage + compute) resources from the graph,
|
// 4. Extract booking-relevant (storage + compute) resources from the graph,
|
||||||
// resolving the selected instance for each resource.
|
// resolving the selected instance for each resource.
|
||||||
checkables := collectBookingResources(wf, ws.SelectedInstances)
|
checkables := collectBookingResources(wf, ws.SelectedInstances)
|
||||||
|
fmt.Println(checkables)
|
||||||
// 5. Check every resource against its peer's planner
|
// 5. Check every resource against its peer's planner
|
||||||
unavailable, warnings := checkResourceAvailability(checkables, start, end)
|
unavailable, warnings := checkResourceAvailability(checkables, start, end)
|
||||||
|
fmt.Println(unavailable, warnings)
|
||||||
result := &CheckResult{
|
result := &CheckResult{
|
||||||
Start: start,
|
Start: start,
|
||||||
End: end,
|
End: end,
|
||||||
|
|||||||
BIN
oc-scheduler
BIN
oc-scheduler
Binary file not shown.
@@ -7,6 +7,42 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "GetAll",
|
||||||
|
Router: `/`,
|
||||||
|
AllowHTTPMethods: []string{"get"},
|
||||||
|
MethodParams: param.Make(),
|
||||||
|
Filters: nil,
|
||||||
|
Params: nil})
|
||||||
|
|
||||||
|
beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "Get",
|
||||||
|
Router: `/:id`,
|
||||||
|
AllowHTTPMethods: []string{"get"},
|
||||||
|
MethodParams: param.Make(),
|
||||||
|
Filters: nil,
|
||||||
|
Params: nil})
|
||||||
|
|
||||||
|
beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "Search",
|
||||||
|
Router: `/search/:start_date/:end_date`,
|
||||||
|
AllowHTTPMethods: []string{"get"},
|
||||||
|
MethodParams: param.Make(),
|
||||||
|
Filters: nil,
|
||||||
|
Params: nil})
|
||||||
|
|
||||||
|
beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:BookingController"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "ExecutionSearch",
|
||||||
|
Router: `/search/execution/:id`,
|
||||||
|
AllowHTTPMethods: []string{"get"},
|
||||||
|
MethodParams: param.Make(),
|
||||||
|
Filters: nil,
|
||||||
|
Params: nil})
|
||||||
|
|
||||||
beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"],
|
beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"],
|
||||||
beego.ControllerComments{
|
beego.ControllerComments{
|
||||||
Method: "GetLogs",
|
Method: "GetLogs",
|
||||||
@@ -88,6 +124,15 @@ func init() {
|
|||||||
Filters: nil,
|
Filters: nil,
|
||||||
Params: nil})
|
Params: nil})
|
||||||
|
|
||||||
|
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
|
||||||
|
beego.ControllerComments{
|
||||||
|
Method: "CheckStream",
|
||||||
|
Router: `/:id/check`,
|
||||||
|
AllowHTTPMethods: []string{"get"},
|
||||||
|
MethodParams: param.Make(),
|
||||||
|
Filters: nil,
|
||||||
|
Params: nil})
|
||||||
|
|
||||||
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
|
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
|
||||||
beego.ControllerComments{
|
beego.ControllerComments{
|
||||||
Method: "SearchScheduledDraftOrder",
|
Method: "SearchScheduledDraftOrder",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"oc-scheduler/controllers"
|
"oc-scheduler/controllers"
|
||||||
|
|
||||||
beego "github.com/beego/beego/v2/server/web"
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
@@ -41,4 +42,7 @@ func init() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
beego.AddNamespace(ns)
|
beego.AddNamespace(ns)
|
||||||
|
|
||||||
|
// Route WebSocket hors du pipeline Beego pour éviter le WriteHeader parasite
|
||||||
|
beego.Handler("/oc/:id/check", http.HandlerFunc(controllers.CheckStreamHandler))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,116 @@
|
|||||||
},
|
},
|
||||||
"basePath": "/oc/",
|
"basePath": "/oc/",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/booking/": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"booking"
|
||||||
|
],
|
||||||
|
"description": "find booking by id\n\u003cbr\u003e",
|
||||||
|
"operationId": "BookingController.GetAll",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "is_draft",
|
||||||
|
"description": "draft wished",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "{booking} models.booking"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/booking/search/execution/{id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"booking"
|
||||||
|
],
|
||||||
|
"description": "search bookings by execution\n\u003cbr\u003e",
|
||||||
|
"operationId": "BookingController.Search",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "id",
|
||||||
|
"description": "id execution",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "is_draft",
|
||||||
|
"description": "draft wished",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "{workspace} models.workspace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/booking/search/{start_date}/{end_date}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"booking"
|
||||||
|
],
|
||||||
|
"description": "search bookings\n\u003cbr\u003e",
|
||||||
|
"operationId": "BookingController.Search",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "start_date",
|
||||||
|
"description": "the word search you want to get",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "end_date",
|
||||||
|
"description": "the word search you want to get",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "is_draft",
|
||||||
|
"description": "draft wished",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "{workspace} models.workspace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/booking/{id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"booking"
|
||||||
|
],
|
||||||
|
"description": "find booking by id\n\u003cbr\u003e",
|
||||||
|
"operationId": "BookingController.Get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "id",
|
||||||
|
"description": "the id you want to get",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "{booking} models.booking"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/execution/": {
|
"/execution/": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -240,6 +350,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/{id}/check": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"oc-scheduler/controllersWorkflowSchedulerController"
|
||||||
|
],
|
||||||
|
"description": "WebSocket stream of slot availability for a workflow.\n\u003cbr\u003e",
|
||||||
|
"operationId": "WorkflowSchedulerController.CheckStream",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "id",
|
||||||
|
"description": "workflow id",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "as_possible",
|
||||||
|
"description": "find nearest free slot from now",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "preemption",
|
||||||
|
"description": "validate anyway, raise warnings",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"101": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/{id}/order": {
|
"/{id}/order": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -279,6 +424,10 @@
|
|||||||
"name": "loki",
|
"name": "loki",
|
||||||
"description": "Operations about workflow\n"
|
"description": "Operations about workflow\n"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "booking",
|
||||||
|
"description": "Operations about workspace\n"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "execution",
|
"name": "execution",
|
||||||
"description": "Operations about workflow\n"
|
"description": "Operations about workflow\n"
|
||||||
|
|||||||
@@ -57,6 +57,31 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: '{workspace} models.workspace'
|
description: '{workspace} models.workspace'
|
||||||
|
/{id}/check:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- oc-scheduler/controllersWorkflowSchedulerController
|
||||||
|
description: |-
|
||||||
|
WebSocket stream of slot availability for a workflow.
|
||||||
|
<br>
|
||||||
|
operationId: WorkflowSchedulerController.CheckStream
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
description: workflow id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: as_possible
|
||||||
|
description: find nearest free slot from now
|
||||||
|
type: boolean
|
||||||
|
- in: query
|
||||||
|
name: preemption
|
||||||
|
description: validate anyway, raise warnings
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
"101":
|
||||||
|
description: ""
|
||||||
/{id}/order:
|
/{id}/order:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -74,6 +99,86 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: '{workspace} models.workspace'
|
description: '{workspace} models.workspace'
|
||||||
|
/booking/:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- booking
|
||||||
|
description: |-
|
||||||
|
find booking by id
|
||||||
|
<br>
|
||||||
|
operationId: BookingController.GetAll
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: is_draft
|
||||||
|
description: draft wished
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: '{booking} models.booking'
|
||||||
|
/booking/{id}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- booking
|
||||||
|
description: |-
|
||||||
|
find booking by id
|
||||||
|
<br>
|
||||||
|
operationId: BookingController.Get
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
description: the id you want to get
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: '{booking} models.booking'
|
||||||
|
/booking/search/{start_date}/{end_date}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- booking
|
||||||
|
description: |-
|
||||||
|
search bookings
|
||||||
|
<br>
|
||||||
|
operationId: BookingController.Search
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: start_date
|
||||||
|
description: the word search you want to get
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: path
|
||||||
|
name: end_date
|
||||||
|
description: the word search you want to get
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: is_draft
|
||||||
|
description: draft wished
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: '{workspace} models.workspace'
|
||||||
|
/booking/search/execution/{id}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- booking
|
||||||
|
description: |-
|
||||||
|
search bookings by execution
|
||||||
|
<br>
|
||||||
|
operationId: BookingController.Search
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
description: id execution
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: is_draft
|
||||||
|
description: draft wished
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: '{workspace} models.workspace'
|
||||||
/execution/:
|
/execution/:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -205,6 +310,9 @@ tags:
|
|||||||
- name: loki
|
- name: loki
|
||||||
description: |
|
description: |
|
||||||
Operations about workflow
|
Operations about workflow
|
||||||
|
- name: booking
|
||||||
|
description: |
|
||||||
|
Operations about workspace
|
||||||
- name: execution
|
- name: execution
|
||||||
description: |
|
description: |
|
||||||
Operations about workflow
|
Operations about workflow
|
||||||
|
|||||||
115
ws.go
Normal file
115
ws.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
timeout := flag.Int("timeout", 30, "secondes sans message avant de quitter")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
args := flag.Args()
|
||||||
|
// Exemples de routes WS disponibles :
|
||||||
|
// ws://localhost:8090/oc/<workflow-id>/check
|
||||||
|
// ws://localhost:8090/oc/<workflow-id>/check?as_possible=true
|
||||||
|
// ws://localhost:8090/oc/<workflow-id>/check?as_possible=true&preemption=true
|
||||||
|
url := "ws://localhost:8090/oc/WORKFLOW_ID/check?as_possible=true"
|
||||||
|
token := ""
|
||||||
|
// Body JSON envoyé comme premier message WebSocket (WorkflowSchedule).
|
||||||
|
// Seuls start + duration_s sont requis si as_possible=true.
|
||||||
|
body := `{"start":"` + time.Now().UTC().Format(time.RFC3339) + `","duration_s":3600}`
|
||||||
|
|
||||||
|
if len(args) >= 1 {
|
||||||
|
url = args[0]
|
||||||
|
}
|
||||||
|
if len(args) >= 2 {
|
||||||
|
token = args[1]
|
||||||
|
}
|
||||||
|
if len(args) >= 3 {
|
||||||
|
body = args[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
origin := "http://localhost/"
|
||||||
|
config, err := websocket.NewConfig(url, origin)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Config invalide : %v", err)
|
||||||
|
}
|
||||||
|
if token != "" {
|
||||||
|
config.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
fmt.Printf("Token : %s...\n", token[:min(20, len(token))])
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Connexion à : %s\n", url)
|
||||||
|
ws, err := websocket.DialConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Impossible de se connecter : %v", err)
|
||||||
|
}
|
||||||
|
defer ws.Close()
|
||||||
|
fmt.Println("Connecté — envoi du body initial...")
|
||||||
|
|
||||||
|
// Envoi du WorkflowSchedule comme premier message.
|
||||||
|
if err := websocket.Message.Send(ws, body); err != nil {
|
||||||
|
log.Fatalf("Impossible d'envoyer le body initial : %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Body envoyé : %s\n\nEn attente de messages...\n\n", body)
|
||||||
|
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, os.Interrupt)
|
||||||
|
|
||||||
|
msgs := make(chan string)
|
||||||
|
errs := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
var raw string
|
||||||
|
if err := websocket.Message.Receive(ws, &raw); err != nil {
|
||||||
|
errs <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msgs <- raw
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
idleTimer := time.NewTimer(time.Duration(*timeout) * time.Second)
|
||||||
|
defer idleTimer.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
fmt.Println("\nInterruption — fermeture.")
|
||||||
|
return
|
||||||
|
case err := <-errs:
|
||||||
|
fmt.Printf("Connexion fermée : %v\n", err)
|
||||||
|
return
|
||||||
|
case <-idleTimer.C:
|
||||||
|
fmt.Printf("Timeout (%ds) — aucun message reçu, fermeture.\n", *timeout)
|
||||||
|
return
|
||||||
|
case raw := <-msgs:
|
||||||
|
idleTimer.Reset(time.Duration(*timeout) * time.Second)
|
||||||
|
var data any
|
||||||
|
if err := json.Unmarshal([]byte(raw), &data); err == nil {
|
||||||
|
b, _ := json.MarshalIndent(data, "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Message brut : %s\n", raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user