39 Commits

Author SHA1 Message Date
mr
5302ed48b3 WorkflowScheduler Gestionnary 2026-01-14 15:15:26 +01:00
mr
60492ed8f5 dockerfile 2026-01-08 10:43:54 +01:00
mr
de2f486ebb git ignore 2025-11-13 09:49:44 +01:00
637bdcaeab Ajouter .gitattributes 2025-11-01 16:36:39 +01:00
pb
6897fd192e Merge branch 'feature/create-minio-svcacc' 2025-08-12 12:17:46 +02:00
pb
8176c4d8b8 updated how the S3 service accounts are created on booking 2025-08-12 12:16:01 +02:00
pb
ad62aa24f3 scheduling/booking is now happening with priced instnces 2025-07-10 12:50:28 +02:00
mr
97b51efd50 oclib 2025-06-24 16:59:35 +02:00
mr
07ce87bb49 test 2025-06-24 09:05:15 +02:00
mr
5d1acac9ae Merge branch 'main' of https://cloud.o-forge.io/core/oc-scheduler into main 2025-06-16 09:17:03 +02:00
pb
a1256ee331 changed the return of POST /loki from bytes[] to map[string]interface{}, now logs are displayed in oc-front 2025-06-03 18:02:24 +02:00
pb
922ca1d013 cleaned some log 2025-05-27 15:52:59 +02:00
pb
8ee878fee6 Added some logging 2025-04-30 17:55:46 +02:00
mr
e74f3adcaa demo 2025-04-28 16:17:44 +02:00
mr
af6aa9e17f adjustment 2025-03-28 08:47:44 +01:00
mr
e7f8518f34 dev launch mode 2025-03-06 09:33:09 +01:00
mr
af90189b89 neo oclib 2025-02-21 11:18:45 +01:00
mr
469752f136 traefik 2025-02-19 12:04:30 +01:00
mr
df4e6fcc5c oclib update + controller 2025-02-18 15:01:10 +01:00
mr
e808556906 Merge branch 'feature/namespace' into main 2025-02-17 10:20:56 +01:00
mr
b0b2206c12 test 2025-02-17 10:18:50 +01:00
mr
2a331bdf8f neo lib 2025-02-06 08:50:22 +01:00
mr
efc0fe7948 adapt exec to new instance struct + neo oc lib 2025-02-05 08:39:29 +01:00
mr
d36c31fcc4 oc-scheduler working 2025-01-17 17:21:52 +01:00
plm
8f18da492b k8s integration 2025-01-15 16:23:49 +01:00
mr
ff077380ab oclib 2024-11-21 11:03:29 +01:00
mr
9a86b819f7 test 2024-11-14 09:52:34 +01:00
mr
5e9170cbf2 test 2024-11-12 14:33:10 +01:00
mr
dd5232e2ff test 2024-11-08 14:02:26 +01:00
mr
ce61d214f3 oclib 2024-11-07 13:39:10 +01:00
mr
1e6a000dd0 scheduler 2024-11-04 14:28:01 +01:00
mr
042f0238e0 scheduler 2024-11-04 14:21:43 +01:00
mr
d7a793e991 scheduler 2024-10-30 17:09:55 +01:00
mr
07654de103 oclib 2024-10-30 14:00:51 +01:00
mr
d67fb51a6b scheduler traefik 2024-10-30 10:46:31 +01:00
mr
f624313667 new router 2024-10-15 16:17:21 +02:00
mr
f4146c36f3 retablish port 2024-10-15 12:16:06 +02:00
mr
ace0193d3f Merge branch 'main' of https://cloud.o-forge.io/core/oc-scheduler into main 2024-10-15 10:55:42 +02:00
mr
5244676987 Neo Oc-Scheduler 2024-10-15 10:55:07 +02:00
24 changed files with 1544 additions and 258 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
# Force Go as the main language
*.go linguist-detectable=true
* linguist-language=Go

2
.gitignore vendored
View File

@@ -8,7 +8,7 @@
*.dll
*.so
*.dylib
env.env
# Test binary, built with `go test -c`
*.test

View File

@@ -1,29 +1,44 @@
FROM golang:alpine AS deps
WORKDIR /app
COPY go.mod go.sum ./
RUN sed -i '/replace/d' go.mod
RUN go mod download
#----------------------------------------------------------------------------------------------
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN apk add git
RUN go get github.com/beego/bee/v2 && go install github.com/beego/bee/v2@master
RUN go install github.com/beego/bee/v2@latest
RUN timeout 15 bee run -gendoc=true -downdoc=true -runmode=dev || :
WORKDIR /oc-scheduler
RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' swagger/index.html
COPY --from=deps /go/pkg /go/pkg
COPY --from=deps /app/go.mod /app/go.sum ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" .
RUN export CGO_ENABLED=0 && \
export GOOS=linux && \
export GOARCH=amd64 && \
export BUILD_FLAGS="-ldflags='-w -s'"
RUN ls /app
COPY . .
FROM scratch
RUN sed -i '/replace/d' go.mod
RUN bee pack
RUN mkdir -p /app/extracted && tar -zxvf oc-scheduler.tar.gz -C /app/extracted
#----------------------------------------------------------------------------------------------
FROM golang:alpine
WORKDIR /app
COPY --from=builder /app/oc-scheduler /usr/bin/
COPY --from=builder /app/swagger /app/swagger
COPY docker_scheduler.json /etc/oc/scheduler.json
COPY --from=builder /app/extracted/oc-scheduler /usr/bin/
COPY --from=builder /app/extracted/swagger /app/swagger
COPY --from=builder /app/extracted/docker_scheduler.json /etc/oc/scheduler.json
EXPOSE 8080

40
Makefile Normal file
View File

@@ -0,0 +1,40 @@
.DEFAULT_GOAL := all
build: clean
bee pack
run:
bee run -gendoc=true -downdoc=true
purge:
lsof -t -i:8090 | xargs kill | true
run-dev:
bee generate routers && bee run -gendoc=true -downdoc=true -runmode=prod
dev: purge run-dev
debug:
bee run -downdebug -gendebug
clean:
rm -rf oc-peer.tar.gz
docker:
DOCKER_BUILDKIT=1 docker build -t oc-scheduler -f Dockerfile . --build-arg=HOST=$(HOST)
docker tag oc-scheduler:latest oc/oc-scheduler:0.0.1
publish-kind:
kind load docker-image oc/oc-scheduler:0.0.1 --name opencloud | true
publish-registry:
@echo "TODO"
docker-deploy:
docker compose up -d
run-docker: docker publish-kind publish-registry docker-deploy
all: docker publish-kind publish-registry
.PHONY: build run clean docker publish-kind publish-registry

View File

@@ -6,7 +6,9 @@ To build :
bee generate routers
bee run -gendoc=true -downdoc=true
OR
make dev
If default Swagger page is displayed instead of tyour api, change url in swagger/index.html file to :
<>
url: "swagger.json"

View File

@@ -1,5 +1,5 @@
appname = oc-scheduler
httpport = 8080
httpport = 8090
runmode = dev
autorender = false
copyrequestbody = true

87
controllers/loki.go Normal file
View File

@@ -0,0 +1,87 @@
package controllers
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"cloud.o-forge.io/core/oc-lib/config"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about workflow
type LokiController struct {
beego.Controller
}
type LokiInfo struct {
Start string `json:"start"`
End string `json:"end"`
}
// @Title GetLogs
// @Description get logs
// @Param body body models.compute true "The compute content"
// @Success 200 {workspace} models.workspace
// @router / [post]
func (o *LokiController) GetLogs() {
var resp map[string]interface{}
json.Unmarshal(o.Ctx.Input.CopyBody(100000), &resp)
path := "/loki/api/v1/query_range"
if len(resp) > 0 {
start := fmt.Sprintf("%v", resp["start"])
if len(start) > 10 {
start = start[0:10]
}
end := fmt.Sprintf("%v", resp["end"])
if len(end) > 10 {
end = end[0:10]
}
query := []string{}
for k, v := range resp {
if k == "start" || k == "end" {
continue
}
query = append(query, fmt.Sprintf("%v=\"%v\"", k, v))
}
if len(query) == 0 || len(start) < 10 || len(end) < 10 {
o.Ctx.ResponseWriter.WriteHeader(403)
o.Data["json"] = map[string]string{"error": "Query error, missing data : start, end or query"}
o.ServeJSON()
return
}
path += "?query={" + strings.Join(query, ", ") + "}&start=" + start + "&end=" + end
resp, err := http.Get(config.GetConfig().LokiUrl + path) // CALL
fmt.Println(resp, path)
if err != nil {
o.Ctx.ResponseWriter.WriteHeader(422)
o.Data["json"] = map[string]string{"error": err.Error()}
o.ServeJSON()
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
// Unmarshal: string → []byte → object
err = json.Unmarshal(body, &result)
if err != nil {
o.Ctx.ResponseWriter.WriteHeader(403)
o.Data["json"] = map[string]string{"error": err.Error()}
o.ServeJSON()
return
}
o.Data["json"] = result
o.ServeJSON()
return
}
o.Ctx.ResponseWriter.WriteHeader(403)
o.Data["json"] = map[string]string{"error": "Query error"}
o.ServeJSON()
}

View File

@@ -15,7 +15,10 @@ type VersionController struct {
// @Success 200
// @router / [get]
func (c *VersionController) GetAll() {
c.Data["json"] = map[string]string{"version": "1"}
c.Data["json"] = map[string]string{
"service": "oc-scheduler",
"version": "1",
}
c.ServeJSON()
}
@@ -27,10 +30,6 @@ func (c *VersionController) Status() {
errStr := ""
api := tools.API{}
state, code, err := api.GetState()
if state == tools.ALIVE {
state, _, err = api.CheckRemoteAPIs(map[string]string{})
}
if err != nil {
errStr = err.Error()
}

View File

@@ -9,18 +9,21 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
var collection = oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION)
// Operations about workflow
type WorkflowExecutionController struct {
beego.Controller
}
// @Title Search
// @Title SearchPerDate
// @Description search workspace
// @Param start_date path string true "the word search you want to get"
// @Param end_date path string true "the word search you want to get"
// @Param is_draft query string false "draft wished"
// @Success 200 {workspace} models.workspace
// @router /search/:start_date/:end_date [get]
func (o *WorkflowExecutionController) Search() {
func (o *WorkflowExecutionController) SearchPerDate() {
/*
* This is a sample of how to use the search function
* The search function is used to search for data in the database
@@ -30,6 +33,7 @@ func (o *WorkflowExecutionController) Search() {
* The search function returns a list of data that matches the filter
* The data is then returned as a json object
*/
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
// store and return Id or post with UUID
start_date, _ := time.Parse("2006-01-02", o.Ctx.Input.Param(":start_date"))
end_date, _ := time.Parse("2006-01-02", o.Ctx.Input.Param(":end_date"))
@@ -40,16 +44,20 @@ func (o *WorkflowExecutionController) Search() {
"execution_date": {{Operator: "gte", Value: sd}, {Operator: "lte", Value: ed}},
},
}
o.Data["json"] = oclib.Search(&f, "", oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION))
isDraft := o.Ctx.Input.Query("is_draft")
o.Data["json"] = oclib.NewRequest(collection, user, peerID, groups, nil).Search(&f, "", isDraft == "true")
o.ServeJSON()
}
// @Title GetAll
// @Description find workflow by workflowid
// @Param is_draft query string false "draft wished"
// @Success 200 {workflow} models.workflow
// @router / [get]
func (o *WorkflowExecutionController) GetAll() {
o.Data["json"] = oclib.LoadAll(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION))
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
isDraft := o.Ctx.Input.Query("is_draft")
o.Data["json"] = oclib.NewRequest(collection, user, peerID, groups, nil).LoadAll(isDraft == "true")
o.ServeJSON()
}
@@ -59,7 +67,22 @@ func (o *WorkflowExecutionController) GetAll() {
// @Success 200 {workflow} models.workflow
// @router /:id [get]
func (o *WorkflowExecutionController) Get() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
o.Data["json"] = oclib.LoadOne(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), id)
o.Data["json"] = oclib.NewRequest(collection, user, peerID, groups, nil).LoadOne(id)
o.ServeJSON()
}
// @Title Search
// @Description find compute by key word
// @Param search path string true "the search you want to get"
// @Param is_draft query string false "draft wished"
// @Success 200 {compute} models.compute
// @router /search/:search [get]
func (o *WorkflowExecutionController) Search() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
isDraft := o.Ctx.Input.Query("is_draft")
search := o.Ctx.Input.Param(":search")
o.Data["json"] = oclib.NewRequest(collection, user, peerID, groups, nil).Search(nil, search, isDraft == "true")
o.ServeJSON()
}

View File

@@ -0,0 +1,382 @@
package controllers
import (
"encoding/json"
"fmt"
"oc-scheduler/infrastructure"
"slices"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/models/resources"
"cloud.o-forge.io/core/oc-lib/models/workflow"
"cloud.o-forge.io/core/oc-lib/models/workflow/graph"
"cloud.o-forge.io/core/oc-lib/tools"
beego "github.com/beego/beego/v2/server/web"
"github.com/google/uuid"
)
var orderCollection = oclib.LibDataEnum(oclib.ORDER)
var logger = oclib.GetLogger()
// Operations about workflow
type WorkflowSchedulerController struct {
beego.Controller
}
// @Title Schedule
// @Description schedule workflow
// @Param id path string true "id execution"
// @Param body body models.compute true "The compute content"
// @Success 200 {workspace} models.workspace
// @router /:id [post]
func (o *WorkflowSchedulerController) Schedule() {
logger := oclib.GetLogger()
code := 200
e := ""
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
wfId := o.Ctx.Input.Param(":id")
var resp *infrastructure.WorkflowSchedule
json.Unmarshal(o.Ctx.Input.CopyBody(100000), &resp)
caller := tools.NewHTTPCaller(map[tools.DataType]map[tools.METHOD]string{ // paths to call other OC services
tools.PEER: {
tools.POST: "/status/",
},
tools.BOOKING: {
tools.GET: "/booking/check/:id/:start_date/:end_date",
tools.POST: "/booking/",
},
})
logger.Info().Msg("Booking for " + wfId)
req := oclib.NewRequest(collection, user, peerID, groups, caller)
resp.UUID = uuid.New().String()
sch, _, execs, err := resp.Schedules(wfId, &tools.APIRequest{
Username: user,
PeerID: peerID,
Groups: groups,
Caller: caller,
})
if err != nil {
if sch != nil {
for _, w := range sch.WorkflowExecution {
req.DeleteOne(w.GetID())
}
}
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 409,
"error": "Error when scheduling your execution(s): " + err.Error(),
}
o.ServeJSON()
return
}
logger.Info().Msg("Creating S3 service account if necessary")
for _, exec := range execs {
execId := exec.ExecutionsID
logger.Info().Msg("S3 ServiceAccount for " + execId)
// execId = "6cdaf6e4-5727-480e-ab97-f78853c4e553"
err = createStorageServiceAccount(execId, peerID, wfId, sch, caller, user, groups)
if err != nil {
// if sch != nil {
// for _, w := range sch.WorkflowExecution {
// req.DeleteOne(w.GetID())
// }
// }
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 409,
"error": err.Error(),
}
o.ServeJSON()
return
}
}
o.Data["json"] = map[string]interface{}{
"data": sch.WorkflowExecution,
"code": code,
"error": e,
}
o.ServeJSON()
}
// @Title UnSchedule
// @Description schedule workflow
// @Param id path string true "id execution"
// @Param body body models.compute true "The compute content"
// @Success 200 {workspace} models.workspace
// @router /:id [delete]
func (o *WorkflowSchedulerController) UnSchedule() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
// TODO UNSCHEDULER
filter := &dbs.Filters{
And: map[string][]dbs.Filter{
"workflow_id": {{Operator: dbs.EQUAL.String(), Value: id}},
},
}
o.Data["json"] = oclib.NewRequest(collection, user, peerID, groups, nil).Search(filter, "", true)
o.ServeJSON()
}
// @Title SearchScheduledDraftOrder
// @Description schedule workflow
// @Param id path string true "id execution"
// @Success 200 {workspace} models.workspace
// @router /:id/order [get]
func (o *WorkflowSchedulerController) SearchScheduledDraftOrder() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
filter := &dbs.Filters{
And: map[string][]dbs.Filter{
"workflow_id": {{Operator: dbs.EQUAL.String(), Value: id}},
"order_by": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}
o.Data["json"] = oclib.NewRequest(orderCollection, user, peerID, groups, nil).Search(filter, "", true)
o.ServeJSON()
}
func createStorageServiceAccount(execId string, peerID string, wfId string, wfs *infrastructure.WorkflowSchedule, caller *tools.HTTPCaller, user string, groups []string) error {
// Retrieve the Workflow in the WorkflowSchedule
wf := loadWorkflow(wfId, peerID)
// storageItems := wf.GetGraphItems(wf.Graph.IsStorage)
itemMap := wf.GetItemsByResources()
// mapStorageRessources, err := getItemByRessourceId(wf, storageItems)
for id, items := range itemMap[tools.STORAGE_RESOURCE] {
_ = items
// Load the storage
s, err := oclib.LoadOneStorage(id, user, peerID, groups)
if err != nil {
return err
}
if s.StorageType == enum.S3 {
// DEV MULTI PEER MINIO CREDENTIAL CREATION
// retrieve all the processing linked to a compute using the storage : processing -- compute -- storage
// In this case we need to retrieve the Item ID(s) for each storage to be able to evaluate links with other items
associatedComputingResources := getAssociatedComputeRessources(*wf, itemMap[tools.STORAGE_RESOURCE][id])
for _, computeId := range associatedComputingResources {
c, err := oclib.LoadOneComputing(computeId, user, peerID, groups)
if err != nil {
return err
}
if c.CreatorID == s.CreatorID {
// post on datacenter /minio/createServiceAccount
err := postCreateServiceAccount(peerID, s, caller, execId, wfId)
if err != nil {
// Add a logger.Info() here
return err
}
} else {
// get on storage datacenter /minio/createServiceAccount
access, secret, err := getServiceAccountCredentials(peerID, *s, caller, execId, wfId, *c)
if err != nil {
// Add a logger.Info() here
return err
}
// post on computing datacenter /minio/createSAsecret
err = postS3Secret(peerID, *s, caller, execId, wfId, *c, access, secret) // create the secret holding the retrieved access on c's peer
if err != nil {
// Add a logger.Info() here
return err
}
}
}
}
}
return nil
}
func postCreateServiceAccount(peerID string, s *resources.StorageResource, caller *tools.HTTPCaller, execId string, wfId string) error {
l := oclib.GetLogger()
fmt.Println("Creating a service account on " + peerID + " for " + s.Name)
res := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", peerID, []string{}, nil).LoadOne(s.CreatorID)
if res.Code != 200 {
l.Error().Msg("Error while loading a peer for creation of the serviceAccount")
return fmt.Errorf(res.Err)
}
p := res.ToPeer()
caller.URLS[tools.MINIO_SVCACC] = map[tools.METHOD]string{
tools.POST: "/serviceaccount/" + s.UUID + "/" + execId,
}
l.Debug().Msg("Lauching execution on" + p.UUID)
_, err := p.LaunchPeerExecution(p.UUID, wfId, tools.MINIO_SVCACC, tools.POST, nil, caller)
if err != nil {
l.Error().Msg("Error when executing on peer at " + p.Url + " when creating a S3 service account")
l.Error().Msg(err.Error())
return err
}
if caller.LastResults["code"].(int) != 200 {
l.Error().Msg(fmt.Sprint("Error when trying to create a serviceAccount on storage " + s.Name + " on peer at " + p.Url))
if _, ok := caller.LastResults["body"]; ok {
l.Error().Msg(string(caller.LastResults["body"].([]byte)))
return fmt.Errorf(string(caller.LastResults["body"].(map[string]interface{})["error"].([]byte)))
}
}
return nil
}
func loadWorkflow(workflowId string, peerId string) *workflow.Workflow {
res := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW), "", peerId, []string{}, nil).LoadOne(workflowId)
if res.Code != 200 {
l := oclib.GetLogger()
l.Error().Msg("Error while loading a workflow for creation of the serviceAccount")
return nil
}
return res.ToWorkflow()
}
// func getItemByRessourceId(storages string) (map[string][]string, error) {
// var storagesMap map[string][]string
// }
func getAssociatedComputeRessources(wf workflow.Workflow, storageNodes []string) []string {
storageProcessingLinks := make([]string, 0)
for _, id := range storageNodes {
processings := getStorageRelatedProcessing(wf, id) // Retrieve all the Processing item linked to one storage node
for _, procId := range processings {
computings := getComputeProcessing(wf, procId)
if !slices.Contains(storageProcessingLinks, computings) {
storageProcessingLinks = append(storageProcessingLinks, computings)
}
}
}
return storageProcessingLinks
}
// returns a list of processing item's Id that use the Storage
// theses item Id can be used to instantiate the resource
func getStorageRelatedProcessing(wf workflow.Workflow, storageId string) (relatedProcessing []string) {
var storageLinks []graph.GraphLink
// Only keep the links that are associated to the storage
for _, link := range wf.Graph.Links {
if link.Destination.ID == storageId || link.Source.ID == storageId {
storageLinks = append(storageLinks, link)
}
}
for _, link := range storageLinks {
var resourceId string
if link.Source.ID != storageId {
resourceId = link.Source.ID
} else {
resourceId = link.Destination.ID
}
if wf.Graph.IsProcessing(wf.Graph.Items[resourceId]) {
relatedProcessing = append(relatedProcessing, resourceId)
}
}
return
}
func getComputeProcessing(wf workflow.Workflow, processingId string) (res string) {
computeRel := wf.GetByRelatedProcessing(processingId, wf.Graph.IsCompute)
for _, rel := range computeRel {
return rel.Node.GetID()
}
return ""
}
func getServiceAccountCredentials(peerID string, storageRes resources.StorageResource, caller *tools.HTTPCaller, execId string, wfId string, computeRes resources.ComputeResource) (string, string, error) {
l := oclib.GetLogger()
fmt.Println("Getting a service account for" + computeRes.CreatorID + " on S3 " + storageRes.Name + " on peer " + storageRes.CreatorID)
res := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", peerID, []string{}, nil).LoadOne(storageRes.CreatorID)
if res.Code != 200 {
l.Error().Msg("Error while loading a peer for creation of the serviceAccount")
return "", "", fmt.Errorf(res.Err)
}
p := res.ToPeer()
caller.URLS[tools.MINIO_SVCACC] = map[tools.METHOD]string{
tools.POST: "/serviceaccount/" + storageRes.UUID + "/" + execId,
}
body := map[string]bool{"retrieve": true}
l.Debug().Msg("Lauching execution on" + p.UUID)
resp, err := p.LaunchPeerExecution(p.UUID, wfId, tools.MINIO_SVCACC, tools.POST, body, caller)
if err != nil {
l.Error().Msg("Error when executing on peer at " + p.Url + " when retrieving S3 credentials")
l.Error().Msg(err.Error())
return "", "", err
}
result_code := caller.LastResults["code"].(int)
if !slices.Contains([]int{200, 201}, result_code) {
l.Error().Msg(fmt.Sprint("Error when trying to create a serviceAccount on storage " + storageRes.Name + " on peer at " + p.Url))
if _, ok := caller.LastResults["body"]; ok {
l.Error().Msg(string(caller.LastResults["body"].([]byte)))
return "", "", fmt.Errorf(string(caller.LastResults["body"].(map[string]interface{})["error"].([]byte)))
}
}
var access, secret string
if a, ok := resp["access"]; !ok {
return "", "", fmt.Errorf("Error in the response returned when creating a S3 serviceAccount on " + storageRes.Name + " on peer " + p.UUID)
} else {
access = a.(string)
}
if s, ok := resp["secret"]; !ok {
return "", "", fmt.Errorf("Error in the response returned when creating a S3 serviceAccount on " + storageRes.Name + " on peer " + p.UUID)
} else {
secret = s.(string)
}
return access, secret, nil
}
func postS3Secret(peerID string, s resources.StorageResource, caller *tools.HTTPCaller, execId string, wfId string, c resources.ComputeResource, access string, secret string) error {
l := oclib.GetLogger()
res := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", peerID, []string{}, nil).LoadOne(c.CreatorID)
if res.Code != 200 {
l.Error().Msg("Error while loading a peer for creation of the serviceAccount")
return fmt.Errorf(res.Err)
}
p := res.ToPeer()
caller.URLS[tools.MINIO_SVCACC_SECRET] = map[tools.METHOD]string{
tools.POST: "/secret/" + s.UUID + "/" + execId,
}
body := map[string]string{"access": access, "secret": secret}
_, err := p.LaunchPeerExecution(p.UUID, wfId, tools.MINIO_SVCACC_SECRET, tools.POST, body, caller)
if err != nil {
l.Error().Msg("Error when executing on peer at " + p.Url + " when creating a secret holding s3 credentials in namespace " + execId)
l.Error().Msg(err.Error())
return fmt.Errorf("Error when executing on peer at " + p.Url + " when creating a secret holding s3 credentials" + " : " + err.Error())
}
result_code := caller.LastResults["code"].(int)
if !slices.Contains([]int{200, 201}, result_code) {
l.Error().Msg(fmt.Sprint("Error when trying to post the credential to " + s.Name + "to a secret on peer at " + p.Url))
if _, ok := caller.LastResults["body"]; ok {
l.Error().Msg(string(caller.LastResults["body"].([]byte)))
return fmt.Errorf(string(caller.LastResults["body"].(map[string]interface{})["error"].([]byte)))
}
}
return nil
}

View File

@@ -1,33 +0,0 @@
version: '3.4'
services:
mongo:
image: 'mongo:latest'
networks:
- catalog
ports:
- 27017:27017
container_name: mongo
volumes:
- oc-catalog-data:/data/db
- oc-catalog-data:/data/configdb
mongo-express:
image: "mongo-express:latest"
restart: always
depends_on:
- mongo
networks:
- catalog
ports:
- 8081:8081
environment:
- ME_CONFIG_BASICAUTH_USERNAME=test
- ME_CONFIG_BASICAUTH_PASSWORD=test
volumes:
oc-catalog-data:
networks:
catalog:
# name: catalog

View File

@@ -5,12 +5,21 @@ services:
environment:
- MONGO_DATABASE=DC_myDC
image: 'oc-scheduler:latest'
labels:
- "traefik.enable=true"
- "traefik.http.routers.scheduler.entrypoints=web"
- "traefik.http.routers.scheduler.rule=PathPrefix(`/scheduler`)"
- "traefik.http.middlewares.scheduler-rewrite.replacepathregex.regex=^/scheduler(.*)"
- "traefik.http.middlewares.scheduler-rewrite.replacepathregex.replacement=/oc$$1"
- "traefik.http.routers.scheduler.middlewares=scheduler-rewrite"
- "traefik.http.services.scheduler.loadbalancer.server.port=8080"
- "traefik.http.middlewares.scheduler.forwardauth.address=http://oc-auth:8080/oc/forward"
ports:
- 8090:8080
container_name: oc-scheduler
networks:
- catalog
- oc
networks:
catalog:
oc:
external: true

View File

@@ -1,4 +1,6 @@
{
"MONGO_URL":"mongodb://mongo:27017/",
"MONGO_DATABASE":"DC_myDC"
"NATS_URL":"nats://nats:4222",
"MONGO_DATABASE":"DC_myDC",
"LOKI_URL": "http://loki:3100"
}

4
env.env Normal file
View File

@@ -0,0 +1,4 @@
KUBERNETES_SERVICE_HOST=192.168.47.20
KUBE_CA="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTVlk3ZHZhNEdYTVdkMy9jMlhLN3JLYjlnWXgyNSthaEE0NmkyNVBkSFAKRktQL2UxSVMyWVF0dzNYZW1TTUQxaStZdzJSaVppNUQrSVZUamNtNHdhcnFvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWtlUVJpNFJiODduME5yRnZaWjZHClc2SU55NnN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRXA5ck04WmdNclRZSHYxZjNzOW5DZXZZeWVVa3lZUk4KWjUzazdoaytJS1FDSVFDbk05TnVGKzlTakIzNDFacGZ5ays2NEpWdkpSM3BhcmVaejdMd2lhNm9kdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
KUBE_CERT="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJWUxWNkFPQkdrU1F3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl6TVRFeU1ETTJNQjRYRFRJME1EZ3dPREV3TVRNMU5sb1hEVEkxTURndwpPREV3TVRNMU5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJGQ2Q1MFdPeWdlQ2syQzcKV2FrOWY4MVAvSkJieVRIajRWOXBsTEo0ck5HeHFtSjJOb2xROFYxdUx5RjBtOTQ2Nkc0RmRDQ2dqaXFVSk92Swp3NVRPNnd5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVFJkOFI5cXVWK2pjeUVmL0ovT1hQSzMyS09XekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQTArbThqTDBJVldvUTZ0dnB4cFo4NVlMalF1SmpwdXM0aDdnSXRxS3NmUVVDSUI2M2ZNdzFBMm5OVWU1TgpIUGZOcEQwSEtwcVN0Wnk4djIyVzliYlJUNklZCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFRc3hXWk9pbnIrcVp4TmFEQjVGMGsvTDF5cE01VHAxOFRaeU92ektJazQKRTFsZWVqUm9STW0zNmhPeVljbnN3d3JoNnhSUnBpMW5RdGhyMzg0S0Z6MlBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBYZkVmYXJsZm8zTWhIL3lmemx6Cnl0OWlqbHN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUxJL2dNYnNMT3MvUUpJa3U2WHVpRVMwTEE2cEJHMXgKcnBlTnpGdlZOekZsQWlFQW1wdjBubjZqN3M0MVI0QzFNMEpSL0djNE53MHdldlFmZWdEVGF1R2p3cFk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
KUBE_DATA="LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5ZS1BFb1dhd1NKUzJlRW5oWmlYMk5VZlY1ZlhKV2krSVNnV09TNFE5VTlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUozblJZN0tCNEtUWUx0WnFUMS96VS84a0Z2Sk1lUGhYMm1Vc25pczBiR3FZblkyaVZEeApYVzR2SVhTYjNqcm9iZ1YwSUtDT0twUWs2OHJEbE03ckRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="

51
go.mod
View File

@@ -1,60 +1,61 @@
module oc-scheduler
go 1.22.0
go 1.23.0
toolchain go1.22.4
toolchain go1.24.0
require (
cloud.o-forge.io/core/oc-lib v0.0.0-20241002120813-a09a04e1a71e
github.com/beego/beego/v2 v2.3.1
cloud.o-forge.io/core/oc-lib v0.0.0-20260114125749-fa5b7543332d
github.com/beego/beego/v2 v2.3.8
github.com/google/uuid v1.6.0
github.com/smartystreets/goconvey v1.7.2
go.mongodb.org/mongo-driver v1.17.1
go.mongodb.org/mongo-driver v1.17.4
)
require (
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
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/goraz/onion v0.1.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/nats.go v1.37.0 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nats.go v1.44.0 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.58.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/prometheus/client_golang v1.23.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

163
go.sum
View File

@@ -1,14 +1,44 @@
cloud.o-forge.io/core/oc-lib v0.0.0-20240904135449-4f0ab6a3760f h1:v9mw3uNg/DJswOvHooMu8/BMedA+vIXbma+8iUwsjUI=
cloud.o-forge.io/core/oc-lib v0.0.0-20240904135449-4f0ab6a3760f/go.mod h1:FIJD0taWLJ5pjQLJ6sfE2KlTkvbmk5SMcyrxdjsaVz0=
cloud.o-forge.io/core/oc-lib v0.0.0-20241002120813-a09a04e1a71e h1:77QHk5JSf0q13B/Ai3xjcsGSS7nX+9AfxcsYz5oDo/A=
cloud.o-forge.io/core/oc-lib v0.0.0-20241002120813-a09a04e1a71e/go.mod h1:t+zpCTVKVdHH/BImwtMYY2QIWLMXKgY4n/JhFm3Vpu8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250630120603-3971d5ca5d7b h1:ld3dxfjFcquqMiq9Exm8kiNg9WNWPOaCyzUly4pi4sc=
cloud.o-forge.io/core/oc-lib v0.0.0-20250630120603-3971d5ca5d7b/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27 h1:iogk6pV3gybzQDBXMI6Qd/jvSA1h+3oRE+vLl1MRjew=
cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250707143058-365b924e4b9d h1:9utgm0JRYtbzSQDmEmRbyzOfshKaQyK/EpDqMJOdKpA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250707143058-365b924e4b9d/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250708115955-346275e12cb4 h1:OxDo6/lucAYbCtTw3ZoOK/z/M4HxNgd+wClT17Z8UJg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250708115955-346275e12cb4/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250709145437-4e3ff9aa086b h1:PagTxoBr/LomQuTA7HL8q1vuNNDfdvFHAKi4pjGwf1M=
cloud.o-forge.io/core/oc-lib v0.0.0-20250709145437-4e3ff9aa086b/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250709154237-83e590d4e190 h1:/8uQ2nkJnv13K0+BL/QbxaVJI+oAOq5A/aBPgNrsjbQ=
cloud.o-forge.io/core/oc-lib v0.0.0-20250709154237-83e590d4e190/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250710094754-98a2359c9d9f h1:PZ8yVeZ4q85lMQ06KIRyHkSJnrlFf78fxgV2fjzZHqc=
cloud.o-forge.io/core/oc-lib v0.0.0-20250710094754-98a2359c9d9f/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730160123-76d83878ebd3 h1:SCG9evvlT1yrYi9mxvIX2hZaQAuv33AdH6rKqAOH6yg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730160123-76d83878ebd3/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730161555-a093369dc5a2 h1:M6bVZ08gSYnwOHWS/zqNe8+7xwc4zewjmxDor5kBXqo=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730161555-a093369dc5a2/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730162109-be2a1cc11474 h1:LpC+PkWmzKcsqKJbaqDiHnO5UxeGaJtscJ2aEqMXD0I=
cloud.o-forge.io/core/oc-lib v0.0.0-20250730162109-be2a1cc11474/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250731135305-cc3091d401ea h1:yJ4cdFycOw8+X97gh8e33piztu6J0V+iWWkVtvx9V/g=
cloud.o-forge.io/core/oc-lib v0.0.0-20250731135305-cc3091d401ea/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805095627-76e9b2562e9b h1:ktjmh3VA0gb+TAfbnQNX0XAGUpA6HYm9p9myyvYL1IE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805095627-76e9b2562e9b/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805112547-cc939451fd81 h1:539qIasa1Vz+FY8nEdLTQHXJqZBSLDuRY7mWo2r+vDg=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805112547-cc939451fd81/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805113921-40a61387b9f1 h1:53KzZ+1JqRY6J7EVzQpNBmLzNuxb8oHNW3UgqxkYABo=
cloud.o-forge.io/core/oc-lib v0.0.0-20250805113921-40a61387b9f1/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20260113150431-6d745fe92216 h1:9ab37/TK1JhdOOvYbqq9J9hDbipofBkq0l2GQ6umARY=
cloud.o-forge.io/core/oc-lib v0.0.0-20260113150431-6d745fe92216/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20260114125532-0e378dc19c06 h1:kDTCqxzV8dvLeXPzPWIn4LgFqwgVprrXwNnP+ftA9C0=
cloud.o-forge.io/core/oc-lib v0.0.0-20260114125532-0e378dc19c06/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20260114125749-fa5b7543332d h1:6oGSN4Fb+H7LNVbUEN7vaDtWBHZTdd2Y1BkBdZ7MLXE=
cloud.o-forge.io/core/oc-lib v0.0.0-20260114125749-fa5b7543332d/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/beego/beego/v2 v2.3.0 h1:iECVwzm6egw6iw6tkWrEDqXG4NQtKLQ6QBSYqlM6T/I=
github.com/beego/beego/v2 v2.3.0/go.mod h1:Ob/5BJ9fIKZLd4s9ZV3o9J6odkkIyL83et+p98gyYXo=
github.com/beego/beego/v2 v2.3.1 h1:7MUKMpJYzOXtCUsTEoXOxsDV/UcHw6CPbaWMlthVNsc=
github.com/beego/beego/v2 v2.3.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc=
github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -23,23 +53,23 @@ github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/etcd-io/etcd v3.3.17+incompatible/go.mod h1:cdZ77EstHBwVtD6iTgzgvogwcjo9m4iOqoijouPJ4bs=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -53,10 +83,8 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -66,8 +94,9 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -83,10 +112,12 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M=
github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=
@@ -95,21 +126,25 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.58.0 h1:N+N8vY4/23r6iYfD3UQZUoJPnUYAo7v6LG5XZxjZTXo=
github.com/prometheus/common v0.58.0/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 h1:v9ezJDHA1XGxViAUSIoO/Id7Fl63u6d0YmsAm+/p2hs=
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02/go.mod h1:RF16/A3L0xSa0oSERcnhd8Pu3IXSDZSK2gmGIMsttFE=
github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0=
@@ -121,8 +156,8 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
@@ -132,31 +167,31 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -167,23 +202,27 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

362
infrastructure/scheduler.go Normal file
View File

@@ -0,0 +1,362 @@
package infrastructure
import (
"errors"
"fmt"
"strings"
"sync"
"time"
"cloud.o-forge.io/core/oc-lib/models/bill"
"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/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/order"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/resources/purchase_resource"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/models/workflow"
"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
"cloud.o-forge.io/core/oc-lib/tools"
"github.com/google/uuid"
"github.com/robfig/cron"
)
/*
* WorkflowSchedule is a struct that contains the scheduling information of a workflow
* It contains the mode of the schedule (Task or Service), the name of the schedule, the start and end time of the schedule and the cron expression
*/
// it's a flying object only use in a session time. It's not stored in the database
type WorkflowSchedule struct {
UUID string `json:"id" validate:"required"` // ExecutionsID is the list of the executions id of the workflow
Workflow *workflow.Workflow `json:"workflow,omitempty"` // Workflow is the workflow dependancy of the schedule
WorkflowExecution []*workflow_execution.WorkflowExecution `json:"workflow_executions,omitempty"` // WorkflowExecution is the list of executions of the workflow
Message string `json:"message,omitempty"` // Message is the message of the schedule
Warning string `json:"warning,omitempty"` // Warning is the warning message of the schedule
Start time.Time `json:"start" validate:"required,ltfield=End"` // Start is the start time of the schedule, is required and must be less than the End time
End *time.Time `json:"end,omitempty"` // End is the end time of the schedule, is required and must be greater than the Start time
DurationS float64 `json:"duration_s" default:"-1"` // End is the end time of the schedule
Cron string `json:"cron,omitempty"` // here the cron format : ss mm hh dd MM dw task
BookingMode booking.BookingMode `json:"booking_mode,omitempty"` // BookingMode qualify the preemption order of the scheduling. if no payment allowed with preemption set up When_Possible
SelectedInstances workflow.ConfigItem `json:"selected_instances"`
SelectedPartnerships workflow.ConfigItem `json:"selected_partnerships"`
SelectedBuyings workflow.ConfigItem `json:"selected_buyings"`
SelectedStrategies workflow.ConfigItem `json:"selected_strategies"`
SelectedBillingStrategy pricing.BillingStrategy `json:"selected_billing_strategy"`
}
// TODO PREEMPTION !
/*
To schedule a preempted, omg.
pour faire ça on doit alors lancé une exécution prioritaire qui passera devant toutes les autres, celon un niveau de priorité.
Preemptible = 7, pour le moment il n'existera que 0 et 7.
Dans le cas d'une préemption l'exécution est immédiable et bloquera tout le monde tant qu'il n'a pas été exécuté.
Une ressource doit pouvoir être preemptible pour être exécutée de la sorte.
Se qui implique si on est sur une ressource par ressource que si un élement n'est pas préemptible,
alors il devra être effectué dés que possible
Dans le cas dés que possible, la start date est immédiate MAIS !
ne pourra se lancé que SI il n'existe pas d'exécution se lançant durant la période indicative. ( Ultra complexe )
*/
func NewScheduler(mode int, start string, end string, durationInS float64, cron string) *WorkflowSchedule {
ws := &WorkflowSchedule{
UUID: uuid.New().String(),
Start: time.Now(),
BookingMode: booking.BookingMode(mode),
DurationS: durationInS,
Cron: cron,
}
s, err := time.Parse("2006-01-02T15:04:05", start)
if err == nil && ws.BookingMode == booking.PLANNED {
ws.Start = s // can apply a defined start other than now, if planned
}
e, err := time.Parse("2006-01-02T15:04:05", end)
if err == nil {
ws.End = &e
}
return ws
}
func (ws *WorkflowSchedule) GetBuyAndBook(wfID string, request *tools.APIRequest) (bool, *workflow.Workflow, []*workflow_execution.WorkflowExecution, []*purchase_resource.PurchaseResource, []*booking.Booking, error) {
if request.Caller == nil && request.Caller.URLS == nil && request.Caller.URLS[tools.BOOKING] == nil || request.Caller.URLS[tools.BOOKING][tools.GET] == "" {
return false, nil, []*workflow_execution.WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, errors.New("no caller defined")
}
access := workflow.NewAccessor(request)
res, code, err := access.LoadOne(wfID)
if code != 200 {
return false, nil, []*workflow_execution.WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, errors.New("could not load the workflow with id: " + err.Error())
}
wf := res.(*workflow.Workflow)
isPreemptible, longest, priceds, wf, err := wf.Planify(ws.Start, ws.End,
ws.SelectedInstances, ws.SelectedPartnerships, ws.SelectedBuyings, ws.SelectedStrategies,
int(ws.BookingMode), request)
if err != nil {
return false, wf, []*workflow_execution.WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err
}
ws.DurationS = longest
ws.Message = "We estimate that the workflow will start at " + ws.Start.String() + " and last " + fmt.Sprintf("%v", ws.DurationS) + " seconds."
if ws.End != nil && ws.Start.Add(time.Duration(longest)*time.Second).After(*ws.End) {
ws.Warning = "The workflow may be too long to be executed in the given time frame, we will try to book it anyway\n"
}
execs, err := ws.GetExecutions(wf, isPreemptible)
if err != nil {
return false, wf, []*workflow_execution.WorkflowExecution{}, []*purchase_resource.PurchaseResource{}, []*booking.Booking{}, err
}
purchased := []*purchase_resource.PurchaseResource{}
bookings := []*booking.Booking{}
for _, exec := range execs {
purchased = append(purchased, exec.Buy(ws.SelectedBillingStrategy, ws.UUID, wfID, priceds)...)
bookings = append(bookings, exec.Book(ws.UUID, wfID, priceds)...)
}
errCh := make(chan error, len(bookings))
var m sync.Mutex
for _, b := range bookings {
go getBooking(b, request, errCh, &m)
}
for i := 0; i < len(bookings); i++ {
if err := <-errCh; err != nil {
return false, wf, execs, purchased, bookings, err
}
}
return true, wf, execs, purchased, bookings, nil
}
func (ws *WorkflowSchedule) GenerateOrder(purchases []*purchase_resource.PurchaseResource, bookings []*booking.Booking, request *tools.APIRequest) error {
newOrder := &order.Order{
AbstractObject: utils.AbstractObject{
Name: "order_" + request.PeerID + "_" + time.Now().UTC().Format("2006-01-02T15:04:05"),
IsDraft: true,
},
ExecutionsID: ws.UUID,
Purchases: purchases,
Bookings: bookings,
Status: enum.PENDING,
}
if res, _, err := order.NewAccessor(request).StoreOne(newOrder); err == nil {
if _, err := bill.DraftFirstBill(res.(*order.Order), request); err != nil {
return err
}
return nil
} else {
return err
}
}
func getBooking(b *booking.Booking, request *tools.APIRequest, errCh chan error, m *sync.Mutex) {
m.Lock()
c, err := getCallerCopy(request, errCh)
if err != nil {
errCh <- err
return
}
m.Unlock()
meth := c.URLS[tools.BOOKING][tools.GET]
meth = strings.ReplaceAll(meth, ":id", b.ResourceID)
meth = strings.ReplaceAll(meth, ":start_date", b.ExpectedStartDate.Format("2006-01-02T15:04:05"))
meth = strings.ReplaceAll(meth, ":end_date", b.ExpectedEndDate.Format("2006-01-02T15:04:05"))
c.URLS[tools.BOOKING][tools.GET] = meth
_, err = (&peer.Peer{}).LaunchPeerExecution(b.DestPeerID, b.ResourceID, tools.BOOKING, tools.GET, nil, &c)
if err != nil {
errCh <- fmt.Errorf("error on " + b.DestPeerID + err.Error())
return
}
errCh <- nil
}
func getCallerCopy(request *tools.APIRequest, errCh chan error) (tools.HTTPCaller, error) {
var c tools.HTTPCaller
err := request.Caller.DeepCopy(c)
if err != nil {
errCh <- err
return tools.HTTPCaller{}, nil
}
c.URLS = request.Caller.URLS
return c, err
}
func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*WorkflowSchedule, *workflow.Workflow, []*workflow_execution.WorkflowExecution, error) {
if request == nil {
return ws, nil, []*workflow_execution.WorkflowExecution{}, errors.New("no request found")
}
c := request.Caller
if c == nil || c.URLS == nil || c.URLS[tools.BOOKING] == nil {
return ws, nil, []*workflow_execution.WorkflowExecution{}, errors.New("no caller defined")
}
methods := c.URLS[tools.BOOKING]
if _, ok := methods[tools.GET]; !ok {
return ws, nil, []*workflow_execution.WorkflowExecution{}, errors.New("no path found")
}
ok, wf, executions, purchases, bookings, err := ws.GetBuyAndBook(wfID, request)
ws.WorkflowExecution = executions
if !ok || err != nil {
return ws, nil, executions, errors.New("could not book the workflow : " + fmt.Sprintf("%v", err))
}
ws.Workflow = wf
var errCh = make(chan error, len(bookings))
var m sync.Mutex
for _, purchase := range purchases {
go ws.CallDatacenter(purchase, purchase.DestPeerID, tools.PURCHASE_RESOURCE, request, errCh, &m)
}
for i := 0; i < len(purchases); i++ {
if err := <-errCh; err != nil {
return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err))
}
}
errCh = make(chan error, len(bookings))
for _, booking := range bookings {
go ws.CallDatacenter(booking, booking.DestPeerID, tools.BOOKING, request, errCh, &m)
}
for i := 0; i < len(bookings); i++ {
if err := <-errCh; err != nil {
return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err))
}
}
if err := ws.GenerateOrder(purchases, bookings, request); err != nil {
return ws, wf, executions, err
}
fmt.Println("Schedules")
for _, exec := range executions {
err := exec.PurgeDraft(request)
if err != nil {
return ws, nil, []*workflow_execution.WorkflowExecution{}, errors.New("purge draft" + fmt.Sprintf("%v", err))
}
exec.StoreDraftDefault()
utils.GenericStoreOne(exec, workflow_execution.NewAccessor(request))
}
fmt.Println("Schedules")
wf.GetAccessor(&tools.APIRequest{Admin: true}).UpdateOne(wf, wf.GetID())
return ws, wf, executions, nil
}
func (ws *WorkflowSchedule) CallDatacenter(purchase utils.DBObject, destPeerID string, dt tools.DataType, request *tools.APIRequest, errCh chan error, m *sync.Mutex) {
m.Lock()
c, err := getCallerCopy(request, errCh)
if err != nil {
errCh <- err
return
}
m.Unlock()
if res, err := (&peer.Peer{}).LaunchPeerExecution(destPeerID, "", dt, tools.POST, purchase.Serialize(purchase), &c); err != nil {
errCh <- err
return
} else {
data := res["data"].(map[string]interface{})
purchase.SetID(fmt.Sprintf("%v", data["id"]))
}
errCh <- nil
}
/*
BOOKING IMPLIED TIME, not of subscription but of execution
so is processing time execution time applied on computes
data can improve the processing time
time should implied a security time border (10sec) if not from the same executions
VERIFY THAT WE HANDLE DIFFERENCE BETWEEN LOCATION TIME && BOOKING
*/
/*
* getExecutions is a function that returns the executions of a workflow
* it returns an array of workflow_execution.WorkflowExecution
*/
func (ws *WorkflowSchedule) GetExecutions(workflow *workflow.Workflow, isPreemptible bool) ([]*workflow_execution.WorkflowExecution, error) {
workflows_executions := []*workflow_execution.WorkflowExecution{}
dates, err := ws.GetDates()
if err != nil {
return workflows_executions, err
}
for _, date := range dates {
obj := &workflow_execution.WorkflowExecution{
AbstractObject: utils.AbstractObject{
UUID: uuid.New().String(), // set the uuid of the execution
Name: workflow.Name + "_execution_" + date.Start.String(), // set the name of the execution
},
Priority: 1,
ExecutionsID: ws.UUID,
ExecDate: date.Start, // set the execution date
EndDate: date.End, // set the end date
State: enum.DRAFT, // set the state to 1 (scheduled)
WorkflowID: workflow.GetID(), // set the workflow id dependancy of the execution
}
if ws.BookingMode != booking.PLANNED {
obj.Priority = 0
}
if ws.BookingMode == booking.PREEMPTED && isPreemptible {
obj.Priority = 7
}
ws.SelectedStrategies = obj.SelectedStrategies
ws.SelectedPartnerships = obj.SelectedPartnerships
ws.SelectedBuyings = obj.SelectedBuyings
ws.SelectedInstances = obj.SelectedInstances
workflows_executions = append(workflows_executions, obj)
}
return workflows_executions, nil
}
func (ws *WorkflowSchedule) GetDates() ([]Schedule, error) {
schedule := []Schedule{}
if len(ws.Cron) > 0 { // if cron is set then end date should be set
if ws.End == nil {
return schedule, errors.New("a cron task should have an end date")
}
if ws.DurationS <= 0 {
ws.DurationS = ws.End.Sub(ws.Start).Seconds()
}
cronStr := strings.Split(ws.Cron, " ") // split the cron string to treat it
if len(cronStr) < 6 { // if the cron string is less than 6 fields, return an error because format is : ss mm hh dd MM dw (6 fields)
return schedule, errors.New("Bad cron message: (" + ws.Cron + "). Should be at least ss mm hh dd MM dw")
}
subCron := strings.Join(cronStr[:6], " ")
// cron should be parsed as ss mm hh dd MM dw t (min 6 fields)
specParser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) // create a new cron parser
sched, err := specParser.Parse(subCron) // parse the cron string
if err != nil {
return schedule, errors.New("Bad cron message: " + err.Error())
}
// loop through the cron schedule to set the executions
for s := sched.Next(ws.Start); !s.IsZero() && s.Before(*ws.End); s = sched.Next(s) {
e := s.Add(time.Duration(ws.DurationS) * time.Second)
schedule = append(schedule, Schedule{
Start: s,
End: &e,
})
}
} else { // if no cron, set the execution to the start date
schedule = append(schedule, Schedule{
Start: ws.Start,
End: ws.End,
})
}
return schedule, nil
}
type Schedule struct {
Start time.Time
End *time.Time
}
/*
* TODO : LARGEST GRAIN PLANIFYING THE WORKFLOW WHEN OPTION IS SET
* SET PROTECTION BORDER TIME
*/

18
main.go
View File

@@ -4,7 +4,9 @@ import (
_ "oc-scheduler/routers"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/tools"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/filter/cors"
)
const appname = "oc-scheduler"
@@ -21,16 +23,24 @@ func main() {
oclib.SetConfig(
o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"),
o.GetStringDefault("MONGO_DATABASE", "DC_myDC"),
"",
o.GetStringDefault("LOKI_URL", ""),
o.GetStringDefault("NATS_URL", "nats://localhost:4222"),
o.GetStringDefault("LOKI_URL", "loki://localhost:3100"),
o.GetStringDefault("LOG_LEVEL", "info"),
)
// Beego init
beego.BConfig.AppName = appname
beego.BConfig.Listen.HTTPPort = 8080
beego.BConfig.Listen.HTTPPort = o.GetIntDefault("port", 8080)
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
api := &tools.API{}
api.Discovered(beego.BeeApp.Handlers.GetAllControllerInfo())
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Authorization", "Content-Type"},
ExposeHeaders: []string{"Content-Length", "Content-Type"},
AllowCredentials: true,
}))
beego.Run()
}

Binary file not shown.

View File

@@ -7,6 +7,15 @@ import (
func init() {
beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:LokiController"],
beego.ControllerComments{
Method: "GetLogs",
Router: `/`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-scheduler/controllers:VersionController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:VersionController"],
beego.ControllerComments{
Method: "GetAll",
@@ -46,10 +55,46 @@ func init() {
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowExecutionController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowExecutionController"],
beego.ControllerComments{
Method: "Search",
Router: `/search/:search`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowExecutionController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowExecutionController"],
beego.ControllerComments{
Method: "SearchPerDate",
Router: `/search/:start_date/:end_date`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
beego.ControllerComments{
Method: "Schedule",
Router: `/:id`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
beego.ControllerComments{
Method: "UnSchedule",
Router: `/:id`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"] = append(beego.GlobalControllerRouter["oc-scheduler/controllers:WorkflowSchedulerController"],
beego.ControllerComments{
Method: "SearchScheduledDraftOrder",
Router: `/:id/order`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
}

View File

@@ -15,7 +15,15 @@ import (
func init() {
ns := beego.NewNamespace("/oc/",
beego.NSNamespace("/workflow_execution",
beego.NSInclude(
&controllers.WorkflowSchedulerController{},
),
beego.NSNamespace("/loki",
beego.NSInclude(
&controllers.LokiController{},
),
),
beego.NSNamespace("/execution",
beego.NSInclude(
&controllers.WorkflowExecutionController{},
),

View File

@@ -1,5 +1,6 @@
{
"port":8080,
"port":8090,
"MONGO_URL":"mongodb://localhost:27017/",
"MONGO_DATABASE":"DC_myDC"
"MONGO_DATABASE":"DC_myDC",
"LOKI_URL": "http://localhost:3100"
}

View File

@@ -9,12 +9,147 @@
"email": "admin@o-cloud.io"
},
"license": {
"name": "MIT",
"url": "https://opensource.org/license/mit"
"name": "AGPL",
"url": "https://www.gnu.org/licenses/agpl-3.0.html"
}
},
"basePath": "/oc/",
"paths": {
"/execution/": {
"get": {
"tags": [
"execution"
],
"description": "find workflow by workflowid\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.GetAll",
"parameters": [
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{workflow} models.workflow"
}
}
}
},
"/execution/search/{search}": {
"get": {
"tags": [
"execution"
],
"description": "find compute by key word\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.Search",
"parameters": [
{
"in": "path",
"name": "search",
"description": "the search you want to get",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{compute} models.compute"
}
}
}
},
"/execution/search/{start_date}/{end_date}": {
"get": {
"tags": [
"execution"
],
"description": "search workspace\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.SearchPerDate",
"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"
}
}
}
},
"/execution/{id}": {
"get": {
"tags": [
"execution"
],
"description": "find workflow by workflowid\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the workflowid you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{workflow} models.workflow"
}
}
}
},
"/loki/": {
"post": {
"tags": [
"loki"
],
"description": "get logs\n\u003cbr\u003e",
"operationId": "LokiController.GetLogs",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The compute content",
"required": true,
"schema": {
"$ref": "#/definitions/models.compute"
}
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
}
},
"/version/": {
"get": {
"tags": [
@@ -43,39 +178,80 @@
}
}
},
"/workflow_execution/": {
"get": {
"/{id}": {
"post": {
"tags": [
"workflow_execution"
"oc-scheduler/controllersWorkflowSchedulerController"
],
"description": "find workflow by workflowid\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.GetAll",
"responses": {
"200": {
"description": "{workflow} models.workflow"
}
}
}
},
"/workflow_execution/search/{start_date}/{end_date}": {
"get": {
"tags": [
"workflow_execution"
],
"description": "search workspace\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.Search",
"description": "schedule workflow\n\u003cbr\u003e",
"operationId": "WorkflowSchedulerController.Schedule",
"parameters": [
{
"in": "path",
"name": "start_date",
"description": "the word search you want to get",
"name": "id",
"description": "id execution",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The compute content",
"required": true,
"schema": {
"$ref": "#/definitions/models.compute"
}
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
},
"delete": {
"tags": [
"oc-scheduler/controllersWorkflowSchedulerController"
],
"description": "schedule workflow\n\u003cbr\u003e",
"operationId": "WorkflowSchedulerController.UnSchedule",
"parameters": [
{
"in": "path",
"name": "end_date",
"description": "the word search you want to get",
"name": "id",
"description": "id execution",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The compute content",
"required": true,
"schema": {
"$ref": "#/definitions/models.compute"
}
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
}
},
"/{id}/order": {
"get": {
"tags": [
"oc-scheduler/controllersWorkflowSchedulerController"
],
"description": "schedule workflow\n\u003cbr\u003e",
"operationId": "WorkflowSchedulerController.SearchScheduledDraftOrder",
"parameters": [
{
"in": "path",
"name": "id",
"description": "id execution",
"required": true,
"type": "string"
}
@@ -86,34 +262,25 @@
}
}
}
}
},
"/workflow_execution/{id}": {
"get": {
"tags": [
"workflow_execution"
],
"description": "find workflow by workflowid\n\u003cbr\u003e",
"operationId": "WorkflowExecutionController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the workflowid you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{workflow} models.workflow"
}
}
}
"definitions": {
"models.compute": {
"title": "compute",
"type": "object"
}
},
"tags": [
{
"name": "workflow_execution",
"name": "oc-scheduler/controllersWorkflowSchedulerController",
"description": "Operations about workflow\n"
},
{
"name": "loki",
"description": "Operations about workflow\n"
},
{
"name": "execution",
"description": "Operations about workflow\n"
},
{

View File

@@ -8,10 +8,170 @@ info:
contact:
email: admin@o-cloud.io
license:
name: MIT
url: https://opensource.org/license/mit
name: AGPL
url: https://www.gnu.org/licenses/agpl-3.0.html
basePath: /oc/
paths:
/{id}:
post:
tags:
- oc-scheduler/controllersWorkflowSchedulerController
description: |-
schedule workflow
<br>
operationId: WorkflowSchedulerController.Schedule
parameters:
- in: path
name: id
description: id execution
required: true
type: string
- in: body
name: body
description: The compute content
required: true
schema:
$ref: '#/definitions/models.compute'
responses:
"200":
description: '{workspace} models.workspace'
delete:
tags:
- oc-scheduler/controllersWorkflowSchedulerController
description: |-
schedule workflow
<br>
operationId: WorkflowSchedulerController.UnSchedule
parameters:
- in: path
name: id
description: id execution
required: true
type: string
- in: body
name: body
description: The compute content
required: true
schema:
$ref: '#/definitions/models.compute'
responses:
"200":
description: '{workspace} models.workspace'
/{id}/order:
get:
tags:
- oc-scheduler/controllersWorkflowSchedulerController
description: |-
schedule workflow
<br>
operationId: WorkflowSchedulerController.SearchScheduledDraftOrder
parameters:
- in: path
name: id
description: id execution
required: true
type: string
responses:
"200":
description: '{workspace} models.workspace'
/execution/:
get:
tags:
- execution
description: |-
find workflow by workflowid
<br>
operationId: WorkflowExecutionController.GetAll
parameters:
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{workflow} models.workflow'
/execution/{id}:
get:
tags:
- execution
description: |-
find workflow by workflowid
<br>
operationId: WorkflowExecutionController.Get
parameters:
- in: path
name: id
description: the workflowid you want to get
required: true
type: string
responses:
"200":
description: '{workflow} models.workflow'
/execution/search/{search}:
get:
tags:
- execution
description: |-
find compute by key word
<br>
operationId: WorkflowExecutionController.Search
parameters:
- in: path
name: search
description: the search you want to get
required: true
type: string
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{compute} models.compute'
/execution/search/{start_date}/{end_date}:
get:
tags:
- execution
description: |-
search workspace
<br>
operationId: WorkflowExecutionController.SearchPerDate
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'
/loki/:
post:
tags:
- loki
description: |-
get logs
<br>
operationId: LokiController.GetLogs
parameters:
- in: body
name: body
description: The compute content
required: true
schema:
$ref: '#/definitions/models.compute'
responses:
"200":
description: '{workspace} models.workspace'
/version/:
get:
tags:
@@ -34,58 +194,18 @@ paths:
responses:
"200":
description: ""
/workflow_execution/:
get:
definitions:
models.compute:
title: compute
type: object
tags:
- workflow_execution
description: |-
find workflow by workflowid
<br>
operationId: WorkflowExecutionController.GetAll
responses:
"200":
description: '{workflow} models.workflow'
/workflow_execution/{id}:
get:
tags:
- workflow_execution
description: |-
find workflow by workflowid
<br>
operationId: WorkflowExecutionController.Get
parameters:
- in: path
name: id
description: the workflowid you want to get
required: true
type: string
responses:
"200":
description: '{workflow} models.workflow'
/workflow_execution/search/{start_date}/{end_date}:
get:
tags:
- workflow_execution
description: |-
search workspace
<br>
operationId: WorkflowExecutionController.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
responses:
"200":
description: '{workspace} models.workspace'
tags:
- name: workflow_execution
- name: oc-scheduler/controllersWorkflowSchedulerController
description: |
Operations about workflow
- name: loki
description: |
Operations about workflow
- name: execution
description: |
Operations about workflow
- name: version