diff --git a/controllers/allowed_image.go b/controllers/allowed_image.go
index 22e443a..cfb5c2d 100644
--- a/controllers/allowed_image.go
+++ b/controllers/allowed_image.go
@@ -3,6 +3,7 @@ package controllers
import (
"encoding/json"
"slices"
+ "strconv"
oclib "cloud.o-forge.io/core/oc-lib"
beego "github.com/beego/beego/v2/server/web"
@@ -28,11 +29,15 @@ func isAdmin(groups []string) bool {
// @Title GetAll
// @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
// @router / [get]
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)
- 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.ServeJSON()
}
diff --git a/controllers/datacenter.go b/controllers/datacenter.go
index 7c2b3a2..5d9f9ea 100644
--- a/controllers/datacenter.go
+++ b/controllers/datacenter.go
@@ -1,12 +1,15 @@
package controllers
import (
+ "fmt"
"net/http"
"oc-datacenter/infrastructure/monitor"
+ "strconv"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
"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"
"github.com/gorilla/websocket"
)
@@ -16,74 +19,143 @@ type DatacenterController struct {
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
// @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 offset query string false
+// @Param limit query string false
// @Success 200 {booking} models.booking
// @router / [get]
func (o *DatacenterController) GetAll() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
isDraft := o.Ctx.Input.Query("is_draft")
- storages := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), user, peerID, groups, nil).Search(&dbs.Filters{
- Or: map[string][]dbs.Filter{
- "abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
- },
- }, "", isDraft == "true")
- computes := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_DATACENTER), user, peerID, groups, nil).Search(&dbs.Filters{
- Or: map[string][]dbs.Filter{
- "abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
- },
- }, "", isDraft == "true")
- storages.Data = append(storages.Data, computes.Data...)
- if storages.Err != "" {
- storages.Err += " - " + computes.Err
+ offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
+ limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
+ m := map[string][]utils.ShallowDBObject{}
+ for _, col := range o.collection(false) {
+ if m[col.String()] == nil {
+ m[col.String()] = []utils.ShallowDBObject{}
+ }
+ s := oclib.NewRequest(oclib.LibDataEnum(col), user, peerID, groups, nil).LoadAll(isDraft == "true", int64(offset), int64(limit))
+ fmt.Println(s)
+ m[col.String()] = append(m[col.String()], s.Data...)
+ }
+ fmt.Println(m)
+ o.Data["json"] = map[string]interface{}{
+ "data": m,
+ "code": 200,
+ "err": nil,
}
- o.Data["json"] = storages
o.ServeJSON()
}
// @Title Get
// @Description find booking by id
// @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"
// @Success 200 {booking} models.booking
-// @router /:id [get]
+// @router /:type/:id [get]
func (o *DatacenterController) Get() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
- isDraft := o.Ctx.Input.Query("is_draft")
id := o.Ctx.Input.Param(":id")
- storages := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE), 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(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,
- }
+ for _, col := range o.collection(false) {
+ data := oclib.NewRequest(col, user, peerID, groups, nil).LoadOne(id)
+ o.Data["json"] = data
+ if data.Data != nil {
+ break
}
+ }
+ o.ServeJSON()
+}
- } else {
- o.Data["json"] = map[string]interface{}{
- "data": storages.Data[0],
- "code": storages.Code,
- "err": storages.Err,
+// @Title Delete
+// @Description find booking by id
+// @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"
+// @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()
@@ -97,7 +169,7 @@ var upgrader = websocket.Upgrader{
// @Description find booking by id
// @Param id path string true "the id you want to get"
// @Success 200 {booking} models.booking
-// @router /:id [get]
+// @router /logs/:id [get]
func (o *DatacenterController) Log() {
// user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
diff --git a/go.mod b/go.mod
index e31d6ef..928d97e 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ 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/biter777/countries v1.7.5 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index c436ecf..cc86af7 100644
--- a/go.sum
+++ b/go.sum
@@ -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-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-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/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
diff --git a/infrastructure/admiralty/admiralty.go b/infrastructure/admiralty/admiralty.go
index 4224418..bcd6f9b 100644
--- a/infrastructure/admiralty/admiralty.go
+++ b/infrastructure/admiralty/admiralty.go
@@ -274,7 +274,7 @@ func provisionPVCsForTarget(ctx context.Context, targetNS string, sourceExecutio
"executions_id": {{Operator: dbs.EQUAL.String(), Value: sourceExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 1000)
if res.Err != "" || len(res.Data) == 0 {
return
@@ -424,7 +424,7 @@ func (s *AdmiraltySetter) TeardownIfRemote(exec *workflow_execution.WorkflowExec
"executions_id": {{Operator: dbs.EQUAL.String(), Value: exec.ExecutionsID}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.COMPUTE_RESOURCE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 1000)
if res.Err != "" || len(res.Data) == 0 {
return
diff --git a/infrastructure/allowed_image_bootstrap.go b/infrastructure/allowed_image_bootstrap.go
index f27d4f7..a167737 100644
--- a/infrastructure/allowed_image_bootstrap.go
+++ b/infrastructure/allowed_image_bootstrap.go
@@ -29,7 +29,7 @@ func BootstrapAllowedImages() {
And: map[string][]dbs.Filter{
"image": {{Operator: dbs.EQUAL.String(), Value: img.Image}},
},
- }, "", false)
+ }, "", false, 0, 1)
if existing.Err != "" || len(existing.Data) > 0 {
continue // déjà présente ou erreur de recherche : on passe
}
diff --git a/infrastructure/booking_watchdog.go b/infrastructure/booking_watchdog.go
deleted file mode 100644
index e54e84c..0000000
--- a/infrastructure/booking_watchdog.go
+++ /dev/null
@@ -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{}{})
-}
diff --git a/infrastructure/kubernetes/kubernetes.go b/infrastructure/kubernetes/kubernetes.go
index e39d6b5..be27c7d 100644
--- a/infrastructure/kubernetes/kubernetes.go
+++ b/infrastructure/kubernetes/kubernetes.go
@@ -152,7 +152,7 @@ func (s *KubernetesService) filterNonAllowed(images []string) []string {
And: map[string][]dbs.Filter{
"image": {{Operator: dbs.EQUAL.String(), Value: name}},
},
- }, "", false)
+ }, "", false, 0, 1000)
if len(res.Data) == 0 {
toRemove = append(toRemove, img)
diff --git a/infrastructure/monitor/monitor.go b/infrastructure/monitor/monitor.go
index 87be824..b13b069 100644
--- a/infrastructure/monitor/monitor.go
+++ b/infrastructure/monitor/monitor.go
@@ -57,7 +57,7 @@ func Call(book *booking.Booking,
"source": {{Operator: dbs.EQUAL.String(), Value: instance.Source}},
"abstractlive.resources_id": {{Operator: dbs.EQUAL.String(), Value: computeRes.GetID()}},
},
- }, "", false)
+ }, "", false, 0, 1000)
if res.Err != "" {
continue
}
diff --git a/infrastructure/nats/nats.go b/infrastructure/nats/nats.go
index 3ecd2d7..009b8c3 100644
--- a/infrastructure/nats/nats.go
+++ b/infrastructure/nats/nats.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "oc-datacenter/infrastructure"
"oc-datacenter/infrastructure/admiralty"
"oc-datacenter/infrastructure/kubernetes"
"oc-datacenter/infrastructure/kubernetes/models"
@@ -237,7 +238,7 @@ func ListenNATS() {
if err := json.Unmarshal(resp.Payload, &evt); err != nil || evt.ExecutionsID == "" {
return
}
- go kubernetes.NewKubernetesService(evt.ExecutionsID).TeardownForExecution(evt.ExecutionID)
+ go infrastructure.TeardownForExecution(evt.ExecutionID, evt.ExecutionsID)
},
// ─── REMOVE_RESOURCE ────────────────────────────────────────────────────────
diff --git a/infrastructure/storage/minio_setter.go b/infrastructure/storage/minio_setter.go
index a5381c7..01a7e7f 100644
--- a/infrastructure/storage/minio_setter.go
+++ b/infrastructure/storage/minio_setter.go
@@ -298,7 +298,7 @@ func (m *MinioSetter) TeardownAsSource(ctx context.Context, event MinioDeleteEve
// loadMinioURL searches through all live storages accessible by peerID to find
// the one that references MinioID, and returns its endpoint URL.
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 != "" {
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}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 {
return
diff --git a/infrastructure/storage/pvc_setter.go b/infrastructure/storage/pvc_setter.go
index 521ef7c..e9ea3b2 100644
--- a/infrastructure/storage/pvc_setter.go
+++ b/infrastructure/storage/pvc_setter.go
@@ -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.
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 != "" {
return ""
}
@@ -175,7 +175,7 @@ func ResolveStorageName(storageID, peerID string) string {
// loadStorageSize looks up the SizeGB for this storage in live storages.
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 != "" {
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}},
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 {
return
diff --git a/infrastructure/kubernetes/watchdog.go b/infrastructure/watchdog.go
similarity index 72%
rename from infrastructure/kubernetes/watchdog.go
rename to infrastructure/watchdog.go
index 9bd7d79..479a723 100644
--- a/infrastructure/kubernetes/watchdog.go
+++ b/infrastructure/watchdog.go
@@ -1,27 +1,126 @@
-package kubernetes
+package infrastructure
import (
"context"
+ "encoding/json"
"fmt"
+ "oc-datacenter/conf"
+ "oc-datacenter/infrastructure/admiralty"
+ "oc-datacenter/infrastructure/kubernetes"
+ "oc-datacenter/infrastructure/storage"
"regexp"
"strings"
+ "sync"
"time"
- "oc-datacenter/conf"
- "oc-datacenter/infrastructure"
- "oc-datacenter/infrastructure/admiralty"
- "oc-datacenter/infrastructure/storage"
-
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/models/workflow_execution"
"cloud.o-forge.io/core/oc-lib/tools"
+ "go.mongodb.org/mongo-driver/bson/primitive"
v1 "k8s.io/api/core/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}$`)
// 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).
//
// Must be launched in a goroutine from main.
-func (s *KubernetesService) Watch() {
+func Watch() {
logger := oclib.GetLogger()
logger.Info().Msg("InfraWatchdog: started")
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
- if err := s.scanOrphaned(); err != nil {
+ if err := scanOrphaned(); err != nil {
logger.Error().Msg("InfraWatchdog: " + err.Error())
}
- if err := s.scanOrphanedMinio(); err != nil {
+ if err := scanOrphanedMinio(); err != nil {
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())
}
- if err := s.scanOrphanedPVC(); err != nil {
+ if err := scanOrphanedPVC(); err != nil {
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
// WorkflowExecution in the DB, and triggers teardown for any that are in a
// terminal state. Namespaces already in Terminating phase are skipped.
-func (s *KubernetesService) scanOrphaned() error {
+func scanOrphaned() error {
logger := oclib.GetLogger()
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",
executionsID, exec.State)
- go s.TeardownForExecution(exec.GetID())
+ go TeardownForExecution(exec.GetID(), exec.ExecutionsID)
}
return nil
}
@@ -107,7 +206,7 @@ func (s *KubernetesService) scanOrphaned() error {
// terminal state and triggers Minio teardown for each unique executionsID found.
// This covers the case where the Kubernetes namespace is already gone (manual
// deletion, prior partial teardown) but Minio SA and bucket were never revoked.
-func (s *KubernetesService) scanOrphanedMinio() error {
+func scanOrphanedMinio() error {
logger := oclib.GetLogger()
myself, err := oclib.GetMySelf()
@@ -121,7 +220,7 @@ func (s *KubernetesService) scanOrphanedMinio() error {
And: map[string][]dbs.Filter{
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 10000)
if 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
// 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.
-func (s *KubernetesService) scanOrphanedAdmiraltyNodes() error {
+func scanOrphanedAdmiraltyNodes() error {
logger := oclib.GetLogger()
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
// returns a non-empty name — the same guard used by teardownPVCForExecution.
-func (s *KubernetesService) scanOrphanedPVC() error {
+func scanOrphanedPVC() error {
logger := oclib.GetLogger()
myself, err := oclib.GetMySelf()
@@ -265,7 +364,7 @@ func (s *KubernetesService) scanOrphanedPVC() error {
And: map[string][]dbs.Filter{
"resource_type": {{Operator: dbs.EQUAL.String(), Value: tools.LIVE_STORAGE.EnumIndex()}},
},
- }, "", false)
+ }, "", false, 0, 10000)
if 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{
"executions_id": {{Operator: dbs.EQUAL.String(), Value: executionsID}},
},
- }, "", false)
+ }, "", false, 0, 10000)
if res.Err != "" || len(res.Data) == 0 {
return nil
@@ -325,7 +424,7 @@ func findTerminalExecution(executionsID string, peerID string) *workflow_executi
return nil
}
- if !infrastructure.ClosingStates[exec.State] {
+ if !ClosingStates[exec.State] {
return nil
}
return exec
@@ -334,7 +433,7 @@ func findTerminalExecution(executionsID string, peerID string) *workflow_executi
// teardownInfraForExecution handles infrastructure cleanup when a workflow terminates.
// oc-datacenter is responsible only for infra here — booking/execution state
// is managed by oc-scheduler.
-func (s *KubernetesService) TeardownForExecution(executionID string) {
+func TeardownForExecution(executionID string, executionsID string) {
logger := oclib.GetLogger()
myself, err := oclib.GetMySelf()
@@ -352,8 +451,8 @@ func (s *KubernetesService) TeardownForExecution(executionID string) {
exec := res.(*workflow_execution.WorkflowExecution)
ctx := context.Background()
- admiralty.NewAdmiraltySetter(s.ExecutionsID).TeardownIfRemote(exec, selfPeerID)
- storage.NewMinioSetter(s.ExecutionsID, "").TeardownForExecution(ctx, selfPeerID)
- storage.NewPVCSetter(s.ExecutionsID, "").TeardownForExecution(ctx, selfPeerID)
- s.CleanupImages(ctx)
+ admiralty.NewAdmiraltySetter(executionsID).TeardownIfRemote(exec, selfPeerID)
+ storage.NewMinioSetter(executionsID, "").TeardownForExecution(ctx, selfPeerID)
+ storage.NewPVCSetter(executionsID, "").TeardownForExecution(ctx, selfPeerID)
+ kubernetes.NewKubernetesService(executionsID).CleanupImages(ctx)
}
diff --git a/main.go b/main.go
index fed919d..02d6b61 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
import (
"oc-datacenter/conf"
"oc-datacenter/infrastructure"
+ "oc-datacenter/infrastructure/nats"
_ "oc-datacenter/routers"
oclib "cloud.o-forge.io/core/oc-lib"
@@ -30,9 +31,9 @@ func main() {
infrastructure.BootstrapAllowedImages()
- go infrastructure.ListenNATS()
+ go nats.ListenNATS()
go infrastructure.WatchBookings()
- go infrastructure.WatchInfra()
+ go infrastructure.Watch()
beego.Run()
}
diff --git a/oc-datacenter b/oc-datacenter
deleted file mode 100755
index b9d7c63..0000000
Binary files a/oc-datacenter and /dev/null differ
diff --git a/routers/commentsRouter.go b/routers/commentsRouter.go
index ff8ea3f..da00c12 100644
--- a/routers/commentsRouter.go
+++ b/routers/commentsRouter.go
@@ -7,43 +7,7 @@ import (
func init() {
- beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
- 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.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AllowedImageController"],
beego.ControllerComments{
Method: "GetAll",
Router: `/`,
@@ -52,7 +16,7 @@ func init() {
Filters: 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{
Method: "Post",
Router: `/`,
@@ -61,7 +25,7 @@ func init() {
Filters: 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{
Method: "Get",
Router: `/:id`,
@@ -70,65 +34,11 @@ func init() {
Filters: 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{
- Method: "Log",
+ Method: "Delete",
Router: `/:id`,
- 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: "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"},
+ AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
@@ -145,17 +55,35 @@ func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:DatacenterController"],
beego.ControllerComments{
Method: "Get",
- Router: `/:id`,
+ Router: `/:type/:id`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: 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{
- Method: "CreateServiceAccount",
- Router: `/serviceaccount/:minioId/:executions`,
- AllowHTTPMethods: []string{"post"},
+ Method: "Delete",
+ Router: `/:type/:id`,
+ 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(),
Filters: nil,
Params: nil})
@@ -169,15 +97,6 @@ func init() {
Filters: 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.ControllerComments{
Method: "GetAll",
diff --git a/routers/router.go b/routers/router.go
index 9119a39..5e047ad 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -14,11 +14,10 @@ import (
)
func init() {
- ns := beego.NewNamespace("/oc/",
+ ns := beego.NewNamespace("/oc",
beego.NSInclude(
&controllers.DatacenterController{},
),
-
beego.NSNamespace("/session",
beego.NSInclude(
&controllers.SessionController{},
diff --git a/swagger/index.html b/swagger/index.html
index 2a9d4e2..9df41b1 100644
--- a/swagger/index.html
+++ b/swagger/index.html
@@ -39,7 +39,7 @@
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
- url: "https://petstore.swagger.io/v2/swagger.json",
+ url: "swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
diff --git a/swagger/swagger.json b/swagger/swagger.json
index bf38760..41fee25 100644
--- a/swagger/swagger.json
+++ b/swagger/swagger.json
@@ -13,7 +13,7 @@
"url": "https://www.gnu.org/licenses/agpl-3.0.html"
}
},
- "basePath": "/oc/",
+ "basePath": "/oc",
"paths": {
"/": {
"get": {
@@ -23,11 +23,30 @@
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "DatacenterController.GetAll",
"parameters": [
+ {
+ "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"
+ },
+ {
+ "in": "query",
+ "name": "offset",
+ "description": "false",
+ "type": "string"
+ },
+ {
+ "in": "query",
+ "name": "limit",
+ "description": "false",
+ "type": "string"
}
],
"responses": {
@@ -37,222 +56,24 @@
}
}
},
- "/admiralty/kubeconfig/{execution}": {
+ "/allowed-image/": {
"get": {
"tags": [
- "admiralty"
+ "allowed-image"
],
- "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": "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",
+ "description": "Retourne toutes les images autorisées à persister sur ce peer\n\u003cbr\u003e",
+ "operationId": "AllowedImageController.GetAll",
"parameters": [
{
"in": "query",
- "name": "is_draft",
- "description": "draft wished",
- "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"
- },
+ "name": "offset",
+ "description": "false",
"type": "string"
},
{
"in": "query",
- "name": "is_draft",
- "description": "draft wished",
+ "name": "limit",
+ "description": "false",
"type": "string"
}
],
@@ -260,44 +81,54 @@
"200": {
"description": "",
"schema": {
- "$ref": "#/definitions/models.object"
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/allowed_image.AllowedImage"
+ }
+ }
+ }
+ }
+ },
+ "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"
}
}
}
}
},
- "/booking/check/{id}/{start_date}/{end_date}": {
+ "/allowed-image/{id}": {
"get": {
"tags": [
- "booking"
+ "allowed-image"
],
- "description": "check booking\n\u003cbr\u003e",
- "operationId": "BookingController.Check",
+ "description": "Retourne une image autorisée par son ID\n\u003cbr\u003e",
+ "operationId": "AllowedImageController.Get",
"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",
+ "description": "ID de l'image autorisée",
+ "required": true,
"type": "string"
}
],
@@ -305,84 +136,43 @@
"200": {
"description": "",
"schema": {
- "$ref": "#/definitions/models.object"
+ "$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)\n\u003cbr\u003e",
+ "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"
}
}
}
}
},
- "/booking/search/execution/{id}": {
+ "/logs/{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"
+ "oc-datacenter/controllersDatacenterController"
],
"description": "find booking by id\n\u003cbr\u003e",
- "operationId": "BookingController.Get",
+ "operationId": "DatacenterController.Log",
"parameters": [
{
"in": "path",
@@ -397,36 +187,6 @@
"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}": {
@@ -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": {
"tags": [
"oc-datacenter/controllersDatacenterController"
@@ -500,6 +308,47 @@
"required": true,
"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",
"name": "is_draft",
@@ -516,21 +365,8 @@
}
},
"definitions": {
- "controllers.RemoteKubeconfig": {
- "title": "RemoteKubeconfig",
- "type": "object",
- "properties": {
- "Data": {
- "type": "string"
- }
- }
- },
- "models.compute": {
- "title": "compute",
- "type": "object"
- },
- "models.object": {
- "title": "object",
+ "allowed_image.AllowedImage": {
+ "title": "AllowedImage",
"type": "object"
}
},
@@ -539,17 +375,13 @@
"name": "oc-datacenter/controllersDatacenterController",
"description": "Operations about workspace\n"
},
- {
- "name": "booking",
- "description": "Operations about workspace\n"
- },
{
"name": "version",
"description": "VersionController operations for Version\n"
},
{
- "name": "admiralty",
- "description": "Operations about the admiralty objects of the datacenter\n"
+ "name": "allowed-image",
+ "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"
}
]
}
\ No newline at end of file
diff --git a/swagger/swagger.yml b/swagger/swagger.yml
index 43a4fac..edb5ead 100644
--- a/swagger/swagger.yml
+++ b/swagger/swagger.yml
@@ -10,7 +10,7 @@ info:
license:
name: AGPL
url: https://www.gnu.org/licenses/agpl-3.0.html
-basePath: /oc/
+basePath: /oc
paths:
/:
get:
@@ -21,14 +21,27 @@ paths:
operationId: DatacenterController.GetAll
parameters:
+ - 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
+ - in: query
+ name: offset
+ description: "false"
+ type: string
+ - in: query
+ name: limit
+ description: "false"
+ type: string
responses:
"200":
description: '{booking} models.booking'
- /{id}:
+ /{type}/{id}:
get:
tags:
- oc-datacenter/controllersDatacenterController
@@ -42,6 +55,11 @@ paths:
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
name: is_draft
description: draft wished
@@ -49,134 +67,24 @@ paths:
responses:
"200":
description: '{booking} models.booking'
- /admiralty/kubeconfig/{execution}:
- get:
+ delete:
tags:
- - admiralty
- 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: |-
- Create an Admiralty Source on remote cluster
-
- 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
-
- 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
-
- operationId: AdmiraltyController.GetAllTargets
- responses:
- "200":
- description: ""
- /admiralty/targets/{execution}:
- get:
- tags:
- - admiralty
- description: |-
- find one Admiralty Target
-
- 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
+ - oc-datacenter/controllersDatacenterController
description: |-
find booking by id
- operationId: BookingController.GetAll
+ 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
name: is_draft
description: draft wished
@@ -184,38 +92,128 @@ paths:
responses:
"200":
description: '{booking} models.booking'
- post:
+ /{type}/search/{search}:
+ get:
tags:
- - booking
+ - oc-datacenter/controllersDatacenterController
description: |-
- create booking
+ search datacenter
- operationId: BookingController.Post.
+ operationId: DatacenterController.Search
parameters:
- - in: body
- name: booking
- description: the booking you want to post
+ - 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
- schema:
- type: string
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'
+ /allowed-image/:
+ get:
+ tags:
+ - allowed-image
+ description: |-
+ Retourne toutes les images autorisées à persister sur ce peer
+
+ operationId: AllowedImageController.GetAll
+ parameters:
+ - in: query
+ name: offset
+ description: "false"
+ type: string
+ - in: query
+ name: limit
+ description: "false"
+ type: string
responses:
"200":
description: ""
schema:
- $ref: '#/definitions/models.object'
- /booking/{id}:
+ type: array
+ items:
+ $ref: '#/definitions/allowed_image.AllowedImage'
+ post:
+ tags:
+ - allowed-image
+ description: |-
+ Ajoute une image à la liste des images autorisées (peer admin uniquement)
+
+ 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:
tags:
- - booking
+ - allowed-image
+ description: |-
+ Retourne une image autorisée par son ID
+
+ 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)
+
+ 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: |-
find booking by id
- operationId: BookingController.Get
+ operationId: DatacenterController.Log
parameters:
- in: path
name: id
@@ -225,107 +223,6 @@ paths:
responses:
"200":
description: '{booking} models.booking'
- put:
- tags:
- - booking
- description: |-
- create computes
-
- 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
-
- 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
-
- 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
-
- 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}:
get:
tags:
@@ -369,28 +266,22 @@ paths:
"200":
description: ""
definitions:
- controllers.RemoteKubeconfig:
- title: RemoteKubeconfig
- type: object
- properties:
- Data:
- type: string
- models.compute:
- title: compute
- type: object
- models.object:
- title: object
+ allowed_image.AllowedImage:
+ title: AllowedImage
type: object
tags:
- name: oc-datacenter/controllersDatacenterController
description: |
Operations about workspace
-- name: booking
- description: |
- Operations about workspace
- name: version
description: |
VersionController operations for Version
-- name: admiralty
+- name: allowed-image
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)