Datacenter Update to Ws

This commit is contained in:
mr
2026-04-09 07:49:35 +02:00
parent c87245e83f
commit 74919994c2
20 changed files with 633 additions and 922 deletions

View File

@@ -3,6 +3,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"slices" "slices"
"strconv"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
@@ -28,11 +29,15 @@ func isAdmin(groups []string) bool {
// @Title GetAll // @Title GetAll
// @Description Retourne toutes les images autorisées à persister sur ce peer // @Description Retourne toutes les images autorisées à persister sur ce peer
// @Param offset query string false
// @Param limit query string false
// @Success 200 {object} []allowed_image.AllowedImage // @Success 200 {object} []allowed_image.AllowedImage
// @router / [get] // @router / [get]
func (o *AllowedImageController) GetAll() { func (o *AllowedImageController) GetAll() {
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
res := oclib.NewRequest(oclib.LibDataEnum(oclib.ALLOWED_IMAGE), user, peerID, groups, nil).LoadAll(false) res := oclib.NewRequest(oclib.LibDataEnum(oclib.ALLOWED_IMAGE), user, peerID, groups, nil).LoadAll(false, int64(offset), int64(limit))
o.Data["json"] = res o.Data["json"] = res
o.ServeJSON() o.ServeJSON()
} }

View File

@@ -1,12 +1,15 @@
package controllers package controllers
import ( import (
"fmt"
"net/http" "net/http"
"oc-datacenter/infrastructure/monitor" "oc-datacenter/infrastructure/monitor"
"strconv"
"time" "time"
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"
"cloud.o-forge.io/core/oc-lib/models/utils"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@@ -16,74 +19,143 @@ type DatacenterController struct {
beego.Controller beego.Controller
} }
func resourceTypeEnum(t string, special bool) []oclib.LibDataEnum {
e := []oclib.LibDataEnum{}
if special && t == "resource" {
return e
}
if t == "storage" || t == "live" {
e = append(e, oclib.LibDataEnum(oclib.LIVE_STORAGE))
}
if t == "datacenter" || t == "live" {
e = append(e, oclib.LibDataEnum(oclib.LIVE_DATACENTER))
}
return e
}
func (o *DatacenterController) collection(special bool) []oclib.LibDataEnum {
// Extrait le type depuis le segment d'URL après "resource"
// URL forme: /oc/resource/{type}/...
typ := o.Ctx.Input.Param(":type")
return resourceTypeEnum(typ, special)
}
// @Title Search
// @Description search datacenter
// @Param type path string true "the type you want to get"
// @Param search path string true "the word search you want to get"
// @Param is_draft query string false "draft wished"
// @Param offset query string false
// @Param limit query string false
// @Success 200 {workspace} models.workspace
// @router /:type/search/:search [get]
func (o *DatacenterController) Search() {
/*
* This is a sample of how to use the search function
* The search function is used to search for data in the database
* The search function takes in a filter and a data type
* The filter is a struct that contains the search parameters
* The data type is an enum that specifies the type of data to search for
* The search function returns a list of data that matches the filter
* The data is then returned as a json object
*/
// store and return Id or post with UUID
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
search := o.Ctx.Input.Param(":search")
if search == "*" {
search = ""
}
isDraft := o.Ctx.Input.Query("is_draft")
m := map[string][]utils.ShallowDBObject{}
for _, col := range o.collection(false) {
if m[col.String()] == nil {
m[col.String()] = []utils.ShallowDBObject{}
}
s := oclib.NewRequest(col, user, peerID, groups, nil).Search(&dbs.Filters{
Or: map[string][]dbs.Filter{
// "abstractlive.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
"abstractlive.abstractobject.name": {{Operator: dbs.LIKE.String(), Value: search}},
},
}, "", isDraft == "true", int64(offset), int64(limit))
fmt.Println(s)
m[col.String()] = append(m[col.String()], s.Data...)
}
o.Data["json"] = map[string]interface{}{
"data": m,
"code": 200,
"err": nil,
}
o.ServeJSON()
}
// @Title GetAll // @Title GetAll
// @Description find booking by id // @Description find booking by id
// @Param type path string true "the word type you want to get"
// @Param is_draft query string false "draft wished" // @Param is_draft query string false "draft wished"
// @Param offset query string false
// @Param limit query string false
// @Success 200 {booking} models.booking // @Success 200 {booking} models.booking
// @router / [get] // @router / [get]
func (o *DatacenterController) GetAll() { func (o *DatacenterController) GetAll() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
isDraft := o.Ctx.Input.Query("is_draft") isDraft := o.Ctx.Input.Query("is_draft")
storages := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), user, peerID, groups, nil).Search(&dbs.Filters{ offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
Or: map[string][]dbs.Filter{ limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}}, m := map[string][]utils.ShallowDBObject{}
}, for _, col := range o.collection(false) {
}, "", isDraft == "true") if m[col.String()] == nil {
computes := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_DATACENTER), user, peerID, groups, nil).Search(&dbs.Filters{ m[col.String()] = []utils.ShallowDBObject{}
Or: map[string][]dbs.Filter{ }
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}}, s := oclib.NewRequest(oclib.LibDataEnum(col), user, peerID, groups, nil).LoadAll(isDraft == "true", int64(offset), int64(limit))
}, fmt.Println(s)
}, "", isDraft == "true") m[col.String()] = append(m[col.String()], s.Data...)
storages.Data = append(storages.Data, computes.Data...) }
if storages.Err != "" { fmt.Println(m)
storages.Err += " - " + computes.Err o.Data["json"] = map[string]interface{}{
"data": m,
"code": 200,
"err": nil,
} }
o.Data["json"] = storages
o.ServeJSON() o.ServeJSON()
} }
// @Title Get // @Title Get
// @Description find booking by id // @Description find booking by id
// @Param id path string true "the id you want to get" // @Param id path string true "the id you want to get"
// @Param type path string true "the word type you want to get"
// @Param is_draft query string false "draft wished" // @Param is_draft query string false "draft wished"
// @Success 200 {booking} models.booking // @Success 200 {booking} models.booking
// @router /:id [get] // @router /:type/:id [get]
func (o *DatacenterController) Get() { func (o *DatacenterController) Get() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
isDraft := o.Ctx.Input.Query("is_draft")
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")
storages := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), user, peerID, groups, nil).Search(&dbs.Filters{ for _, col := range o.collection(false) {
Or: map[string][]dbs.Filter{ data := oclib.NewRequest(col, user, peerID, groups, nil).LoadOne(id)
"abstractinstanciatedresource.abstractresource.abstractobject.id": {{Operator: dbs.EQUAL.String(), Value: id}}, o.Data["json"] = data
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}}, if data.Data != nil {
}, break
}, "", isDraft == "true")
if len(storages.Data) == 0 {
computes := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_DATACENTER), user, peerID, groups, nil).Search(&dbs.Filters{
Or: map[string][]dbs.Filter{
"abstractinstanciatedresource.abstractresource.abstractobject.id": {{Operator: dbs.EQUAL.String(), Value: id}},
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}, "", isDraft == "true")
if len(computes.Data) == 0 {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": computes.Code,
"err": computes.Err,
} }
} else {
o.Data["json"] = map[string]interface{}{
"data": computes.Data[0],
"code": computes.Code,
"err": computes.Err,
} }
o.ServeJSON()
} }
} else { // @Title Delete
o.Data["json"] = map[string]interface{}{ // @Description find booking by id
"data": storages.Data[0], // @Param id path string true "the id you want to get"
"code": storages.Code, // @Param type path string true "the word type you want to get"
"err": storages.Err, // @Param is_draft query string false "draft wished"
// @Success 200 {booking} models.booking
// @router /:type/:id [delete]
func (o *DatacenterController) Delete() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
for _, col := range o.collection(false) {
data := oclib.NewRequest(col, user, peerID, groups, nil).DeleteOne(id)
o.Data["json"] = data
if data.Data != nil {
break
} }
} }
o.ServeJSON() o.ServeJSON()
@@ -97,7 +169,7 @@ var upgrader = websocket.Upgrader{
// @Description find booking by id // @Description find booking by id
// @Param id path string true "the id you want to get" // @Param id path string true "the id you want to get"
// @Success 200 {booking} models.booking // @Success 200 {booking} models.booking
// @router /:id [get] // @router /logs/:id [get]
func (o *DatacenterController) Log() { func (o *DatacenterController) Log() {
// user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) // user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id") id := o.Ctx.Input.Param(":id")

2
go.mod
View File

@@ -15,7 +15,7 @@ require (
) )
require ( require (
cloud.o-forge.io/core/oc-lib v0.0.0-20260325092016-4580200e8057 // indirect cloud.o-forge.io/core/oc-lib v0.0.0-20260408134044-284533ad1d7b // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/biter777/countries v1.7.5 // indirect github.com/biter777/countries v1.7.5 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect

4
go.sum
View File

@@ -14,6 +14,10 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20260324114937-6d0c78946e8b h1:y0rppyzGIQTIy
cloud.o-forge.io/core/oc-lib v0.0.0-20260324114937-6d0c78946e8b/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA= cloud.o-forge.io/core/oc-lib v0.0.0-20260324114937-6d0c78946e8b/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
cloud.o-forge.io/core/oc-lib v0.0.0-20260325092016-4580200e8057 h1:pR+lZzcCWZ0kke2r2xXa7OpdbLpPW3gZSWZ8gGHh274= cloud.o-forge.io/core/oc-lib v0.0.0-20260325092016-4580200e8057 h1:pR+lZzcCWZ0kke2r2xXa7OpdbLpPW3gZSWZ8gGHh274=
cloud.o-forge.io/core/oc-lib v0.0.0-20260325092016-4580200e8057/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA= cloud.o-forge.io/core/oc-lib v0.0.0-20260325092016-4580200e8057/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
cloud.o-forge.io/core/oc-lib v0.0.0-20260407090927-6fe91eda875d h1:54Vl14gurwAkmZEaWZKUM5eDZfB7MF/fzWjibWLQljE=
cloud.o-forge.io/core/oc-lib v0.0.0-20260407090927-6fe91eda875d/go.mod h1:+ENuvBfZdESSvecoqGY/wSvRlT3vinEolxKgwbOhUpA=
cloud.o-forge.io/core/oc-lib v0.0.0-20260408134044-284533ad1d7b h1:mOU+tc87/KEQgFmw1RcQ9E9Rbz8Q2jLOh5Cpu6po9Ww=
cloud.o-forge.io/core/oc-lib v0.0.0-20260408134044-284533ad1d7b/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/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=

View File

@@ -274,7 +274,7 @@ func provisionPVCsForTarget(ctx context.Context, targetNS string, sourceExecutio
"executions_id": {{Operator: dbs.EQUAL.String(), Value: sourceExecutionsID}}, "executions_id": {{Operator: dbs.EQUAL.String(), Value: sourceExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 1000)
if res.Err != "" || len(res.Data) == 0 { if res.Err != "" || len(res.Data) == 0 {
return return
@@ -424,7 +424,7 @@ func (s *AdmiraltySetter) TeardownIfRemote(exec *workflow_execution.WorkflowExec
"executions_id": {{Operator: dbs.EQUAL.String(), Value: exec.ExecutionsID}}, "executions_id": {{Operator: dbs.EQUAL.String(), Value: exec.ExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.COMPUTE_RESOURCE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.COMPUTE_RESOURCE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 1000)
if res.Err != "" || len(res.Data) == 0 { if res.Err != "" || len(res.Data) == 0 {
return return

View File

@@ -29,7 +29,7 @@ func BootstrapAllowedImages() {
And: map[string][]dbs.Filter{ And: map[string][]dbs.Filter{
"image": {{Operator: dbs.EQUAL.String(), Value: img.Image}}, "image": {{Operator: dbs.EQUAL.String(), Value: img.Image}},
}, },
}, "", false) }, "", false, 0, 1)
if existing.Err != "" || len(existing.Data) > 0 { if existing.Err != "" || len(existing.Data) > 0 {
continue // déjà présente ou erreur de recherche : on passe continue // déjà présente ou erreur de recherche : on passe
} }

View File

@@ -1,112 +0,0 @@
package infrastructure
import (
"encoding/json"
"fmt"
"sync"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/dbs"
bookingmodel "cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/tools"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// processedBookings tracks booking IDs already handled this process lifetime.
var processedBookings sync.Map
// closingStates is the set of terminal booking states.
var ClosingStates = map[enum.BookingStatus]bool{
enum.FAILURE: true,
enum.SUCCESS: true,
enum.FORGOTTEN: true,
enum.CANCELLED: true,
}
// WatchBookings is a safety-net fallback for when oc-monitord fails to launch.
// It detects bookings that are past expected_start_date by at least 1 minute and
// are still in a non-terminal state. Instead of writing to the database directly,
// it emits WORKFLOW_STEP_DONE_EVENT with State=FAILURE on NATS so that oc-scheduler
// handles the state transition — keeping a single source of truth for booking state.
//
// Must be launched in a goroutine from main.
func WatchBookings() {
logger := oclib.GetLogger()
logger.Info().Msg("BookingWatchdog: started")
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for range ticker.C {
if err := scanStaleBookings(); err != nil {
logger.Error().Msg("BookingWatchdog: " + err.Error())
}
}
}
// scanStaleBookings queries all bookings whose ExpectedStartDate passed more than
// 1 minute ago. Non-terminal ones get a WORKFLOW_STEP_DONE_EVENT FAILURE emitted
// on NATS so oc-scheduler closes them.
func scanStaleBookings() error {
myself, err := oclib.GetMySelf()
if err != nil {
return fmt.Errorf("could not resolve local peer: %w", err)
}
peerID := myself.GetID()
deadline := time.Now().UTC().Add(-time.Minute)
res := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), "", peerID, []string{}, nil).
Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"expected_start_date": {{
Operator: dbs.LTE.String(),
Value: primitive.NewDateTimeFromTime(deadline),
}},
},
}, "", false)
if res.Err != "" {
return fmt.Errorf("stale booking search failed: %s", res.Err)
}
for _, dbo := range res.Data {
b, ok := dbo.(*bookingmodel.Booking)
if !ok {
continue
}
go emitWatchdogFailure(b)
}
return nil
}
// emitWatchdogFailure publishes a WORKFLOW_STEP_DONE_EVENT FAILURE for a stale
// booking. oc-scheduler is the single authority for booking state transitions.
func emitWatchdogFailure(b *bookingmodel.Booking) {
logger := oclib.GetLogger()
if _, done := processedBookings.Load(b.GetID()); done {
return
}
if ClosingStates[b.State] {
processedBookings.Store(b.GetID(), struct{}{})
return
}
now := time.Now().UTC()
payload, err := json.Marshal(tools.WorkflowLifecycleEvent{
BookingID: b.GetID(),
State: enum.FAILURE.EnumIndex(),
RealEnd: &now,
})
if err != nil {
return
}
tools.NewNATSCaller().SetNATSPub(tools.WORKFLOW_STEP_DONE_EVENT, tools.NATSResponse{
FromApp: "oc-datacenter",
Method: int(tools.WORKFLOW_STEP_DONE_EVENT),
Payload: payload,
})
logger.Info().Msgf("BookingWatchdog: booking %s stale → emitting FAILURE", b.GetID())
processedBookings.Store(b.GetID(), struct{}{})
}

View File

@@ -152,7 +152,7 @@ func (s *KubernetesService) filterNonAllowed(images []string) []string {
And: map[string][]dbs.Filter{ And: map[string][]dbs.Filter{
"image": {{Operator: dbs.EQUAL.String(), Value: name}}, "image": {{Operator: dbs.EQUAL.String(), Value: name}},
}, },
}, "", false) }, "", false, 0, 1000)
if len(res.Data) == 0 { if len(res.Data) == 0 {
toRemove = append(toRemove, img) toRemove = append(toRemove, img)

View File

@@ -57,7 +57,7 @@ func Call(book *booking.Booking,
"source": {{Operator: dbs.EQUAL.String(), Value: instance.Source}}, "source": {{Operator: dbs.EQUAL.String(), Value: instance.Source}},
"abstractlive.resources_id": {{Operator: dbs.EQUAL.String(), Value: computeRes.GetID()}}, "abstractlive.resources_id": {{Operator: dbs.EQUAL.String(), Value: computeRes.GetID()}},
}, },
}, "", false) }, "", false, 0, 1000)
if res.Err != "" { if res.Err != "" {
continue continue
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"oc-datacenter/infrastructure"
"oc-datacenter/infrastructure/admiralty" "oc-datacenter/infrastructure/admiralty"
"oc-datacenter/infrastructure/kubernetes" "oc-datacenter/infrastructure/kubernetes"
"oc-datacenter/infrastructure/kubernetes/models" "oc-datacenter/infrastructure/kubernetes/models"
@@ -237,7 +238,7 @@ func ListenNATS() {
if err := json.Unmarshal(resp.Payload, &evt); err != nil || evt.ExecutionsID == "" { if err := json.Unmarshal(resp.Payload, &evt); err != nil || evt.ExecutionsID == "" {
return return
} }
go kubernetes.NewKubernetesService(evt.ExecutionsID).TeardownForExecution(evt.ExecutionID) go infrastructure.TeardownForExecution(evt.ExecutionID, evt.ExecutionsID)
}, },
// ─── REMOVE_RESOURCE ──────────────────────────────────────────────────────── // ─── REMOVE_RESOURCE ────────────────────────────────────────────────────────

View File

@@ -298,7 +298,7 @@ func (m *MinioSetter) TeardownAsSource(ctx context.Context, event MinioDeleteEve
// loadMinioURL searches through all live storages accessible by peerID to find // loadMinioURL searches through all live storages accessible by peerID to find
// the one that references MinioID, and returns its endpoint URL. // the one that references MinioID, and returns its endpoint URL.
func (m *MinioSetter) loadMinioURL(peerID string) (string, error) { func (m *MinioSetter) loadMinioURL(peerID string) (string, error) {
res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false) res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false, 0, 10000)
if res.Err != "" { if res.Err != "" {
return "", fmt.Errorf("loadMinioURL: failed to load live storages: %s", res.Err) return "", fmt.Errorf("loadMinioURL: failed to load live storages: %s", res.Err)
} }
@@ -324,7 +324,7 @@ func (m *MinioSetter) TeardownForExecution(ctx context.Context, localPeerID stri
"executions_id": {{Operator: dbs.EQUAL.String(), Value: m.ExecutionsID}}, "executions_id": {{Operator: dbs.EQUAL.String(), Value: m.ExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 { if res.Err != "" || len(res.Data) == 0 {
return return

View File

@@ -160,7 +160,7 @@ func (p *PVCSetter) TeardownAsSource(ctx context.Context, event PVCDeleteEvent)
// ResolveStorageName returns the live storage name for a given storageID, or "" if not found. // ResolveStorageName returns the live storage name for a given storageID, or "" if not found.
func ResolveStorageName(storageID, peerID string) string { func ResolveStorageName(storageID, peerID string) string {
res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false) res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false, 0, 10000)
if res.Err != "" { if res.Err != "" {
return "" return ""
} }
@@ -175,7 +175,7 @@ func ResolveStorageName(storageID, peerID string) string {
// loadStorageSize looks up the SizeGB for this storage in live storages. // loadStorageSize looks up the SizeGB for this storage in live storages.
func (p *PVCSetter) loadStorageSize(peerID string) (string, error) { func (p *PVCSetter) loadStorageSize(peerID string) (string, error) {
res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false) res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), "", peerID, []string{}, nil).LoadAll(false, 0, 10000)
if res.Err != "" { if res.Err != "" {
return "", fmt.Errorf("loadStorageSize: %s", res.Err) return "", fmt.Errorf("loadStorageSize: %s", res.Err)
} }
@@ -199,7 +199,7 @@ func (p *PVCSetter) TeardownForExecution(ctx context.Context, localPeerID string
"executions_id": {{Operator: dbs.EQUAL.String(), Value: p.ExecutionsID}}, "executions_id": {{Operator: dbs.EQUAL.String(), Value: p.ExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 { if res.Err != "" || len(res.Data) == 0 {
return return

View File

@@ -1,27 +1,126 @@
package kubernetes package infrastructure
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"oc-datacenter/conf"
"oc-datacenter/infrastructure/admiralty"
"oc-datacenter/infrastructure/kubernetes"
"oc-datacenter/infrastructure/storage"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"oc-datacenter/conf"
"oc-datacenter/infrastructure"
"oc-datacenter/infrastructure/admiralty"
"oc-datacenter/infrastructure/storage"
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"
bookingmodel "cloud.o-forge.io/core/oc-lib/models/booking" bookingmodel "cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/models/workflow_execution" "cloud.o-forge.io/core/oc-lib/models/workflow_execution"
"cloud.o-forge.io/core/oc-lib/tools" "cloud.o-forge.io/core/oc-lib/tools"
"go.mongodb.org/mongo-driver/bson/primitive"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// uuidNsPattern matches Kubernetes namespace names that are execution UUIDs. // processedBookings tracks booking IDs already handled this process lifetime.
var processedBookings sync.Map
// closingStates is the set of terminal booking states.
var ClosingStates = map[enum.BookingStatus]bool{
enum.FAILURE: true,
enum.SUCCESS: true,
enum.FORGOTTEN: true,
enum.CANCELLED: true,
}
// WatchBookings is a safety-net fallback for when oc-monitord fails to launch.
// It detects bookings that are past expected_start_date by at least 1 minute and
// are still in a non-terminal state. Instead of writing to the database directly,
// it emits WORKFLOW_STEP_DONE_EVENT with State=FAILURE on NATS so that oc-scheduler
// handles the state transition — keeping a single source of truth for booking state.
//
// Must be launched in a goroutine from main.
func WatchBookings() {
logger := oclib.GetLogger()
logger.Info().Msg("BookingWatchdog: started")
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for range ticker.C {
if err := scanStaleBookings(); err != nil {
logger.Error().Msg("BookingWatchdog: " + err.Error())
}
}
}
// scanStaleBookings queries all bookings whose ExpectedStartDate passed more than
// 1 minute ago. Non-terminal ones get a WORKFLOW_STEP_DONE_EVENT FAILURE emitted
// on NATS so oc-scheduler closes them.
func scanStaleBookings() error {
myself, err := oclib.GetMySelf()
if err != nil {
return fmt.Errorf("could not resolve local peer: %w", err)
}
peerID := myself.GetID()
deadline := time.Now().UTC().Add(-time.Minute)
res := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), "", peerID, []string{}, nil).
Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"expected_start_date": {{
Operator: dbs.LTE.String(),
Value: primitive.NewDateTimeFromTime(deadline),
}},
},
}, "", false, 0, 10000)
if res.Err != "" {
return fmt.Errorf("stale booking search failed: %s", res.Err)
}
for _, dbo := range res.Data {
b, ok := dbo.(*bookingmodel.Booking)
if !ok {
continue
}
go emitWatchdogFailure(b)
}
return nil
}
// emitWatchdogFailure publishes a WORKFLOW_STEP_DONE_EVENT FAILURE for a stale
// booking. oc-scheduler is the single authority for booking state transitions.
func emitWatchdogFailure(b *bookingmodel.Booking) {
logger := oclib.GetLogger()
if _, done := processedBookings.Load(b.GetID()); done {
return
}
if ClosingStates[b.State] {
processedBookings.Store(b.GetID(), struct{}{})
return
}
now := time.Now().UTC()
payload, err := json.Marshal(tools.WorkflowLifecycleEvent{
BookingID: b.GetID(),
State: enum.FAILURE.EnumIndex(),
RealEnd: &now,
})
if err != nil {
return
}
tools.NewNATSCaller().SetNATSPub(tools.WORKFLOW_STEP_DONE_EVENT, tools.NATSResponse{
FromApp: "oc-datacenter",
Method: int(tools.WORKFLOW_STEP_DONE_EVENT),
Payload: payload,
})
logger.Info().Msgf("BookingWatchdog: booking %s stale → emitting FAILURE", b.GetID())
processedBookings.Store(b.GetID(), struct{}{})
}
var uuidNsPattern = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) var uuidNsPattern = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
// WatchInfra is a safety-net watchdog that periodically scans Kubernetes for // WatchInfra is a safety-net watchdog that periodically scans Kubernetes for
@@ -30,22 +129,22 @@ var uuidNsPattern = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-
// missed due to oc-monitord or oc-datacenter crash/restart). // missed due to oc-monitord or oc-datacenter crash/restart).
// //
// Must be launched in a goroutine from main. // Must be launched in a goroutine from main.
func (s *KubernetesService) Watch() { func Watch() {
logger := oclib.GetLogger() logger := oclib.GetLogger()
logger.Info().Msg("InfraWatchdog: started") logger.Info().Msg("InfraWatchdog: started")
ticker := time.NewTicker(5 * time.Minute) ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop() defer ticker.Stop()
for range ticker.C { for range ticker.C {
if err := s.scanOrphaned(); err != nil { if err := scanOrphaned(); err != nil {
logger.Error().Msg("InfraWatchdog: " + err.Error()) logger.Error().Msg("InfraWatchdog: " + err.Error())
} }
if err := s.scanOrphanedMinio(); err != nil { if err := scanOrphanedMinio(); err != nil {
logger.Error().Msg("InfraWatchdog(minio): " + err.Error()) logger.Error().Msg("InfraWatchdog(minio): " + err.Error())
} }
if err := s.scanOrphanedAdmiraltyNodes(); err != nil { if err := scanOrphanedAdmiraltyNodes(); err != nil {
logger.Error().Msg("InfraWatchdog(admiralty-nodes): " + err.Error()) logger.Error().Msg("InfraWatchdog(admiralty-nodes): " + err.Error())
} }
if err := s.scanOrphanedPVC(); err != nil { if err := scanOrphanedPVC(); err != nil {
logger.Error().Msg("InfraWatchdog(pvc): " + err.Error()) logger.Error().Msg("InfraWatchdog(pvc): " + err.Error())
} }
} }
@@ -54,7 +153,7 @@ func (s *KubernetesService) Watch() {
// scanOrphanedInfra lists all UUID-named Kubernetes namespaces, looks up their // scanOrphanedInfra lists all UUID-named Kubernetes namespaces, looks up their
// WorkflowExecution in the DB, and triggers teardown for any that are in a // WorkflowExecution in the DB, and triggers teardown for any that are in a
// terminal state. Namespaces already in Terminating phase are skipped. // terminal state. Namespaces already in Terminating phase are skipped.
func (s *KubernetesService) scanOrphaned() error { func scanOrphaned() error {
logger := oclib.GetLogger() logger := oclib.GetLogger()
serv, err := tools.NewKubernetesService( serv, err := tools.NewKubernetesService(
@@ -98,7 +197,7 @@ func (s *KubernetesService) scanOrphaned() error {
logger.Info().Msgf("InfraWatchdog: orphaned infra detected for execution %s (state=%v) → teardown", logger.Info().Msgf("InfraWatchdog: orphaned infra detected for execution %s (state=%v) → teardown",
executionsID, exec.State) executionsID, exec.State)
go s.TeardownForExecution(exec.GetID()) go TeardownForExecution(exec.GetID(), exec.ExecutionsID)
} }
return nil return nil
} }
@@ -107,7 +206,7 @@ func (s *KubernetesService) scanOrphaned() error {
// terminal state and triggers Minio teardown for each unique executionsID found. // terminal state and triggers Minio teardown for each unique executionsID found.
// This covers the case where the Kubernetes namespace is already gone (manual // This covers the case where the Kubernetes namespace is already gone (manual
// deletion, prior partial teardown) but Minio SA and bucket were never revoked. // deletion, prior partial teardown) but Minio SA and bucket were never revoked.
func (s *KubernetesService) scanOrphanedMinio() error { func scanOrphanedMinio() error {
logger := oclib.GetLogger() logger := oclib.GetLogger()
myself, err := oclib.GetMySelf() myself, err := oclib.GetMySelf()
@@ -121,7 +220,7 @@ func (s *KubernetesService) scanOrphanedMinio() error {
And: map[string][]dbs.Filter{ And: map[string][]dbs.Filter{
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 10000)
if res.Err != "" { if res.Err != "" {
return fmt.Errorf("failed to search LIVE_STORAGE bookings: %s", res.Err) return fmt.Errorf("failed to search LIVE_STORAGE bookings: %s", res.Err)
@@ -175,7 +274,7 @@ func (s *KubernetesService) scanOrphanedMinio() error {
// This covers the gap where the namespace is already gone (or Terminating) but // This covers the gap where the namespace is already gone (or Terminating) but
// the virtual node was never cleaned up by the Admiralty controller — which can // the virtual node was never cleaned up by the Admiralty controller — which can
// happen when the node goes NotReady before the AdmiraltyTarget CRD is deleted. // happen when the node goes NotReady before the AdmiraltyTarget CRD is deleted.
func (s *KubernetesService) scanOrphanedAdmiraltyNodes() error { func scanOrphanedAdmiraltyNodes() error {
logger := oclib.GetLogger() logger := oclib.GetLogger()
serv, err := tools.NewKubernetesService( serv, err := tools.NewKubernetesService(
@@ -251,7 +350,7 @@ func (s *KubernetesService) scanOrphanedAdmiraltyNodes() error {
// //
// A LIVE_STORAGE booking is treated as a local PVC only when ResolveStorageName // A LIVE_STORAGE booking is treated as a local PVC only when ResolveStorageName
// returns a non-empty name — the same guard used by teardownPVCForExecution. // returns a non-empty name — the same guard used by teardownPVCForExecution.
func (s *KubernetesService) scanOrphanedPVC() error { func scanOrphanedPVC() error {
logger := oclib.GetLogger() logger := oclib.GetLogger()
myself, err := oclib.GetMySelf() myself, err := oclib.GetMySelf()
@@ -265,7 +364,7 @@ func (s *KubernetesService) scanOrphanedPVC() error {
And: map[string][]dbs.Filter{ And: map[string][]dbs.Filter{
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}}, "resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
}, },
}, "", false) }, "", false, 0, 10000)
if res.Err != "" { if res.Err != "" {
return fmt.Errorf("failed to search LIVE_STORAGE bookings: %s", res.Err) return fmt.Errorf("failed to search LIVE_STORAGE bookings: %s", res.Err)
@@ -314,7 +413,7 @@ func findTerminalExecution(executionsID string, peerID string) *workflow_executi
And: map[string][]dbs.Filter{ And: map[string][]dbs.Filter{
"executions_id": {{Operator: dbs.EQUAL.String(), Value: executionsID}}, "executions_id": {{Operator: dbs.EQUAL.String(), Value: executionsID}},
}, },
}, "", false) }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 { if res.Err != "" || len(res.Data) == 0 {
return nil return nil
@@ -325,7 +424,7 @@ func findTerminalExecution(executionsID string, peerID string) *workflow_executi
return nil return nil
} }
if !infrastructure.ClosingStates[exec.State] { if !ClosingStates[exec.State] {
return nil return nil
} }
return exec return exec
@@ -334,7 +433,7 @@ func findTerminalExecution(executionsID string, peerID string) *workflow_executi
// teardownInfraForExecution handles infrastructure cleanup when a workflow terminates. // teardownInfraForExecution handles infrastructure cleanup when a workflow terminates.
// oc-datacenter is responsible only for infra here — booking/execution state // oc-datacenter is responsible only for infra here — booking/execution state
// is managed by oc-scheduler. // is managed by oc-scheduler.
func (s *KubernetesService) TeardownForExecution(executionID string) { func TeardownForExecution(executionID string, executionsID string) {
logger := oclib.GetLogger() logger := oclib.GetLogger()
myself, err := oclib.GetMySelf() myself, err := oclib.GetMySelf()
@@ -352,8 +451,8 @@ func (s *KubernetesService) TeardownForExecution(executionID string) {
exec := res.(*workflow_execution.WorkflowExecution) exec := res.(*workflow_execution.WorkflowExecution)
ctx := context.Background() ctx := context.Background()
admiralty.NewAdmiraltySetter(s.ExecutionsID).TeardownIfRemote(exec, selfPeerID) admiralty.NewAdmiraltySetter(executionsID).TeardownIfRemote(exec, selfPeerID)
storage.NewMinioSetter(s.ExecutionsID, "").TeardownForExecution(ctx, selfPeerID) storage.NewMinioSetter(executionsID, "").TeardownForExecution(ctx, selfPeerID)
storage.NewPVCSetter(s.ExecutionsID, "").TeardownForExecution(ctx, selfPeerID) storage.NewPVCSetter(executionsID, "").TeardownForExecution(ctx, selfPeerID)
s.CleanupImages(ctx) kubernetes.NewKubernetesService(executionsID).CleanupImages(ctx)
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"oc-datacenter/conf" "oc-datacenter/conf"
"oc-datacenter/infrastructure" "oc-datacenter/infrastructure"
"oc-datacenter/infrastructure/nats"
_ "oc-datacenter/routers" _ "oc-datacenter/routers"
oclib "cloud.o-forge.io/core/oc-lib" oclib "cloud.o-forge.io/core/oc-lib"
@@ -30,9 +31,9 @@ func main() {
infrastructure.BootstrapAllowedImages() infrastructure.BootstrapAllowedImages()
go infrastructure.ListenNATS() go nats.ListenNATS()
go infrastructure.WatchBookings() go infrastructure.WatchBookings()
go infrastructure.WatchInfra() go infrastructure.Watch()
beego.Run() beego.Run()
} }

Binary file not shown.

View File

@@ -7,43 +7,7 @@ import (
func init() { func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"],
beego.ControllerComments{
Method: "GetKubeSecret",
Router: `/secret/:execution/:peer`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "GetAllTargets",
Router: `/targets`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "GetOneTarget",
Router: `/targets/:execution`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "DeleteAdmiraltySession",
Router: `/targets/:execution`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "GetAll", Method: "GetAll",
Router: `/`, Router: `/`,
@@ -52,7 +16,7 @@ func init() {
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Post", Method: "Post",
Router: `/`, Router: `/`,
@@ -61,7 +25,7 @@ func init() {
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Get", Method: "Get",
Router: `/:id`, Router: `/:id`,
@@ -70,65 +34,11 @@ func init() {
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"], beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Log", Method: "Delete",
Router: `/:id`, Router: `/:id`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "Put",
Router: `/:id`,
AllowHTTPMethods: []string{"put"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "Check",
Router: `/check/:id/:start_date/:end_date`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "ExtendForExecution",
Router: `/extend/:resource_id/from_execution/:execution_id/to/:duration`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "ExtendForNamespace",
Router: `/extend/:resource_id/from_namespace/:namespace/to/:duration`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/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-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "ExecutionSearch",
Router: `/search/execution/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
@@ -145,17 +55,35 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"], beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "Get", Method: "Get",
Router: `/:id`, Router: `/:type/:id`,
AllowHTTPMethods: []string{"get"}, AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"], beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "CreateServiceAccount", Method: "Delete",
Router: `/serviceaccount/:minioId/:executions`, Router: `/:type/:id`,
AllowHTTPMethods: []string{"post"}, AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"],
beego.ControllerComments{
Method: "Search",
Router: `/:type/search/:search`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"],
beego.ControllerComments{
Method: "Log",
Router: `/logs/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(), MethodParams: param.Make(),
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
@@ -169,15 +97,6 @@ func init() {
Filters: nil, Filters: nil,
Params: nil}) Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:VectorController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:VectorController"],
beego.ControllerComments{
Method: "Receive",
Router: `/`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:VersionController"], beego.GlobalControllerRouter["oc-datacenter/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:VersionController"],
beego.ControllerComments{ beego.ControllerComments{
Method: "GetAll", Method: "GetAll",

View File

@@ -14,11 +14,10 @@ import (
) )
func init() { func init() {
ns := beego.NewNamespace("/oc/", ns := beego.NewNamespace("/oc",
beego.NSInclude( beego.NSInclude(
&controllers.DatacenterController{}, &controllers.DatacenterController{},
), ),
beego.NSNamespace("/session", beego.NSNamespace("/session",
beego.NSInclude( beego.NSInclude(
&controllers.SessionController{}, &controllers.SessionController{},

View File

@@ -39,7 +39,7 @@
window.onload = function() { window.onload = function() {
// Begin Swagger UI call region // Begin Swagger UI call region
const ui = SwaggerUIBundle({ const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json", url: "swagger.json",
dom_id: '#swagger-ui', dom_id: '#swagger-ui',
deepLinking: true, deepLinking: true,
presets: [ presets: [

View File

@@ -13,7 +13,7 @@
"url": "https://www.gnu.org/licenses/agpl-3.0.html" "url": "https://www.gnu.org/licenses/agpl-3.0.html"
} }
}, },
"basePath": "/oc/", "basePath": "/oc",
"paths": { "paths": {
"/": { "/": {
"get": { "get": {
@@ -23,11 +23,30 @@
"description": "find booking by id\n\u003cbr\u003e", "description": "find booking by id\n\u003cbr\u003e",
"operationId": "DatacenterController.GetAll", "operationId": "DatacenterController.GetAll",
"parameters": [ "parameters": [
{
"in": "path",
"name": "type",
"description": "the word type you want to get",
"required": true,
"type": "string"
},
{ {
"in": "query", "in": "query",
"name": "is_draft", "name": "is_draft",
"description": "draft wished", "description": "draft wished",
"type": "string" "type": "string"
},
{
"in": "query",
"name": "offset",
"description": "false",
"type": "string"
},
{
"in": "query",
"name": "limit",
"description": "false",
"type": "string"
} }
], ],
"responses": { "responses": {
@@ -37,222 +56,24 @@
} }
} }
}, },
"/admiralty/kubeconfig/{execution}": { "/allowed-image/": {
"get": { "get": {
"tags": [ "tags": [
"admiralty" "allowed-image"
], ],
"parameters": [ "description": "Retourne toutes les images autorisées à persister sur ce peer\n\u003cbr\u003e",
{ "operationId": "AllowedImageController.GetAll",
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/admiralty/node/{execution}": {
"get": {
"tags": [
"admiralty"
],
"parameters": [
{
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/admiralty/secret/{execution}": {
"get": {
"tags": [
"admiralty"
],
"parameters": [
{
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": ""
}
}
},
"post": {
"tags": [
"admiralty"
],
"parameters": [
{
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "kubeconfig",
"description": "Kubeconfig to use when creating secret",
"required": true,
"schema": {
"$ref": "#/definitions/controllers.RemoteKubeconfig"
}
}
],
"responses": {
"201": {
"description": ""
}
}
}
},
"/admiralty/source/{execution}": {
"post": {
"tags": [
"admiralty"
],
"description": "Create an Admiralty Source on remote cluster\n\u003cbr\u003e",
"operationId": "AdmiraltyController.CreateSource",
"parameters": [
{
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
}
],
"responses": {
"201": {
"description": ""
}
}
}
},
"/admiralty/target/{execution}": {
"post": {
"tags": [
"admiralty"
],
"description": "Create an Admiralty Target in the namespace associated to the executionID\n\u003cbr\u003e",
"operationId": "AdmiraltyController.CreateAdmiraltyTarget",
"parameters": [
{
"in": "path",
"name": "execution",
"description": "execution id of the workflow",
"required": true,
"type": "string"
}
],
"responses": {
"201": {
"description": ""
}
}
}
},
"/admiralty/targets": {
"get": {
"tags": [
"admiralty"
],
"description": "find all Admiralty Target\n\u003cbr\u003e",
"operationId": "AdmiraltyController.GetAllTargets",
"responses": {
"200": {
"description": ""
}
}
}
},
"/admiralty/targets/{execution}": {
"get": {
"tags": [
"admiralty"
],
"description": "find one Admiralty Target\n\u003cbr\u003e",
"operationId": "AdmiraltyController.GetOneTarget",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the name of the target to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/booking/": {
"get": {
"tags": [
"booking"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "BookingController.GetAll",
"parameters": [ "parameters": [
{ {
"in": "query", "in": "query",
"name": "is_draft", "name": "offset",
"description": "draft wished", "description": "false",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
},
"post": {
"tags": [
"booking"
],
"description": "create booking\n\u003cbr\u003e",
"operationId": "BookingController.Post.",
"parameters": [
{
"in": "body",
"name": "booking",
"description": "the booking you want to post",
"required": true,
"schema": {
"type": "string"
},
"type": "string" "type": "string"
}, },
{ {
"in": "query", "in": "query",
"name": "is_draft", "name": "limit",
"description": "draft wished", "description": "false",
"type": "string" "type": "string"
} }
], ],
@@ -260,44 +81,54 @@
"200": { "200": {
"description": "", "description": "",
"schema": { "schema": {
"$ref": "#/definitions/models.object" "type": "array",
"items": {
"$ref": "#/definitions/allowed_image.AllowedImage"
} }
} }
} }
} }
}, },
"/booking/check/{id}/{start_date}/{end_date}": { "post": {
"tags": [
"allowed-image"
],
"description": "Ajoute une image à la liste des images autorisées (peer admin uniquement)\n\u003cbr\u003e",
"operationId": "AllowedImageController.Post",
"parameters": [
{
"in": "body",
"name": "body",
"description": "Image à autoriser",
"required": true,
"schema": {
"$ref": "#/definitions/allowed_image.AllowedImage"
}
}
],
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/allowed_image.AllowedImage"
}
}
}
}
},
"/allowed-image/{id}": {
"get": { "get": {
"tags": [ "tags": [
"booking" "allowed-image"
], ],
"description": "check booking\n\u003cbr\u003e", "description": "Retourne une image autorisée par son ID\n\u003cbr\u003e",
"operationId": "BookingController.Check", "operationId": "AllowedImageController.Get",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "id", "name": "id",
"description": "id of the datacenter", "description": "ID de l'image autorisée",
"type": "string" "required": true,
},
{
"in": "path",
"name": "start_date",
"description": "2006-01-02T15:04:05",
"type": "string",
"default": "the booking start date"
},
{
"in": "path",
"name": "end_date",
"description": "2006-01-02T15:04:05",
"type": "string",
"default": "the booking end date"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string" "type": "string"
} }
], ],
@@ -305,84 +136,43 @@
"200": { "200": {
"description": "", "description": "",
"schema": { "schema": {
"$ref": "#/definitions/models.object" "$ref": "#/definitions/allowed_image.AllowedImage"
}
} }
} }
} }
}, },
"/booking/search/execution/{id}": { "delete": {
"get": {
"tags": [ "tags": [
"booking" "allowed-image"
], ],
"description": "search bookings by execution\n\u003cbr\u003e", "description": "Supprime une image de la liste des images autorisées (peer admin uniquement, entrées bootstrap non supprimables)\n\u003cbr\u003e",
"operationId": "BookingController.Search", "operationId": "AllowedImageController.Delete",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "id", "name": "id",
"description": "id execution", "description": "ID de l'image autorisée",
"required": true, "required": true,
"type": "string" "type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "{workspace} models.workspace" "description": "",
"schema": {
"$ref": "#/definitions/allowed_image.AllowedImage"
}
} }
} }
} }
}, },
"/booking/search/{start_date}/{end_date}": { "/logs/{id}": {
"get": { "get": {
"tags": [ "tags": [
"booking" "oc-datacenter/controllersDatacenterController"
],
"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", "description": "find booking by id\n\u003cbr\u003e",
"operationId": "BookingController.Get", "operationId": "DatacenterController.Log",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
@@ -397,36 +187,6 @@
"description": "{booking} models.booking" "description": "{booking} models.booking"
} }
} }
},
"put": {
"tags": [
"booking"
],
"description": "create computes\n\u003cbr\u003e",
"operationId": "BookingController.Update",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the compute id you want to get",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The compute content",
"required": true,
"schema": {
"$ref": "#/definitions/models.compute"
}
}
],
"responses": {
"200": {
"description": "{compute} models.compute"
}
}
} }
}, },
"/session/token/{id}/{duration}": { "/session/token/{id}/{duration}": {
@@ -485,7 +245,55 @@
} }
} }
}, },
"/{id}": { "/{type}/search/{search}": {
"get": {
"tags": [
"oc-datacenter/controllersDatacenterController"
],
"description": "search datacenter\n\u003cbr\u003e",
"operationId": "DatacenterController.Search",
"parameters": [
{
"in": "path",
"name": "type",
"description": "the type you want to get",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "search",
"description": "the word search you want to get",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
},
{
"in": "query",
"name": "offset",
"description": "false",
"type": "string"
},
{
"in": "query",
"name": "limit",
"description": "false",
"type": "string"
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
}
},
"/{type}/{id}": {
"get": { "get": {
"tags": [ "tags": [
"oc-datacenter/controllersDatacenterController" "oc-datacenter/controllersDatacenterController"
@@ -500,6 +308,47 @@
"required": true, "required": true,
"type": "string" "type": "string"
}, },
{
"in": "path",
"name": "type",
"description": "the word type you want to get",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
},
"delete": {
"tags": [
"oc-datacenter/controllersDatacenterController"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "DatacenterController.Delete",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "type",
"description": "the word type you want to get",
"required": true,
"type": "string"
},
{ {
"in": "query", "in": "query",
"name": "is_draft", "name": "is_draft",
@@ -516,21 +365,8 @@
} }
}, },
"definitions": { "definitions": {
"controllers.RemoteKubeconfig": { "allowed_image.AllowedImage": {
"title": "RemoteKubeconfig", "title": "AllowedImage",
"type": "object",
"properties": {
"Data": {
"type": "string"
}
}
},
"models.compute": {
"title": "compute",
"type": "object"
},
"models.object": {
"title": "object",
"type": "object" "type": "object"
} }
}, },
@@ -539,17 +375,13 @@
"name": "oc-datacenter/controllersDatacenterController", "name": "oc-datacenter/controllersDatacenterController",
"description": "Operations about workspace\n" "description": "Operations about workspace\n"
}, },
{
"name": "booking",
"description": "Operations about workspace\n"
},
{ {
"name": "version", "name": "version",
"description": "VersionController operations for Version\n" "description": "VersionController operations for Version\n"
}, },
{ {
"name": "admiralty", "name": "allowed-image",
"description": "Operations about the admiralty objects of the datacenter\n" "description": "AllowedImageController gère la liste locale des images autorisées à persister\nsur ce peer après l'exécution d'un workflow.\n\nGET /allowed-image/ → tous les utilisateurs authentifiés\nGET /allowed-image/:id → tous les utilisateurs authentifiés\nPOST /allowed-image/ → peer admin uniquement\nDELETE /allowed-image/:id → peer admin uniquement (bloqué si IsDefault)\n"
} }
] ]
} }

View File

@@ -10,7 +10,7 @@ info:
license: license:
name: AGPL name: AGPL
url: https://www.gnu.org/licenses/agpl-3.0.html url: https://www.gnu.org/licenses/agpl-3.0.html
basePath: /oc/ basePath: /oc
paths: paths:
/: /:
get: get:
@@ -21,14 +21,27 @@ paths:
<br> <br>
operationId: DatacenterController.GetAll operationId: DatacenterController.GetAll
parameters: parameters:
- in: path
name: type
description: the word type you want to get
required: true
type: string
- in: query - in: query
name: is_draft name: is_draft
description: draft wished description: draft wished
type: string type: string
- in: query
name: offset
description: "false"
type: string
- in: query
name: limit
description: "false"
type: string
responses: responses:
"200": "200":
description: '{booking} models.booking' description: '{booking} models.booking'
/{id}: /{type}/{id}:
get: get:
tags: tags:
- oc-datacenter/controllersDatacenterController - oc-datacenter/controllersDatacenterController
@@ -42,6 +55,11 @@ paths:
description: the id you want to get description: the id you want to get
required: true required: true
type: string type: string
- in: path
name: type
description: the word type you want to get
required: true
type: string
- in: query - in: query
name: is_draft name: is_draft
description: draft wished description: draft wished
@@ -49,134 +67,24 @@ paths:
responses: responses:
"200": "200":
description: '{booking} models.booking' description: '{booking} models.booking'
/admiralty/kubeconfig/{execution}: delete:
get:
tags: tags:
- admiralty - oc-datacenter/controllersDatacenterController
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
responses:
"200":
description: ""
/admiralty/node/{execution}:
get:
tags:
- admiralty
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
responses:
"200":
description: ""
/admiralty/secret/{execution}:
get:
tags:
- admiralty
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
responses:
"200":
description: ""
post:
tags:
- admiralty
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
- in: body
name: kubeconfig
description: Kubeconfig to use when creating secret
required: true
schema:
$ref: '#/definitions/controllers.RemoteKubeconfig'
responses:
"201":
description: ""
/admiralty/source/{execution}:
post:
tags:
- admiralty
description: |- description: |-
Create an Admiralty Source on remote cluster find booking by id
<br> <br>
operationId: AdmiraltyController.CreateSource operationId: DatacenterController.Delete
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
responses:
"201":
description: ""
/admiralty/target/{execution}:
post:
tags:
- admiralty
description: |-
Create an Admiralty Target in the namespace associated to the executionID
<br>
operationId: AdmiraltyController.CreateAdmiraltyTarget
parameters:
- in: path
name: execution
description: execution id of the workflow
required: true
type: string
responses:
"201":
description: ""
/admiralty/targets:
get:
tags:
- admiralty
description: |-
find all Admiralty Target
<br>
operationId: AdmiraltyController.GetAllTargets
responses:
"200":
description: ""
/admiralty/targets/{execution}:
get:
tags:
- admiralty
description: |-
find one Admiralty Target
<br>
operationId: AdmiraltyController.GetOneTarget
parameters: parameters:
- in: path - in: path
name: id name: id
description: the name of the target to get description: the id you want to get
required: true
type: string
- in: path
name: type
description: the word type you want to get
required: true required: true
type: string type: string
responses:
"200":
description: ""
/booking/:
get:
tags:
- booking
description: |-
find booking by id
<br>
operationId: BookingController.GetAll
parameters:
- in: query - in: query
name: is_draft name: is_draft
description: draft wished description: draft wished
@@ -184,38 +92,128 @@ paths:
responses: responses:
"200": "200":
description: '{booking} models.booking' description: '{booking} models.booking'
post: /{type}/search/{search}:
get:
tags: tags:
- booking - oc-datacenter/controllersDatacenterController
description: |- description: |-
create booking search datacenter
<br> <br>
operationId: BookingController.Post. operationId: DatacenterController.Search
parameters: parameters:
- in: body - in: path
name: booking name: type
description: the booking you want to post description: the type you want to get
required: true required: true
schema:
type: string type: string
- in: path
name: search
description: the word search you want to get
required: true
type: string type: string
- in: query - in: query
name: is_draft name: is_draft
description: draft wished description: draft wished
type: string type: string
- in: query
name: offset
description: "false"
type: string
- in: query
name: limit
description: "false"
type: string
responses:
"200":
description: '{workspace} models.workspace'
/allowed-image/:
get:
tags:
- allowed-image
description: |-
Retourne toutes les images autorisées à persister sur ce peer
<br>
operationId: AllowedImageController.GetAll
parameters:
- in: query
name: offset
description: "false"
type: string
- in: query
name: limit
description: "false"
type: string
responses: responses:
"200": "200":
description: "" description: ""
schema: schema:
$ref: '#/definitions/models.object' type: array
/booking/{id}: items:
$ref: '#/definitions/allowed_image.AllowedImage'
post:
tags:
- allowed-image
description: |-
Ajoute une image à la liste des images autorisées (peer admin uniquement)
<br>
operationId: AllowedImageController.Post
parameters:
- in: body
name: body
description: Image à autoriser
required: true
schema:
$ref: '#/definitions/allowed_image.AllowedImage'
responses:
"200":
description: ""
schema:
$ref: '#/definitions/allowed_image.AllowedImage'
/allowed-image/{id}:
get: get:
tags: tags:
- booking - allowed-image
description: |-
Retourne une image autorisée par son ID
<br>
operationId: AllowedImageController.Get
parameters:
- in: path
name: id
description: ID de l'image autorisée
required: true
type: string
responses:
"200":
description: ""
schema:
$ref: '#/definitions/allowed_image.AllowedImage'
delete:
tags:
- allowed-image
description: |-
Supprime une image de la liste des images autorisées (peer admin uniquement, entrées bootstrap non supprimables)
<br>
operationId: AllowedImageController.Delete
parameters:
- in: path
name: id
description: ID de l'image autorisée
required: true
type: string
responses:
"200":
description: ""
schema:
$ref: '#/definitions/allowed_image.AllowedImage'
/logs/{id}:
get:
tags:
- oc-datacenter/controllersDatacenterController
description: |- description: |-
find booking by id find booking by id
<br> <br>
operationId: BookingController.Get operationId: DatacenterController.Log
parameters: parameters:
- in: path - in: path
name: id name: id
@@ -225,107 +223,6 @@ paths:
responses: responses:
"200": "200":
description: '{booking} models.booking' description: '{booking} models.booking'
put:
tags:
- booking
description: |-
create computes
<br>
operationId: BookingController.Update
parameters:
- in: path
name: id
description: the compute id you want to get
required: true
type: string
- in: body
name: body
description: The compute content
required: true
schema:
$ref: '#/definitions/models.compute'
responses:
"200":
description: '{compute} models.compute'
/booking/check/{id}/{start_date}/{end_date}:
get:
tags:
- booking
description: |-
check booking
<br>
operationId: BookingController.Check
parameters:
- in: path
name: id
description: id of the datacenter
type: string
- in: path
name: start_date
description: 2006-01-02T15:04:05
type: string
default: the booking start date
- in: path
name: end_date
description: 2006-01-02T15:04:05
type: string
default: the booking end date
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: ""
schema:
$ref: '#/definitions/models.object'
/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'
/session/token/{id}/{duration}: /session/token/{id}/{duration}:
get: get:
tags: tags:
@@ -369,28 +266,22 @@ paths:
"200": "200":
description: "" description: ""
definitions: definitions:
controllers.RemoteKubeconfig: allowed_image.AllowedImage:
title: RemoteKubeconfig title: AllowedImage
type: object
properties:
Data:
type: string
models.compute:
title: compute
type: object
models.object:
title: object
type: object type: object
tags: tags:
- name: oc-datacenter/controllersDatacenterController - name: oc-datacenter/controllersDatacenterController
description: | description: |
Operations about workspace Operations about workspace
- name: booking
description: |
Operations about workspace
- name: version - name: version
description: | description: |
VersionController operations for Version VersionController operations for Version
- name: admiralty - name: allowed-image
description: | description: |
Operations about the admiralty objects of the datacenter AllowedImageController gère la liste locale des images autorisées à persister
sur ce peer après l'exécution d'un workflow.
GET /allowed-image/ → tous les utilisateurs authentifiés
GET /allowed-image/:id → tous les utilisateurs authentifiés
POST /allowed-image/ → peer admin uniquement
DELETE /allowed-image/:id → peer admin uniquement (bloqué si IsDefault)