Compare commits

..

No commits in common. "main" and "feature/namespace" have entirely different histories.

46 changed files with 994 additions and 2335 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
swagger/
oc-datacenter

View File

@ -1,53 +1,30 @@
ARG KUBERNETES_HOST=${KUBERNETES_HOST:-"127.0.0.1"}
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
RUN apk add git
RUN go install github.com/beego/bee/v2@latest
WORKDIR /oc-datacenter
COPY --from=deps /go/pkg /go/pkg
COPY --from=deps /app/go.mod /app/go.sum ./
RUN export CGO_ENABLED=0 && \
export GOOS=linux && \
export GOARCH=amd64 && \
export BUILD_FLAGS="-ldflags='-w -s'"
COPY . .
RUN sed -i '/replace/d' go.mod
RUN if [ ! -f swagger/index.html ]; then timeout 15 bee run -gendoc=true -downdoc=true; fi
RUN bee generate routers
RUN bee generate docs
RUN bee pack
RUN mkdir -p /app/extracted && tar -zxvf oc-datacenter.tar.gz -C /app/extracted
RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' /app/extracted/swagger/index.html
RUN apk add git
#----------------------------------------------------------------------------------------------
RUN go get github.com/beego/bee/v2 && go install github.com/beego/bee/v2@master
FROM golang:alpine
RUN timeout 15 bee run -gendoc=true -downdoc=true -runmode=dev || :
ENV KUBERNETES_SERVICE_HOST=$KUBERNETES_HOST
RUN sed -i 's/http:\/\/127.0.0.1:8080\/swagger\/swagger.json/swagger.json/g' swagger/index.html
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o setup .
RUN ls /app
FROM scratch
WORKDIR /app
COPY --from=builder /app/extracted/oc-datacenter /usr/bin/
COPY --from=builder /app/extracted/swagger /app/swagger
COPY --from=builder /app/extracted/docker_datacenter.json /etc/oc/datacenter.json
COPY --from=builder /app/setup /usr/bin/setup
COPY --from=builder /app/swagger /app/swagger
COPY docker_datacenter.json /etc/oc/datacenter.json
EXPOSE 8080
ENTRYPOINT ["oc-datacenter"]
ENTRYPOINT ["setup"]

View File

@ -1,40 +0,0 @@
.DEFAULT_GOAL := all
build: clean
bee pack
run:
bee run -gendoc=true -downdoc=true
purge:
lsof -t -i:8092 | 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-datacenter.tar.gz
docker:
DOCKER_BUILDKIT=1 docker build -t oc-datacenter -f Dockerfile . --build-arg=HOST=$(HOST)
docker tag oc-datacenter:latest oc/oc-datacenter:0.0.1
publish-kind:
kind load docker-image oc/oc-datacenter: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

@ -7,9 +7,6 @@ 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"
@ -17,52 +14,7 @@ If default Swagger page is displayed instead of tyour api, change url in swagger
Note on particular process :
- set a bookin delete all related workflow booking before creating new ones. (no update of existing ones)
## Admiralty
The routes in /admiralty will trigger actions on the DC's Kubernetes API to retrieve information on Admiralty resources.
### Targets
Remote clusters that can be used by Admiralty to delegate pods.
To set up a target Admiralty needs to associate a `secret` which contains an edited version of the target's `kubeconfig`.
Once the Target is set the remote cluster appears in the output of `kubectl get nodes` under the name `admiralty-<namespace>-<target name>-*`
**TODO** : We might need a way to test if an IP is associated to an admiralty target
# Docker Kube Settings
Set up your base64 key from your ~/.kube/config.
Don't forget to set up your external IP in docker_datacenter.json
## Admiralty
The routes in /admiralty will trigger actions on the DC's Kubernetes API to retrieve information on Admiralty resources.
### Targets
Remote clusters that can be used by Admiralty to delegate pods.
To set up a target Admiralty needs to associate a `secret` which contains an edited version of the target's `kubeconfig`.
Once the Target is set the remote cluster appears in the output of `kubectl get nodes` under the name `admiralty-<namespace>-<target name>-*`
**TODO** : We might need a way to test if an IP is associated to an admiralty target
# Docker Kube Settings
Set up your base64 key from your ~/.kube/config.
Don't forget to set up your external IP in docker_datacenter.json
## Admiralty
The routes in /admiralty will trigger actions on the DC's Kubernetes API to retrieve information on Admiralty resources.
### Targets
Remote clusters that can be used by Admiralty to delegate pods.
To set up a target Admiralty needs to associate a `secret` which contains an edited version of the target's `kubeconfig`.
Once the Target is set the remote cluster appears in the output of `kubectl get nodes` under the name `admiralty-<namespace>-<target name>-*`
**TODO** : We might need a way to test if an IP is associated to an admiralty target
Don't forget to set up your external IP in docker_datacenter.json

View File

@ -1,5 +1,5 @@
appname = oc-datacenter
httpport = 8092
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
@ -7,4 +7,4 @@ EnableDocs = true
sqlconn =
MONGO_URL = "mongodb://127.0.0.1:27017/beego-demo"
MONGO_DATABASE = "DC_myDC-demo_06042021"
MONGO_DATABASE = "DC_myDC-demo_06042021"

View File

@ -3,14 +3,12 @@ package conf
import "sync"
type Config struct {
Mode string
KubeHost string
KubePort string
KubeCA string
KubeCert string
KubeData string
MinioRootKey string
MinioRootSecret string
Mode string
KubeHost string
KubePort string
KubeCA string
KubeCert string
KubeData string
}
var instance *Config

View File

@ -1,601 +0,0 @@
package controllers
import (
"encoding/base64"
"encoding/json"
"fmt"
"oc-datacenter/conf"
"oc-datacenter/infrastructure"
"oc-datacenter/models"
"slices"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
beego "github.com/beego/beego/v2/server/web"
jwt "github.com/golang-jwt/jwt/v5"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
)
type KubeInfo struct {
Url *string
KubeCA *string
KubeCert *string
KubeKey *string
}
type RemoteKubeconfig struct {
Data *string
}
type KubeUser struct {
Name string
User struct {
Token string
}
}
type KubeconfigToken struct {
ApiVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Preferences string `yaml:"preferences"`
CurrentContext string `yaml:"current-context"`
Clusters []struct {
Cluster struct {
CA string `yaml:"certificate-authority-data"`
Server string `yaml:"server"`
} `yaml:"cluster"`
Name string `yaml:"name"`
} `yaml:"clusters"`
Contexts []struct {
Context struct {
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
} `yaml:"context"`
Name string `yaml:"name"`
} `yaml:"contexts"`
Users []struct {
Name string `yaml:"name"`
User struct {
Token string `yaml:"token"`
} `yaml:"user"`
} `yaml:"users"`
}
// Operations about the admiralty objects of the datacenter
type AdmiraltyController struct {
beego.Controller
}
// @Title GetAllTargets
// @Description find all Admiralty Target
// @Success 200
// @router /targets [get]
func (c *AdmiraltyController) GetAllTargets() {
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
HandleControllerErrors(c.Controller, 500, &err, nil)
// c.Ctx.Output.SetStatus(500)
// c.ServeJSON()
// c.Data["json"] = map[string]string{"error": err.Error()}
return
}
res, err := serv.GetTargets(c.Ctx.Request.Context())
c.Data["json"] = res
c.ServeJSON()
}
// @Title GetOneTarget
// @Description find one Admiralty Target
// @Param id path string true "the name of the target to get"
// @Success 200
// @router /targets/:execution [get]
func (c *AdmiraltyController) GetOneTarget() {
id := c.Ctx.Input.Param(":execution")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
res, err := serv.GetTargets(c.Ctx.Request.Context())
if err != nil {
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
id = "target-" + id
found := slices.Contains(res, id)
if !found {
c.Ctx.Output.SetStatus(404)
c.ServeJSON()
}
c.Data["json"] = id
c.ServeJSON()
}
// @Title DeleteAdmiraltySession
// @Description find one Admiralty Target
// @Param execution path string true "the name of the target to get"
// @Success 200
// @router /targets/:execution [delete]
func (c *AdmiraltyController) DeleteAdmiraltySession() {
id := c.Ctx.Input.Param(":execution")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
err = serv.DeleteNamespace(c.Ctx.Request.Context(), id)
if err != nil {
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
c.Data["json"] = id
c.ServeJSON()
}
// @Title CreateAdmiraltySource
// @Description Create an Admiralty Source on remote cluster
// @Param execution path string true "execution id of the workflow"
// @Success 201
// @router /source/:execution [post]
func (c *AdmiraltyController) CreateAdmiraltySource() {
execution := c.Ctx.Input.Param(":execution")
fmt.Println("execution :: ", execution)
fmt.Println("input :: ", c.Ctx.Input)
serv, err := infrastructure.NewKubernetesService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(), execution)
if err != nil {
if apierrors.IsAlreadyExists(err) {
c.Ctx.Output.SetStatus(409)
c.Data["json"] = map[string]string{"info": "A source already exists for this namespace : " + execution}
c.ServeJSON()
return
}
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
// TODO : Return a description of the created resource
var respData map[string]interface{}
err = json.Unmarshal(res, &respData)
if err != nil {
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
c.Ctx.Output.SetStatus(201)
c.Data["json"] = respData
c.ServeJSON()
}
// @Title CreateAdmiraltyTarget
// @Description Create an Admiralty Target in the namespace associated to the executionID
// @Param execution path string true "execution id of the workflow"
// @Param peer path string true "peerId of the peer the target points to"
// @Success 201
// @router /target/:execution/:peer [post]
func (c *AdmiraltyController) CreateAdmiraltyTarget() {
var data map[string]interface{}
execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
if execution == "" || peerId == "" {
c.Ctx.Output.SetStatus(400)
c.Data["json"] = map[string]string{"error": "parameters can be empty " + "execution: " + execution + " peer: " + peerId}
c.ServeJSON()
return
}
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(), execution, peerId)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
if resp == nil {
fmt.Println("Error while trying to create Admiralty target")
fmt.Println(resp)
fmt.Println(err)
c.Ctx.Output.SetStatus(401)
c.Data["json"] = map[string]string{"error": "Could not perform the action"}
c.ServeJSON()
return
}
err = json.Unmarshal(resp, &data)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
c.Ctx.Output.SetStatus(201)
c.Data["json"] = data
c.ServeJSON()
}
// @Title GetKubeSecret
// @Description Retrieve the secret created from a Kubeconfig that will be associated to an Admiralty Target
// @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Success 200
// @router /secret/:execution/:peer [get]
func (c *AdmiraltyController) GetKubeSecret() {
var data map[string]interface{}
execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(), execution, peerId)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
if resp == nil {
c.Ctx.Output.SetStatus(404)
c.ServeJSON()
return
}
err = json.Unmarshal(resp, &data)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.ServeJSON()
c.Data["json"] = map[string]string{"error": err.Error()}
return
}
c.Data["json"] = data
c.ServeJSON()
}
// @Title CreateKubeSecret
// @Description Creat a secret from a Kubeconfig that will be associated to an Admiralty Target
// @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Param kubeconfig body controllers.RemoteKubeconfig true "Kubeconfig to use when creating secret"
// @Success 201
// @router /secret/:execution/:peer [post]
func (c *AdmiraltyController) CreateKubeSecret() {
var kubeconfig RemoteKubeconfig
var respData map[string]interface{}
data := c.Ctx.Input.CopyBody(100000)
err := json.Unmarshal(data, &kubeconfig)
if err != nil {
fmt.Println("Error when retrieving the data for kubeconfig from request")
fmt.Println(err)
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(), *kubeconfig.Data, execution, peerId)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
err = json.Unmarshal(resp, &respData)
c.Ctx.Output.SetStatus(201)
c.Data["json"] = respData
c.ServeJSON()
}
// @name GetAdmiraltyNodes
// @description Allows user to test if an admiralty connection has already been established : Target and valid Secret set up on the local host and Source set up on remote host
// @Param execution path string true "execution id of the workflow"
// @Param peer path string true "UUID of the peer to which the resource is linked"
// @Success 200
// @router /node/:execution/:peer [get]
func (c *AdmiraltyController) GetNodeReady() {
var secret v1.Secret
execution := c.Ctx.Input.Param(":execution")
peerId := c.Ctx.Input.Param(":peer")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
node, err := serv.GetOneNode(c.Ctx.Request.Context(), execution, peerId)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
if node == nil {
c.Ctx.Output.SetStatus(404)
c.Data["json"] = map[string]string{
"node": "the node for " + execution + " can't be found, make sure both target and source resources are set up on local and remote hosts",
}
c.ServeJSON()
return
}
resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(), execution, peerId)
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
if resp == nil {
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": "Nodes was up but the secret can't be found"}
c.ServeJSON()
return
}
// Extract JWT token RS265 encoded
var editedKubeconfig map[string]interface{}
json.Unmarshal(resp, &secret)
byteEditedKubeconfig := secret.Data["config"]
err = yaml.Unmarshal(byteEditedKubeconfig, &editedKubeconfig)
// err = json.Unmarshal(byteEditedKubeconfig,&editedKubeconfig)
if err != nil {
fmt.Println("Error while retrieving the kubeconfig from secret-", execution)
fmt.Println(err)
c.Ctx.Output.SetStatus(500)
c.Data["json"] = err
c.ServeJSON()
return
}
token, err := retrieveTokenFromKonfig(editedKubeconfig)
if err != nil {
fmt.Println("Error while trying to retrieve token for kubeconfing")
fmt.Println(err)
HandleControllerErrors(c.Controller, 500, &err, nil)
}
// Decode token
isExpired, err := isTokenExpired(token)
if err != nil {
fmt.Println("Error veryfing token's expiration")
c.Ctx.Output.SetStatus(500)
c.Data["json"] = err
c.ServeJSON()
}
if *isExpired {
c.Data["json"] = map[string]interface{}{
"token": "token in the secret is expired and must be regenerated",
"node": node,
}
c.Ctx.Output.SetStatus(410)
c.ServeJSON()
}
c.Data["json"] = map[string]interface{}{"node": node, "token": true}
c.ServeJSON()
}
func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string, error) {
var kubeUsers []KubeUser
b, err := yaml.Marshal(editedKubeconfig["users"])
if err != nil {
fmt.Println("Error while retrieving the users attribute from the Kubeconfig")
fmt.Println(err)
return "", err
}
err = yaml.Unmarshal(b, &kubeUsers)
if err != nil {
fmt.Println("Error while unmarshalling users attribute from kubeconfig")
fmt.Println(err)
return "", nil
}
fmt.Println(kubeUsers)
token := kubeUsers[0].User.Token
return token, nil
}
func isTokenExpired(token string) (*bool, error) {
logger := oclib.GetLogger()
t, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
if err != nil {
fmt.Println("couldn't decode token")
return nil, err
}
expiration, err := t.Claims.GetExpirationTime()
if err != nil {
fmt.Println("Error while checking token's expiration time")
return nil, err
}
logger.Debug().Msg("Expiration date : " + expiration.UTC().Format("2006-01-02T15:04:05"))
logger.Debug().Msg(fmt.Sprint("Now : ", time.Now().Unix()))
logger.Debug().Msg(fmt.Sprint("Token : ", expiration.Unix()))
expired := expiration.Unix() < time.Now().Unix()
return &expired, nil
}
// @name Get Admiralty Kubeconfig
// @description Retrieve a kubeconfig from the host with the token to authenticate as the SA from the namespace identified with execution id
// @Param execution path string true "execution id of the workflow"
// @Success 200
// @router /kubeconfig/:execution [get]
func (c *AdmiraltyController) GetAdmiraltyKubeconfig() {
execution := c.Ctx.Input.Param(":execution")
serv, err := infrastructure.NewService()
if err != nil {
// change code to 500
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
generatedToken, err := serv.GenerateToken(c.Ctx.Request.Context(), execution, 3600)
if err != nil {
fmt.Println("Couldn't generate a token for ns-", execution)
fmt.Println(err)
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
kubeconfig, err := NewHostKubeWithToken(generatedToken)
if err != nil {
fmt.Println("Could not retrieve the Kubeconfig edited with token")
fmt.Println(err)
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
return
}
b, err := json.Marshal(kubeconfig)
if err != nil {
fmt.Println("Error while marshalling kubeconfig")
c.Ctx.Output.SetStatus(500)
c.Data["json"] = map[string]string{"error": err.Error()}
c.ServeJSON()
}
encodedKubeconfig := base64.StdEncoding.EncodeToString(b)
c.Data["json"] = map[string]string{
"data": encodedKubeconfig,
}
json.NewEncoder(c.Ctx.ResponseWriter)
c.ServeJSON()
}
func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error) {
if len(token) == 0 {
return nil, fmt.Errorf("you didn't provide a token to be inserted in the Kubeconfig")
}
encodedCA := base64.StdEncoding.EncodeToString([]byte(conf.GetConfig().KubeCA))
hostKube := models.KubeConfigValue{
APIVersion: "v1",
CurrentContext: "default",
Kind: "Config",
Preferences: struct{}{},
Clusters: []models.KubeconfigNamedCluster{
{
Name: "default",
Cluster: models.KubeconfigCluster{
Server: "https://" + conf.GetConfig().KubeHost + ":6443",
CertificateAuthorityData: encodedCA,
},
},
},
Contexts: []models.KubeconfigNamedContext{
{
Name: "default",
Context: models.KubeconfigContext{
Cluster: "default",
User: "default",
},
},
},
Users: []models.KubeconfigUser{
{
Name: "default",
User: models.KubeconfigUserKeyPair{
Token: token,
},
},
},
}
return &hostKube, nil
}

View File

@ -4,19 +4,14 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"oc-datacenter/infrastructure"
"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/booking"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
b "cloud.o-forge.io/core/oc-lib/models/booking"
beego "github.com/beego/beego/v2/server/web"
"github.com/gorilla/websocket"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
@ -25,8 +20,6 @@ type BookingController struct {
beego.Controller
}
var BookingExample booking.Booking
// @Title Search
// @Description search bookings by execution
// @Param id path string true "id execution"
@ -113,27 +106,6 @@ func (o *BookingController) Get() {
o.ServeJSON()
}
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true }, // allow all origins
}
// @Title Log
// @Description find booking by id
// @Param id path string true "the id you want to get"
// @Success 200 {booking} models.booking
// @router /:id [get]
func (o *BookingController) Log() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
id := o.Ctx.Input.Param(":id")
conn, err := upgrader.Upgrade(o.Ctx.ResponseWriter, o.Ctx.Request, nil)
if err != nil {
o.Ctx.WriteString("WebSocket upgrade failed: " + err.Error())
return
}
defer conn.Close()
infrastructure.NewPrometheusService().Stream(id, 1*time.Second, user, peerID, groups, conn)
}
// @Title Update
// @Description create computes
// @Param id path string true "the compute id you want to get"
@ -155,7 +127,7 @@ func (o *BookingController) Put() {
o.ServeJSON()
return
}
booking := book.Data.(*booking.Booking)
booking := book.Data.(*b.Booking)
if time.Now().After(booking.ExpectedStartDate) {
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).UpdateOne(res, id)
} else {
@ -196,10 +168,12 @@ func (o *BookingController) Check() {
"error": errors.New("invalid date format"),
}
} else {
bks := &booking.Booking{} // create a new booking object
booking := &b.Booking{} // create a new booking object
isAvailable, err2 := booking.Check(id, date, &date2, 1) // check if the booking is available
fmt.Println(isAvailable, err2)
code := 200
err := ""
if isAvailable, err2 := bks.Check(id, date, &date2, 1); !isAvailable {
if !isAvailable {
code = 409
err = "booking not available"
if err2 != nil {
@ -208,7 +182,7 @@ func (o *BookingController) Check() {
}
o.Data["json"] = map[string]interface{}{
"data": map[string]interface{}{
"is_available": true,
"is_available": isAvailable,
},
"code": code,
"error": err,
@ -224,6 +198,7 @@ func (o *BookingController) Check() {
// @Success 200 {object} models.object
// @router / [post]
func (o *BookingController) Post() {
fmt.Println("POST")
/*
* This function is used to create a booking.
* It takes the following parameters:
@ -232,58 +207,15 @@ func (o *BookingController) Post() {
* - datacenter_resource_id: the id of the datacenter
* - workflow_execution: the workflow execution
*/
logger := oclib.GetLogger()
logger.Info().Msg("Received a Booking")
var resp booking.Booking
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
err := json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &resp)
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 422,
"error": err,
}
o.ServeJSON()
return
}
if resp.ResourceType == tools.COMPUTE_RESOURCE {
// later should check... health for any such as docker...
res := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_RESOURCE), user, peerID, groups, nil).LoadOne(resp.ResourceID)
if res.Err != "" {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": res.Code,
"error": res.Err,
}
o.ServeJSON()
return
}
/*serv, err := infrastructure.NewServiceByType(res.ToComputeResource().Infrastructure.String())
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 500,
"error": err,
}
o.ServeJSON()
return
}
if err := serv.CheckHealth(); err != nil {
o.Data["json"] = map[string]interface{}{
"data": nil,
"code": 500,
"error": err,
}
o.ServeJSON()
return
}*/
}
json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &resp)
dc_id := resp.ResourceID
// delete all previous bookings
isDraft := o.Ctx.Input.Query("is_draft")
res := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).Search(&dbs.Filters{And: map[string][]dbs.Filter{
"workflow_id": {{Operator: dbs.EQUAL.String(), Value: resp.WorkflowID}},
"resource_id": {{Operator: dbs.EQUAL.String(), Value: resp.ResourceID}},
"resource_id": {{Operator: dbs.EQUAL.String(), Value: dc_id}},
}}, "", isDraft == "true")
if res.Code != 200 {
o.Data["json"] = map[string]interface{}{
@ -307,159 +239,25 @@ func (o *BookingController) Post() {
o.ServeJSON()
return
}
if b.Data.(*booking.Booking).ResourceType == tools.COMPUTE_RESOURCE {
go func() {
time.Sleep(time.Until(b.Data.(*booking.Booking).ExpectedStartDate))
infrastructure.NewPrometheusService().Stream(b.Data.GetID(), 1*time.Second, user, peerID, groups, nil)
}()
}
logger.Info().Msg("Creating new namespace : " + resp.ExecutionsID)
if err := o.createNamespace(resp.ExecutionsID); err != nil {
logger.Debug().Msg("Error when creating a namespace")
fmt.Println(err.Error())
}
fmt.Println("there was an error creating the namespace", o.createNamespace(resp.ExecutionsID))
o.Data["json"] = map[string]interface{}{
"data": b.Data,
"data": []interface{}{b},
"code": 200,
"error": "",
}
o.ServeJSON()
}
// @Title ExtendForNamespace
// @Description ExtendForNamespace booking
// @Param namespace path string "targetted namespace"
// @Param resource_id path string "resource id"
// @Param end_date path string "the booking end date" format "2006-01-02T15:04:05"
// @Param is_draft query string false "draft wished"
// @Success 200 {object} models.object
// @router /extend/:resource_id/from_namespace/:namespace/to/:duration [post]
func (o *BookingController) ExtendForNamespace() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
resourceID := o.Ctx.Input.Param(":resource_id")
namespace := o.Ctx.Input.Param(":namespace")
duration, err := strconv.Atoi(o.Ctx.Input.Param(":duration"))
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": map[string]interface{}{
"is_available": false,
},
"code": 400,
"error": errors.New("invalid date format"),
}
} else {
o.extend(duration, resourceID, "executions_id", namespace, user, peerID, groups)
}
}
// @Title ExtendForExecution
// @Description ExtendForExecution booking
// @Param namespace path string "targetted namespace"
// @Param resource_id path string "resource id"
// @Param end_date path string "the booking end date" format "2006-01-02T15:04:05"
// @Param is_draft query string false "draft wished"
// @Success 200 {object} models.object
// @router /extend/:resource_id/from_execution/:execution_id/to/:duration [post]
func (o *BookingController) ExtendForExecution() {
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
resourceID := o.Ctx.Input.Param(":resource_id")
executionID := o.Ctx.Input.Param(":execution_id")
duration, err := strconv.Atoi(o.Ctx.Input.Param(":duration"))
if err != nil {
o.Data["json"] = map[string]interface{}{
"data": map[string]interface{}{
"is_available": false,
},
"code": 400,
"error": errors.New("invalid date format"),
}
} else {
o.extend(duration, resourceID, "execution_id", executionID, user, peerID, groups)
}
}
func (o *BookingController) extend(duration int, resourceID string, key string, namespace string, user string, peerID string, groups []string) {
/*
* This function is used to check if a booking is available for a specific datacenter.
* It takes the following parameters:
* - id: the id of the datacenter
* - start_date: the start date of the booking/search/execution/:id
* - end_date: the end date of the booking
*/
req := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil)
res := req.Search(&dbs.Filters{
Or: map[string][]dbs.Filter{
"expected_end_date": {
{Operator: dbs.GTE.String(), Value: time.Now()},
{Operator: dbs.EQUAL.String(), Value: bson.TypeNull},
},
},
And: map[string][]dbs.Filter{
"resource_id": {{Operator: dbs.EQUAL.String(), Value: resourceID}},
key: {{Operator: dbs.EQUAL.String(), Value: namespace}},
"expected_start_date": {{Operator: dbs.GTE.String(), Value: time.Now()}},
},
}, "", false)
if res.Err == "" && len(res.Data) > 0 {
var id string
datas := []utils.DBObject{}
for _, b := range res.Data {
book := b.(*booking.Booking)
if book.ExpectedEndDate != nil {
bb := book.ExpectedEndDate.Add(time.Duration(duration) * time.Second)
if isAvailable, err := (&booking.Booking{}).Check(id, (*book.ExpectedEndDate).Add(1*time.Second), &bb, 1); isAvailable && err == nil {
book.ExpectedEndDate = &bb
result := req.UpdateOne(book.Serialize(book), book.GetID())
datas = append(datas, result.Data)
}
}
}
o.Data["json"] = map[string]interface{}{
"data": datas,
"code": 200,
"error": "",
}
o.ServeJSON()
return
} else if res.Err == "" {
o.Data["json"] = map[string]interface{}{
"data": map[string]interface{}{
"is_available": false,
},
"code": 400,
"error": errors.New("can't find any booking to extend"),
}
} else {
o.Data["json"] = res
}
o.ServeJSON()
}
func (o *BookingController) createNamespace(ns string) error {
/*
* This function is used to create a namespace.
* It takes the following parameters:
* - ns: the namespace you want to create
*/
logger := oclib.GetLogger()
serv, err := infrastructure.NewService()
if err != nil {
return nil
}
ok, err := serv.GetNamespace(o.Ctx.Request.Context(), ns)
if ok != nil && err == nil {
logger.Debug().Msg("A namespace with name " + ns + " already exists")
return nil
}
if err != nil {
return err
}
err = serv.CreateNamespace(o.Ctx.Request.Context(), ns)
if err != nil {
return err
@ -485,5 +283,6 @@ func (o *BookingController) createNamespace(ns string) error {
if err != nil {
return err
}
fmt.Println("ROLLLLLE BIND")
return serv.CreateRoleBinding(o.Ctx.Request.Context(), ns, "argo-role-binding", role)
}

View File

@ -21,12 +21,12 @@ func (o *DatacenterController) GetAll() {
isDraft := o.Ctx.Input.Query("is_draft")
storages := oclib.NewRequest(oclib.LibDataEnum(oclib.STORAGE_RESOURCE), user, peerID, groups, nil).Search(&dbs.Filters{
Or: map[string][]dbs.Filter{
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
"abstractintanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}, "", isDraft == "true")
computes := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_RESOURCE), user, peerID, groups, nil).Search(&dbs.Filters{
Or: map[string][]dbs.Filter{
"abstractinstanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
"abstractintanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}, "", isDraft == "true")
storages.Data = append(storages.Data, computes.Data...)
@ -49,15 +49,15 @@ func (o *DatacenterController) Get() {
id := o.Ctx.Input.Param(":id")
storages := oclib.NewRequest(oclib.LibDataEnum(oclib.STORAGE_RESOURCE), 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}},
"abstractintanciatedresource.abstractresource.abstractobject.id": {{Operator: dbs.EQUAL.String(), Value: id}},
"abstractintanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}, "", isDraft == "true")
if len(storages.Data) == 0 {
computes := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_RESOURCE), 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}},
"abstractintanciatedresource.abstractresource.abstractobject.id": {{Operator: dbs.EQUAL.String(), Value: id}},
"abstractintanciatedresource.abstractresource.abstractobject.creator_id": {{Operator: dbs.EQUAL.String(), Value: peerID}},
},
}, "", isDraft == "true")
if len(computes.Data) == 0 {

View File

@ -1,21 +0,0 @@
package controllers
import (
"fmt"
beego "github.com/beego/beego/v2/server/web"
)
func HandleControllerErrors(c beego.Controller, code int, err *error, data *map[string]interface{}, messages ...string) {
for _, mess := range messages {
fmt.Println(mess)
}
if data != nil {
c.Data["json"] = data
}
if err != nil {
c.Data["json"] = map[string]string{"error": (*err).Error()}
}
c.Ctx.Output.SetStatus(code)
c.ServeJSON()
}

View File

@ -1,122 +0,0 @@
package controllers
import (
"oc-datacenter/infrastructure"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/models/live"
beego "github.com/beego/beego/v2/server/web"
)
type MinioController struct {
beego.Controller
}
// @Title CreateServiceAccounnt
// @Description Add a new ServiceAccount to a Minio server using its ID and an execution ID
// @Success 200
// @Param executions path string true "The executionsID of the execution"
// @Param minioId path string true "The ID of the Minio you want to reach"
// @router /serviceaccount/:minioId/:executions [post]
func (m *MinioController) CreateServiceAccount() {
_, peerID, _ := oclib.ExtractTokenInfo(*m.Ctx.Request)
// This part is solely for dev purposes and should be removed once test on
executionsId := m.Ctx.Input.Param(":executions")
minioId := m.Ctx.Input.Param(":minioId")
// retrieve the live storage with the minioId
s := oclib.NewRequest(oclib.LibDataEnum(oclib.STORAGE_RESOURCE), "", "", []string{}, nil).LoadOne(minioId)
if s.Err != "" {
m.Ctx.Output.SetStatus(400)
m.Data["json"] = map[string]interface{}{"error": " Could not load the storage resource with id " + minioId + ": " + s.Err}
m.ServeJSON()
return
}
live := findLiveStorage(minioId, peerID)
if live == nil {
m.Ctx.Output.SetStatus(404)
m.Data["json"] = map[string]interface{}{"error":"could not find the Minio instance " + s.Err}
m.ServeJSON()
return
}
url := live.Source
service := infrastructure.NewMinioService(url)
// call the method ctrating the svcacc
err := service.CreateClient()
if err != nil {
m.Ctx.Output.SetStatus(500)
m.Data["json"] = map[string]interface{}{"error":"could not create the client for " + minioId + " : " + err.Error()}
m.ServeJSON()
return
}
access, secret, err := service.CreateCredentials(executionsId)
if err != nil {
m.Ctx.Output.SetStatus(500)
m.Data["json"] = map[string]interface{}{"error":"could not create the service account for " + minioId + " : " + err.Error()}
m.ServeJSON()
return
}
// test if the namespace exists
k, err := infrastructure.NewService()
if err != nil {
m.Ctx.Output.SetStatus(500)
m.Data["json"] = map[string]string{"error": err.Error()}
m.ServeJSON()
return
}
ns, err := k.GetNamespace(m.Ctx.Request.Context(), executionsId)
if ns == nil {
m.Ctx.Output.SetStatus(403)
m.Data["json"] = map[string]string{"error":"Could not find the namespace corresponding to executionsID " + executionsId}
m.ServeJSON()
return
}
if err != nil {
m.Ctx.Output.SetStatus(500)
m.Data["json"] = map[string]string{"error": "Error when trying to check if namespace " + executionsId + " exists : " + err.Error()}
m.ServeJSON()
return
}
// store the credentials in the namespace
err = k.CreateSecret(m.Ctx.Request.Context(), minioId, executionsId, access, secret)
if err != nil {
m.Ctx.Output.SetStatus(500)
m.Data["json"] = map[string]string{"error": "Error when storing Minio serviceAccount credentials in namespace " + executionsId + " exists : " + err.Error()}
m.ServeJSON()
return
}
m.Data["json"] = map[string]string{"success": "created secret " + executionsId + "-secret-sa in namespace ns-" + executionsId}
m.ServeJSON()
}
func findLiveStorage(storageId string, peerId string) *live.LiveStorage {
res := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE),"",peerId,[]string{},nil).LoadAll(false)
if res.Err != "" {
l := oclib.GetLogger()
l.Error().Msg(res.Err)
return nil
}
for _, dbo := range res.Data {
r := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_STORAGE),"","",[]string{},nil).LoadOne(dbo.GetID())
l := r.ToLiveStorage()
for _, id := range l.ResourcesID {
if id == storageId {
return l
}
}
}
return nil
}

View File

@ -40,7 +40,7 @@ func (o *SessionController) GetToken() {
return
}
fmt.Println("BLAPO", id, duration)
token, err := serv.GenerateToken(o.Ctx.Request.Context(), id, duration)
token, err := serv.GetToken(o.Ctx.Request.Context(), id, duration)
if err != nil {
// change code to 500
o.Ctx.Output.SetStatus(500)

View File

@ -15,10 +15,7 @@ type VersionController struct {
// @Success 200
// @router / [get]
func (c *VersionController) GetAll() {
c.Data["json"] = map[string]string{
"service": "oc-datacenter",
"version": "1",
}
c.Data["json"] = map[string]string{"version": "1"}
c.ServeJSON()
}

View File

@ -1,7 +1,5 @@
{
"MONGO_URL": "mongodb://mongo:27017/",
"NATS_URL": "nats://localhost:4222",
"MONGO_DATABASE": "DC_myDC",
"KUBERNETES_SERVICE_HOST": "172.16.0.183",
"port": "8092"
"port": 8080,
"MONGO_URL":"mongodb://localhost:27017/",
"MONGO_DATABASE":"DC_myDC"
}

34
docker-compose.base.yml Normal file
View File

@ -0,0 +1,34 @@
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:
external: true
# name: catalog

View File

@ -2,9 +2,6 @@ version: '3.4'
services:
oc-datacenter:
env_file:
- path: ./env.env
required: false
environment:
- MONGO_DATABASE=DC_myDC
image: 'oc-datacenter:latest'
@ -13,17 +10,14 @@ services:
labels:
- "traefik.enable=true"
- "traefik.http.routers.datacenter.entrypoints=web"
- "traefik.http.routers.datacenter.rule=PathPrefix(`/datacenter`)"
- "traefik.http.services.datacenter.loadbalancer.server.port=8080"
- "traefik.http.middlewares.datacenter-rewrite.replacepathregex.regex=^/datacenter(.*)"
- "traefik.http.middlewares.datacenter-rewrite.replacepathregex.replacement=/oc$$1"
- "traefik.http.routers.datacenter.middlewares=datacenter-rewrite"
- "traefik.http.middlewares.datacenter.forwardauth.address=http://oc-auth:8080/oc/forward"
- "traefik.http.middlewares.auth.forwardauth.address=http://oc-auth:8080/oc/forward"
- "traefik.http.routers.workflow.rule=PathPrefix(/datacenter)"
- "traefik.http.routers.datacenter.tls=false"
- "traefik.http.routers.datacenter.middlewares=auth"
container_name: oc-datacenter
networks:
- oc
- catalog
networks:
oc:
catalog:
external: true

View File

@ -1,5 +1,9 @@
{
"MONGO_URL":"mongodb://mongo:27017/",
"NATS_URL":"nats://nats:4222",
"MONGO_DATABASE":"DC_myDC"
"MONGO_DATABASE":"DC_myDC",
"KUBERNETES_SERVICE_HOST" : "192.168.1.69",
"KUBE_CA" : "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTVlk3ZHZhNEdYTVdkMy9jMlhLN3JLYjlnWXgyNSthaEE0NmkyNVBkSFAKRktQL2UxSVMyWVF0dzNYZW1TTUQxaStZdzJSaVppNUQrSVZUamNtNHdhcnFvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWtlUVJpNFJiODduME5yRnZaWjZHClc2SU55NnN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRXA5ck04WmdNclRZSHYxZjNzOW5DZXZZeWVVa3lZUk4KWjUzazdoaytJS1FDSVFDbk05TnVGKzlTakIzNDFacGZ5ays2NEpWdkpSM3BhcmVaejdMd2lhNm9kdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_CERT":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJWUxWNkFPQkdrU1F3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl6TVRFeU1ETTJNQjRYRFRJME1EZ3dPREV3TVRNMU5sb1hEVEkxTURndwpPREV3TVRNMU5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJGQ2Q1MFdPeWdlQ2syQzcKV2FrOWY4MVAvSkJieVRIajRWOXBsTEo0ck5HeHFtSjJOb2xROFYxdUx5RjBtOTQ2Nkc0RmRDQ2dqaXFVSk92Swp3NVRPNnd5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVFJkOFI5cXVWK2pjeUVmL0ovT1hQSzMyS09XekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQTArbThqTDBJVldvUTZ0dnB4cFo4NVlMalF1SmpwdXM0aDdnSXRxS3NmUVVDSUI2M2ZNdzFBMm5OVWU1TgpIUGZOcEQwSEtwcVN0Wnk4djIyVzliYlJUNklZCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFRc3hXWk9pbnIrcVp4TmFEQjVGMGsvTDF5cE01VHAxOFRaeU92ektJazQKRTFsZWVqUm9STW0zNmhPeVljbnN3d3JoNnhSUnBpMW5RdGhyMzg0S0Z6MlBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBYZkVmYXJsZm8zTWhIL3lmemx6Cnl0OWlqbHN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUxJL2dNYnNMT3MvUUpJa3U2WHVpRVMwTEE2cEJHMXgKcnBlTnpGdlZOekZsQWlFQW1wdjBubjZqN3M0MVI0QzFNMEpSL0djNE53MHdldlFmZWdEVGF1R2p3cFk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"KUBE_DATA": "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5ZS1BFb1dhd1NKUzJlRW5oWmlYMk5VZlY1ZlhKV2krSVNnV09TNFE5VTlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUozblJZN0tCNEtUWUx0WnFUMS96VS84a0Z2Sk1lUGhYMm1Vc25pczBiR3FZblkyaVZEeApYVzR2SVhTYjNqcm9iZ1YwSUtDT0twUWs2OHJEbE03ckRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
}

View File

@ -1,21 +0,0 @@
@startuml
boundary "oc-workflow" as workflow
boundary "oc-monitord" as monitord
boundary "local oc-datacenter" as locdc
boundary "remote oc-datacenter" as rocdc
workflow --> locdc : POST /booking/ {booking object}
locdc --> locdc : create Namespace + ServiceAccount
workflow --> rocdc : POST /boking/
rocdc --> rocdc : create \nNamespace + \nServiceAccount
monitord --> monitord : retrieves a Workflow to execute
monitord --> monitord : workflow needs repartited execution
' monitord --> rocdc : POST /????? (route that use the same \nmethods as /booking/ to create NS & SA)
monitord --> rocdc : POST /admiralty/source
monitord --> rocdc : GET /admiralty/kubeconfig/:execution_id
rocdc -> monitord : base64 encoded edited kubeconfig with token (**how to make it secure** ???)
monitord --> locdc : POST /admiralty/secret/:execution_id
monitord --> locdc : POST /admiralty/target/:execution_id
monitord --> locdc : GET /admiralty/nodes/:execution_id \n(if the node is up it means ALL GOOD)
@enduml

View File

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

90
go.mod
View File

@ -1,22 +1,16 @@
module oc-datacenter
go 1.24.2
go 1.23.0
toolchain go1.24.4
toolchain go1.23.3
require (
cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416
github.com/beego/beego/v2 v2.3.8
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/gorilla/websocket v1.5.3
github.com/minio/madmin-go/v4 v4.1.1
github.com/minio/minio-go/v7 v7.0.94
github.com/necmettindev/randomstring v0.1.0
go.mongodb.org/mongo-driver v1.17.4
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.3
cloud.o-forge.io/core/oc-lib v0.0.0-20250213085018-271cc2caa026
github.com/beego/beego/v2 v2.3.1
go.mongodb.org/mongo-driver v1.17.1
k8s.io/api v0.32.1
k8s.io/apimachinery v0.32.1
k8s.io/client-go v0.32.1
)
require (
@ -24,85 +18,63 @@ require (
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/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // 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.26.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/goraz/onion v0.1.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // 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.43.0 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nats.go v1.37.0 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/prometheus/prom2json v1.4.2 // indirect
github.com/prometheus/prometheus v0.304.1 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/safchain/ethtool v0.6.1 // indirect
github.com/secure-io/sio-go v0.3.1 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
github.com/shirou/gopsutil/v4 v4.25.5 // indirect
github.com/smartystreets/goconvey v1.7.2 // indirect
github.com/tinylib/msgp v1.3.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/x448/float16 v0.8.4 // 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
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

199
go.sum
View File

@ -1,14 +1,14 @@
cloud.o-forge.io/core/oc-lib v0.0.0-20250624102227-e600fedcab06 h1:+RSv62uIC7wsmibsp1XTanQMNznNeOGgPpfhb6ZHT4c=
cloud.o-forge.io/core/oc-lib v0.0.0-20250624102227-e600fedcab06/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250626135921-34b7cdcf06b0 h1:FGWOxAAw3Zh19A2FUeAbttsfuFJ3JDli/un9iCu6uD8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250626135921-34b7cdcf06b0/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416 h1:C51EHyggOm2QZ7INqhKOiyapTIbtRi8y2eJjVXzdtnM=
cloud.o-forge.io/core/oc-lib v0.0.0-20250626142041-d58dc5602416/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f h1:6V+Z81ywYoDYSVMnM4PVaJYXFgCN3xSG3ddiUPn4jL8=
cloud.o-forge.io/core/oc-lib v0.0.0-20250205160221-88b7cfe2fd0f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250212150815-c7c1535ba91a h1:kfTSMCOxYiVGNJWD4OrV7YYTf6t4geKxWpGz4EucpEA=
cloud.o-forge.io/core/oc-lib v0.0.0-20250212150815-c7c1535ba91a/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250213072626-4920322d0afb h1:EybP8jPpIiN5RLiBxr3cvvF9KIaC+uWvzM23ga0t1yI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250213072626-4920322d0afb/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
cloud.o-forge.io/core/oc-lib v0.0.0-20250213085018-271cc2caa026 h1:CYwpofGfpAhMDrT6jqvu9NI/tcgxCD8PKJZDKEfTvVI=
cloud.o-forge.io/core/oc-lib v0.0.0-20250213085018-271cc2caa026/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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=
@ -18,14 +18,11 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
@ -36,19 +33,14 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
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-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -59,42 +51,31 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
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-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
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=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/goraz/onion v0.1.3 h1:KhyvbDA2b70gcz/d5izfwTiOH8SmrvV43AsVzpng3n0=
github.com/goraz/onion v0.1.3/go.mod h1:XEmz1XoBz+wxTgWB8NwuvRm4RAu3vKxvrmYtzK+XCuQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -107,40 +88,28 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
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/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/minio/madmin-go/v4 v4.1.1 h1:Y7JHamjTwnyvXO9aike5SC0c0sNsbs/NpG37c525oe4=
github.com/minio/madmin-go/v4 v4.1.1/go.mod h1:16tMDVIHWcp1zrmL6XrgCwPlZC7kB3UNNs5GDNnjYLQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM=
github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -157,62 +126,38 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
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/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/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
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/necmettindev/randomstring v0.1.0 h1:HeU/mfLCd/5E9At7xznbTeEw5YldGW92fvK8lWtvPwE=
github.com/necmettindev/randomstring v0.1.0/go.mod h1:h2nX9Jl0TLImuMt++XfLStVr8N76BmmP5D5EhLq0KEQ=
github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
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_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/prom2json v1.4.2 h1:PxCTM+Whqi/eykO1MKsEL0p/zMpxp9ybpsmdFamw6po=
github.com/prometheus/prom2json v1.4.2/go.mod h1:zuvPm7u3epZSbXPWHny6G+o8ETgu6eAK3oPr6yFkRWE=
github.com/prometheus/prometheus v0.304.1 h1:e4kpJMb2Vh/PcR6LInake+ofcvFYHT+bCfmBvOkaZbY=
github.com/prometheus/prometheus v0.304.1/go.mod h1:ioGx2SGKTY+fLnJSQCdTHqARVldGNS8OlIe3kvp98so=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/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.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
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 v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
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/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/safchain/ethtool v0.6.1 h1:mhRnXE1H8fV8TTXh/HdqE4tXtb57r//BQh5pPYMuM5k=
github.com/safchain/ethtool v0.6.1/go.mod h1:JzoNbG8xeg/BeVeVoMCtCb3UPWoppZZbFpA+1WFh+M0=
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
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/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc=
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
@ -223,15 +168,14 @@ github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
@ -245,20 +189,15 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -269,60 +208,55 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
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.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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=
@ -332,16 +266,15 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=

View File

@ -4,40 +4,21 @@ import (
"context"
"errors"
"oc-datacenter/conf"
v1 "k8s.io/api/core/v1"
)
type Infrastructure interface {
CreateNamespace(ctx context.Context, ns string) error
DeleteNamespace(ctx context.Context, ns string) error
GenerateToken(ctx context.Context, ns string, duration int) (string, error)
GetToken(ctx context.Context, ns string, duration int) (string, error)
CreateServiceAccount(ctx context.Context, ns string) error
CreateRoleBinding(ctx context.Context, ns string, roleBinding string, role string) error
CreateRole(ctx context.Context, ns string, role string, groups [][]string, resources [][]string, verbs [][]string) error
GetTargets(ctx context.Context) ([]string, error)
CreateAdmiraltySource(context context.Context, executionId string) ([]byte, error)
CreateKubeconfigSecret(context context.Context, kubeconfig string, executionId string, peerId string) ([]byte, error)
GetKubeconfigSecret(context context.Context, executionId string, peerId string) ([]byte, error)
CreateAdmiraltyTarget(context context.Context, executionId string, peerId string) ([]byte, error)
GetOneNode(context context.Context, executionID string, peerId string) (*v1.Node, error)
GetNamespace(context context.Context, executionID string) (*v1.Namespace, error)
CreateSecret(context context.Context, minioId string, executionID string, access string, secret string) error
CheckHealth() error
}
var _service = map[string]func() (Infrastructure, error){
"kubernetes": NewKubernetesService,
}
func NewServiceByType(t string) (Infrastructure, error) {
service, ok := _service[t]
if !ok {
return nil, errors.New("service not found")
}
return service()
}
func NewService() (Infrastructure, error) {
service, ok := _service[conf.GetConfig().Mode]
if !ok {

View File

@ -2,57 +2,22 @@ package infrastructure
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"oc-datacenter/conf"
"strings"
"time"
oclib "cloud.o-forge.io/core/oc-lib"
authv1 "k8s.io/api/authentication/v1"
v1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
apply "k8s.io/client-go/applyconfigurations/core/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
var gvrSources = schema.GroupVersionResource{Group: "multicluster.admiralty.io", Version: "v1alpha1", Resource: "sources"}
var gvrTargets = schema.GroupVersionResource{Group: "multicluster.admiralty.io", Version: "v1alpha1", Resource: "targets"}
type KubernetesService struct {
Set *kubernetes.Clientset
}
func NewDynamicClient() (*dynamic.DynamicClient, error) {
config := &rest.Config{
Host: conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort,
TLSClientConfig: rest.TLSClientConfig{
CAData: []byte(conf.GetConfig().KubeCA),
CertData: []byte(conf.GetConfig().KubeCert),
KeyData: []byte(conf.GetConfig().KubeData),
},
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, errors.New("Error creating Dynamic client: " + err.Error())
}
if dynamicClient == nil {
return nil, errors.New("Error creating Dynamic client: dynamicClient is nil")
}
return dynamicClient, nil
}
func NewKubernetesService() (Infrastructure, error) {
config := &rest.Config{
Host: conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort,
@ -62,35 +27,6 @@ func NewKubernetesService() (Infrastructure, error) {
KeyData: []byte(conf.GetConfig().KubeData),
},
}
// Create clientset
clientset, err := kubernetes.NewForConfig(config)
fmt.Println("NewForConfig", clientset, err)
if err != nil {
return nil, errors.New("Error creating Kubernetes client: " + err.Error())
}
if clientset == nil {
return nil, errors.New("Error creating Kubernetes client: clientset is nil")
}
return &KubernetesService{
Set: clientset,
}, nil
}
func NewRemoteKubernetesService(url string, ca string, cert string, key string) (Infrastructure, error) {
decodedCa, _ := base64.StdEncoding.DecodeString(ca)
decodedCert, _ := base64.StdEncoding.DecodeString(cert)
decodedKey, _ := base64.StdEncoding.DecodeString(key)
config := &rest.Config{
Host: url + ":6443",
TLSClientConfig: rest.TLSClientConfig{
CAData: decodedCa,
CertData: decodedCert,
KeyData: decodedKey,
},
}
// Create clientset
clientset, err := kubernetes.NewForConfig(config)
fmt.Println("NewForConfig", clientset, err)
@ -108,13 +44,9 @@ func NewRemoteKubernetesService(url string, ca string, cert string, key string)
func (k *KubernetesService) CreateNamespace(ctx context.Context, ns string) error {
// Define the namespace
fmt.Println("ExecutionID in CreateNamespace() : ", ns)
namespace := &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
Labels: map[string]string{
"multicluster-scheduler": "enabled",
},
},
}
// Create the namespace
@ -199,41 +131,15 @@ func (k *KubernetesService) CreateRoleBinding(ctx context.Context, ns string, ro
}
func (k *KubernetesService) DeleteNamespace(ctx context.Context, ns string) error {
targetGVR := schema.GroupVersionResource{
Group: "multicluster.admiralty.io",
Version: "v1alpha1",
Resource: "targets",
}
// Delete the Target
dyn, err := NewDynamicClient()
if err != nil {
return err
}
err = dyn.Resource(targetGVR).Namespace(ns).Delete(context.TODO(), "target-"+ns, metav1.DeleteOptions{})
if err != nil {
return err
}
err = k.Set.CoreV1().ServiceAccounts(ns).Delete(context.TODO(), "sa-"+ns, metav1.DeleteOptions{})
if err != nil {
return err
}
// Delete the namespace
if err := k.Set.CoreV1().Namespaces().Delete(ctx, ns, metav1.DeleteOptions{}); err != nil {
return errors.New("Error deleting namespace: " + err.Error())
}
LockKill.Lock()
Kill = append(Kill, ns)
LockKill.Unlock()
fmt.Println("Namespace deleted successfully!")
return nil
}
// Returns the string representing the token generated for the serviceAccount
// in the namespace identified by the value `ns` with the name sa-`ns`, which is valid for
// `duration` seconds
func (k *KubernetesService) GenerateToken(ctx context.Context, ns string, duration int) (string, error) {
func (k *KubernetesService) GetToken(ctx context.Context, ns string, duration int) (string, error) {
// Define TokenRequest (valid for 1 hour)
d := int64(duration)
tokenRequest := &authv1.TokenRequest{
@ -250,370 +156,3 @@ func (k *KubernetesService) GenerateToken(ctx context.Context, ns string, durati
}
return token.Status.Token, nil
}
// Needs refactoring :
// - Retrieving the metada (in a method that Unmarshall the part of the json in a metadata object)
func (k *KubernetesService) GetTargets(ctx context.Context) ([]string, error) {
var listTargets []string
resp, err := getCDRapiKube(*k.Set, ctx, "/apis/multicluster.admiralty.io/v1alpha1/targets")
if err != nil {
return nil, err
}
fmt.Println(string(resp))
var targetDict map[string]interface{}
err = json.Unmarshal(resp, &targetDict)
if err != nil {
fmt.Println("TODO: handle the error when unmarshalling k8s API response")
return nil, err
}
b, _ := json.MarshalIndent(targetDict, "", " ")
fmt.Println(string(b))
data := targetDict["items"].([]interface{})
for _, item := range data {
var metadata metav1.ObjectMeta
item := item.(map[string]interface{})
byteMetada, err := json.Marshal(item["metadata"])
if err != nil {
fmt.Println("Error while Marshalling metadata field")
return nil, err
}
err = json.Unmarshal(byteMetada, &metadata)
if err != nil {
fmt.Println("Error while Unmarshalling metadata field to the library object")
return nil, err
}
listTargets = append(listTargets, metadata.Name)
}
return listTargets, nil
}
// Admiralty Target allows a cluster to deploy pods to remote cluster
//
// The remote cluster must :
//
// - have declared a Source resource
//
// - have declared the same namespace as the one where the pods are created in the local cluster
//
// - have delcared a serviceAccount with sufficient permission to create pods
func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, executionId string, peerId string) ([]byte, error) {
exists, err := k.GetKubeconfigSecret(context, executionId, peerId)
if err != nil {
fmt.Println("Error verifying kube-secret before creating target")
return nil, err
}
if exists == nil {
fmt.Println("Target needs to be binded to a secret in namespace ", executionId)
return nil, nil // Maybe we could create a wrapper for errors and add more info to have
}
targetName := "target-" + getConcatenatedName(peerId, executionId)
target := map[string]interface{}{
"apiVersion": "multicluster.admiralty.io/v1alpha1",
"kind": "Target",
"metadata": map[string]interface{}{
"name": targetName,
"namespace": executionId,
"labels": map[string]interface{}{
"peer": peerId,
},
},
"spec": map[string]interface{}{
"kubeconfigSecret": map[string]string{
"name": "kube-secret-" + getConcatenatedName(peerId, executionId),
},
},
}
res, err := dynamicClientApply(executionId, targetName, gvrTargets, context, target)
if err != nil {
return nil, errors.New("Error when trying to apply Target definition :" + err.Error())
}
return res, nil
}
// Admiralty Source allows a cluster to receive pods from a remote cluster
//
// The source must be associated to a serviceAccount, which will execute the pods locally.
// This serviceAccount must have sufficient permission to create and patch pods
//
// This method is temporary to implement the use of Admiralty, but must be edited
// to rather contact the oc-datacenter from the remote cluster to create the source
// locally and retrieve the token for the serviceAccount
func (k *KubernetesService) CreateAdmiraltySource(context context.Context, executionId string) ([]byte, error) {
source := map[string]interface{}{
"apiVersion": "multicluster.admiralty.io/v1alpha1",
"kind": "Source",
"metadata": map[string]interface{}{
"name": "source-" + executionId,
"namespace": executionId,
},
"spec": map[string]interface{}{
"serviceAccountName": "sa-" + executionId,
},
}
res, err := dynamicClientApply(executionId, "source-"+executionId, gvrSources, context, source)
if err != nil {
return nil, errors.New("Error when trying to apply Source definition :" + err.Error())
}
return res, nil
}
// Create a secret from a kubeconfing. Use it to create the secret binded to an Admiralty
// target, which must contain the serviceAccount's token value
func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kubeconfig string, executionId string, peerId string) ([]byte, error) {
config, err := base64.StdEncoding.DecodeString(kubeconfig)
// config, err := base64.RawStdEncoding.DecodeString(kubeconfig)
if err != nil {
fmt.Println("Error while encoding kubeconfig")
fmt.Println(err)
return nil, err
}
secretApplyConfig := apply.Secret("kube-secret-"+getConcatenatedName(peerId, executionId),
executionId).
WithData(map[string][]byte{
"config": config,
},
)
// exists, err := k.GetKubeconfigSecret(context,executionId)
// if err != nil {
// fmt.Println("Error verifying if kube secret exists in namespace ", executionId)
// return nil, err
// }
// if exists != nil {
// fmt.Println("kube-secret already exists in namespace", executionId)
// fmt.Println("Overriding existing kube-secret with a newer resource")
// // TODO : implement DeleteKubeConfigSecret(executionID)
// deleted, err := k.DeleteKubeConfigSecret(executionId)
// _ = deleted
// _ = err
// }
resp, err := k.Set.CoreV1().
Secrets(executionId).
Apply(context,
secretApplyConfig,
metav1.ApplyOptions{
FieldManager: "admiralty-manager",
})
if err != nil {
fmt.Println("Error while trying to contact API to get secret kube-secret-" + executionId)
fmt.Println(err)
return nil, err
}
data, err := json.Marshal(resp)
if err != nil {
fmt.Println("Couldn't marshal resp from : ", data)
fmt.Println(err)
return nil, err
}
return data, nil
}
func (k *KubernetesService) GetKubeconfigSecret(context context.Context, executionId string, peerId string) ([]byte, error) {
resp, err := k.Set.CoreV1().
Secrets(executionId).
Get(context, "kube-secret-"+getConcatenatedName(peerId, executionId), metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
fmt.Println("kube-secret not found for execution", executionId)
return nil, nil
}
fmt.Println("Error while trying to contact API to get secret kube-secret-" + executionId)
fmt.Println(err)
return nil, err
}
data, err := json.Marshal(resp)
if err != nil {
fmt.Println("Couldn't marshal resp from : ", data)
fmt.Println(err)
return nil, err
}
return data, nil
}
func (k *KubernetesService) DeleteKubeConfigSecret(executionID string) ([]byte, error) {
return []byte{}, nil
}
func (k *KubernetesService) GetNamespace(context context.Context, executionID string)(*v1.Namespace,error){
resp, err := k.Set.CoreV1().Namespaces().Get(context,executionID,metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return nil, nil
}
if err != nil {
logger := oclib.GetLogger()
logger.Error().Msg("An error occured when trying to get namespace " + executionID + " : " + err.Error())
return nil, err
}
return resp, nil
}
func getCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string) ([]byte, error) {
resp, err := client.RESTClient().Get().
AbsPath(path).
DoRaw(ctx) // from https://stackoverflow.com/questions/60764908/how-to-access-kubernetes-crd-using-client-go
if err != nil {
fmt.Println("Error from k8s API when getting "+path+" : ", err)
return nil, err
}
return resp, nil
}
func dynamicClientApply(executionId string, resourceName string, resourceDefinition schema.GroupVersionResource, ctx context.Context, object map[string]interface{}) ([]byte, error) {
cli, err := NewDynamicClient()
if err != nil {
return nil, errors.New("Could not retrieve dynamic client when creating Admiralty Source : " + err.Error())
}
res, err := cli.Resource(resourceDefinition).
Namespace(executionId).
Apply(ctx,
resourceName,
&unstructured.Unstructured{Object: object},
metav1.ApplyOptions{
FieldManager: "kubectl-client-side-apply",
},
)
if err != nil {
o, err := json.Marshal(object)
fmt.Println("Error from k8s API when applying "+fmt.Sprint(string(o))+" to "+gvrSources.String()+" : ", err)
return nil, err
}
// We can add more info to the log with the content of resp if not nil
resByte, err := json.Marshal(res)
if err != nil {
// fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", res)
return nil, err
}
return resByte, nil
}
func (k *KubernetesService) CheckHealth() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Check API server connectivity
_, err := k.Set.ServerVersion()
if err != nil {
return fmt.Errorf("API server unreachable: %v", err)
}
// Check nodes status
nodes, err := k.Set.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list nodes: %v", err)
}
for _, node := range nodes.Items {
for _, condition := range node.Status.Conditions {
if condition.Type == "Ready" && condition.Status != "True" {
return fmt.Errorf("node %s not ready", node.Name)
}
}
}
// Optional: Check if all pods in kube-system are running
pods, err := k.Set.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list pods: %v", err)
}
for _, pod := range pods.Items {
if pod.Status.Phase != "Running" && pod.Status.Phase != "Succeeded" {
return fmt.Errorf("pod %s in namespace kube-system is %s", pod.Name, pod.Status.Phase)
}
}
return nil
}
// Returns the Kubernetes' Node object corresponding to the executionID if it exists on this host
//
// The node is created when an admiralty Target (on host) can connect to an admiralty Source (on remote)
func (k *KubernetesService) GetOneNode(context context.Context, executionID string, peerId string) (*v1.Node, error) {
concatenatedName := getConcatenatedName(peerId, executionID)
res, err := k.Set.CoreV1().
Nodes().
List(
context,
metav1.ListOptions{},
)
if err != nil {
fmt.Println("Error getting the list of nodes from k8s API")
fmt.Println(err)
return nil, err
}
for _, node := range res.Items {
if isNode := strings.Contains(node.Name, "admiralty-"+executionID+"-target-"+concatenatedName+"-"); isNode {
return &node, nil
}
}
return nil, nil
}
func (k *KubernetesService) CreateSecret(context context.Context, minioId string, executionID string, access string, secret string) error {
data := map[string][]byte{
"access-key": []byte(access),
"secret-key": []byte(secret),
}
s := v1.Secret{
Type: v1.SecretTypeOpaque,
Data: data,
ObjectMeta: metav1.ObjectMeta{
Name: minioId+"-secret-s3",
},
}
_, err := k.Set.CoreV1().Secrets(executionID).Create(context,&s,metav1.CreateOptions{})
if err != nil {
logger := oclib.GetLogger()
logger.Error().Msg("An error happened when creating the secret holding minio credentials in namespace " + executionID + " : " + err.Error())
return err
}
return nil
}
// Returns a concatenation of the peerId and namespace in order for
// kubernetes ressources to have a unique name, under 63 characters
// and yet identify which peer they are created for
func getConcatenatedName(peerId string, namespace string) string {
s := strings.Split(namespace, "-")[:2]
n := s[0] + "-" + s[1]
return peerId + "-" + n
}

View File

@ -1,106 +0,0 @@
package infrastructure
import (
"context"
"encoding/json"
"oc-datacenter/conf"
"github.com/minio/madmin-go/v4"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/necmettindev/randomstring"
)
type MinioService struct{
Url string
RootKey string
RootSecret string
MinioClient *madmin.AdminClient
}
type StatementEntry struct {
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource string `json:"Resource"`
}
type PolicyDocument struct {
Version string `json:"Version"`
Statement []StatementEntry `json:"Statement"`
}
func NewMinioService(url string) *MinioService {
return &MinioService{
Url: url,
RootKey: conf.GetConfig().MinioRootKey,
RootSecret: conf.GetConfig().MinioRootSecret,
}
}
func (m *MinioService) CreateClient() error {
cred := credentials.NewStaticV4(m.RootKey,m.RootSecret,"")
cli, err := madmin.NewWithOptions(m.Url, &madmin.Options{Creds: cred, Secure: false}) // Maybe in the future we should use the secure option ?
if err != nil {
return err
}
m.MinioClient = cli
return nil
}
func (m *MinioService) CreateCredentials(executionId string) (string,string,error){
policy := PolicyDocument{
Version: "2012-10-17",
Statement: []StatementEntry{
{
Effect: "Allow",
Action: []string{"s3:GetObject", "s3:PutObject"},
Resource: "arn:aws:s3:::"+executionId+"/*",
},
},
}
p, err := json.Marshal(policy)
if err != nil {
return "","",err
}
randAccess, randSecret := getRandomCreds()
req := madmin.AddServiceAccountReq{
Policy: p,
TargetUser: m.RootKey,
AccessKey: randAccess,
SecretKey: randSecret,
}
res, err := m.MinioClient.AddServiceAccount(context.Background(), req)
if err != nil {
return "", "", err
}
return res.AccessKey, res.SecretKey, nil
}
func getRandomCreds() (string, string){
opts := randomstring.GenerationOptions{
Length: 20,
}
a, _ := randomstring.GenerateString(opts)
opts.Length = 40
s, _ := randomstring.GenerateString(opts)
return a,s
}
func (m *MinioService) CreateBucket(executionId string) error {
return nil
}

View File

@ -1,203 +0,0 @@
package infrastructure
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"slices"
"strconv"
"sync"
"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/booking"
"cloud.o-forge.io/core/oc-lib/models/common/models"
"cloud.o-forge.io/core/oc-lib/models/live"
"github.com/gorilla/websocket"
)
type MetricsSnapshot struct {
From string `json:"origin"`
Metrics []Metric `json:"metrics"`
}
type Metric struct {
Name string `json:"name"`
Value float64 `json:"value"`
Error error `json:"error"`
}
type PrometheusResponse struct {
Status string `json:"status"`
Data struct {
ResultType string `json:"resultType"`
Result []struct {
Metric map[string]string `json:"metric"`
Value []interface{} `json:"value"` // [timestamp, value]
} `json:"result"`
} `json:"data"`
}
var queriesMetrics = []string{
"rate(container_cpu_usage_seconds_total{namespace=\"%s\"}[1m]) * 100",
"container_memory_usage_bytes{namespace=\"%s\"}",
"(container_fs_usage_bytes{namespace=\"%s\"}) / (container_fs_limit_bytes{namespace=\"%s\"}) * 100",
"DCGM_FI_DEV_GPU_UTIL{namespace=\"%s\"}",
// "system_load_average",
"rate(container_fs_reads_bytes_total{namespace=\"%s\"}[1m])",
"rate(container_fs_writes_bytes_total{namespace=\"%s\"}[1m])",
"rate(container_network_receive_bytes_total{namespace=\"%s\"}[1m])",
"rate(container_network_transmit_bytes_total{namespace=\"%s\"}[1m])",
// "system_network_latency_ms",
"rate(http_requests_total{namespace=\"%s\"}[1m])",
"(rate(http_requests_total{status=~\"5..\", namespace=\"%s\"}[1m]) / rate(http_requests_total{namespace=\"%s\"}[1m])) * 100",
// "app_mean_time_to_repair_seconds",
// "app_mean_time_between_failure_seconds",
}
type PrometheusService struct {
}
func NewPrometheusService() *PrometheusService {
return &PrometheusService{}
}
func (p *PrometheusService) queryPrometheus(promURL string, expr string, namespace string) models.Metric {
metric := models.Metric{Name: expr, Value: -1}
resp, err := http.Get(promURL + "/api/v1/query?query=" + url.QueryEscape(fmt.Sprintf(expr, namespace)))
if err != nil {
metric.Error = err
} else {
defer resp.Body.Close()
if body, err := io.ReadAll(resp.Body); err == nil {
var result PrometheusResponse
if err = json.Unmarshal(body, &result); err == nil && len(result.Data.Result) > 0 && len(result.Data.Result[0].Value) == 2 {
metric.Value, metric.Error = strconv.ParseFloat(fmt.Sprintf("%s", result.Data.Result[0].Value[1]), 64)
}
}
}
return metric
}
func (p *PrometheusService) Call(book *booking.Booking, user string, peerID string, groups []string) (*booking.Booking, map[string]models.MetricsSnapshot) {
var wg sync.WaitGroup
metrics := map[string]models.MetricsSnapshot{}
// get all booking... from executions_id == namespace typed datacenter.
cUAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.LIVE_DATACENTER), user, peerID, groups, nil)
cRAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_RESOURCE), user, peerID, groups, nil)
rr := cRAccess.LoadOne(book.ResourceID)
if rr.Err != "" {
fmt.Errorf("can't proceed because of unfound resource %s : %s", book.ResourceID, rr.Err)
return book, metrics
}
computeRes := rr.ToComputeResource()
for _, instance := range computeRes.Instances {
res := cUAccess.Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"source": {{Operator: dbs.EQUAL.String(), Value: instance.Source}},
"abstractlive.resources_id": {{Operator: dbs.EQUAL.String(), Value: computeRes.GetID()}},
},
}, "", false)
if res.Err != "" {
continue
}
for _, r := range res.Data {
// TODO watch out ... to not exec on an absent datacenter...
if r.(*live.LiveDatacenter).MonitorPath == "" {
continue
}
wg.Add(1)
snapshot := models.MetricsSnapshot{From: instance.Source, Metrics: []models.Metric{}}
go func() {
defer wg.Done()
for _, expr := range queriesMetrics {
snapshot.Metrics = append(snapshot.Metrics,
p.queryPrometheus(r.(*live.LiveDatacenter).MonitorPath, expr, book.ExecutionsID))
}
metrics[instance.Name] = snapshot
}()
}
}
wg.Wait()
return book, metrics
}
var LockKill = &sync.Mutex{}
// TODO kill procedure
func (p *PrometheusService) Stream(bookingID string, interval time.Duration, user string, peerID string, groups []string, websocket *websocket.Conn) {
max := 100
bookIDS := []string{}
mets := map[string][]models.MetricsSnapshot{}
bAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil)
book := bAccess.LoadOne(bookingID)
if book.Err != "" {
fmt.Errorf("stop because of empty : %s", book.Err)
}
f := func(e *booking.Booking) bool {
if e.ExpectedEndDate == nil {
return true
}
return time.Now().Before(*e.ExpectedEndDate)
}
for f(book.Data.(*booking.Booking)) {
if slices.Contains(Kill, book.Data.(*booking.Booking).ExecutionsID) {
newKill := []string{}
for _, k := range Kill {
if k != book.Data.(*booking.Booking).ExecutionsID {
newKill = append(newKill, k)
}
}
LockKill.Lock()
Kill = newKill
LockKill.Unlock()
break
}
go func() {
book, metrics := p.Call(book.Data.(*booking.Booking), user, peerID, groups)
for k, v := range metrics {
if me, ok := mets[k]; !ok {
mets[k] = []models.MetricsSnapshot{v}
} else {
me = append(me, v)
mets[k] = me
}
}
bookIDS = append(bookIDS, bookingID)
if websocket != nil {
(*websocket).WriteJSON(metrics)
}
if len(bookIDS) != max {
return
}
if book.ExecutionMetrics == nil {
book.ExecutionMetrics = mets
} else {
for kk, vv := range mets {
if em, ok := book.ExecutionMetrics[kk]; !ok {
book.ExecutionMetrics[kk] = vv
} else {
em = append(em, vv...)
book.ExecutionMetrics[kk] = em
}
}
}
book.GetAccessor(nil).UpdateOne(book, bookingID)
bookIDS = []string{}
}()
time.Sleep(interval)
}
}
var Kill = []string{}
// should add a datacenter... under juridiction... of opencloud...

19
main.go
View File

@ -4,12 +4,10 @@ import (
"encoding/base64"
"oc-datacenter/conf"
_ "oc-datacenter/routers"
"os"
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-datacenter"
@ -21,9 +19,9 @@ func main() {
// Load the right config file
o := oclib.GetConfLoader()
conf.GetConfig().Mode = o.GetStringDefault("MODE", "kubernetes")
conf.GetConfig().KubeHost = o.GetStringDefault("KUBERNETES_SERVICE_HOST", os.Getenv("KUBERNETES_SERVICE_HOST"))
conf.GetConfig().KubeHost = o.GetStringDefault("KUBERNETES_SERVICE_HOST", "")
conf.GetConfig().KubePort = o.GetStringDefault("KUBERNETES_SERVICE_PORT", "6443")
sDec, err := base64.StdEncoding.DecodeString(o.GetStringDefault("KUBE_CA", ""))
if err == nil {
conf.GetConfig().KubeCA = string(sDec)
@ -36,11 +34,6 @@ func main() {
if err == nil {
conf.GetConfig().KubeData = string(sDec)
}
conf.GetConfig().MinioRootKey = o.GetStringDefault("MINIO_ADMIN_ACCESS","")
conf.GetConfig().MinioRootSecret = o.GetStringDefault("MINIO_ADMIN_SECRET","")
// feed the library with the loaded config
oclib.SetConfig(
o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"),
@ -57,12 +50,6 @@ func main() {
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()
}

View File

@ -1,56 +0,0 @@
package models
// KubeConfigValue is a struct used to create a kubectl configuration YAML file.
type KubeConfigValue struct {
APIVersion string `yaml:"apiVersion" json:"apiVersion"`
Kind string `yaml:"kind" json:"kind"`
Clusters []KubeconfigNamedCluster `yaml:"clusters" json:"clusters"`
Users []KubeconfigUser `yaml:"users" json:"users"`
Contexts []KubeconfigNamedContext `yaml:"contexts" json:"contexts"`
CurrentContext string `yaml:"current-context" json:"current-context"`
Preferences struct{} `yaml:"preferences" json:"preferences"`
}
// KubeconfigUser is a struct used to create a kubectl configuration YAML file
type KubeconfigUser struct {
Name string `yaml:"name" json:"name"`
User KubeconfigUserKeyPair `yaml:"user" json:"user"`
}
// KubeconfigUserKeyPair is a struct used to create a kubectl configuration YAML file
type KubeconfigUserKeyPair struct {
Token string `yaml:"token" json:"token"`
}
// KubeconfigAuthProvider is a struct used to create a kubectl authentication provider
type KubeconfigAuthProvider struct {
Name string `yaml:"name" json:"name"`
Config map[string]string `yaml:"config" json:"config"`
}
// KubeconfigNamedCluster is a struct used to create a kubectl configuration YAML file
type KubeconfigNamedCluster struct {
Name string `yaml:"name" json:"name"`
Cluster KubeconfigCluster `yaml:"cluster" json:"cluster"`
}
// KubeconfigCluster is a struct used to create a kubectl configuration YAML file
type KubeconfigCluster struct {
Server string `yaml:"server" json:"server"`
CertificateAuthorityData string `yaml:"certificate-authority-data" json:"certificate-authority-data"`
CertificateAuthority string `yaml:"certificate-authority" json:"certificate-authority"`
}
// KubeconfigNamedContext is a struct used to create a kubectl configuration YAML file
type KubeconfigNamedContext struct {
Name string `yaml:"name" json:"name"`
Context KubeconfigContext `yaml:"context" json:"context"`
}
// KubeconfigContext is a struct used to create a kubectl configuration YAML file
type KubeconfigContext struct {
Cluster string `yaml:"cluster" json:"cluster"`
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
User string `yaml:"user" json:"user"`
}

Binary file not shown.

View File

@ -7,87 +7,6 @@ import (
func init() {
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "GetAdmiraltyKubeconfig",
Router: `/kubeconfig/: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: "GetNodeReady",
Router: `/node/: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: "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: "CreateKubeSecret",
Router: `/secret/:execution/:peer`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "CreateAdmiraltySource",
Router: `/source/:execution`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "CreateAdmiraltyTarget",
Router: `/target/:execution/:peer`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "GetAllTargets",
Router: `/targets`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "GetOneTarget",
Router: `/targets/:execution`,
AllowHTTPMethods: []string{"get"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:AdmiraltyController"],
beego.ControllerComments{
Method: "DeleteAdmiraltySession",
Router: `/targets/:execution`,
AllowHTTPMethods: []string{"delete"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "GetAll",
@ -115,15 +34,6 @@ func init() {
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:BookingController"],
beego.ControllerComments{
Method: "Log",
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",
@ -142,24 +52,6 @@ func init() {
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",
@ -196,15 +88,6 @@ func init() {
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:MinioController"],
beego.ControllerComments{
Method: "CreateServiceAccount",
Router: `/serviceaccount/:minioId/:executions`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Filters: nil,
Params: nil})
beego.GlobalControllerRouter["oc-datacenter/controllers:SessionController"] = append(beego.GlobalControllerRouter["oc-datacenter/controllers:SessionController"],
beego.ControllerComments{
Method: "GetToken",

View File

@ -18,7 +18,6 @@ func init() {
beego.NSInclude(
&controllers.DatacenterController{},
),
beego.NSNamespace("/session",
beego.NSInclude(
&controllers.SessionController{},
@ -34,16 +33,6 @@ func init() {
&controllers.VersionController{},
),
),
beego.NSNamespace("/admiralty",
beego.NSInclude(
&controllers.AdmiraltyController{},
),
),
beego.NSNamespace("/minio",
beego.NSInclude(
&controllers.MinioController{},
),
),
)
beego.AddNamespace(ns)

BIN
swagger/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

BIN
swagger/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

60
swagger/index.html Normal file
View File

@ -0,0 +1,60 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
// End Swagger UI call region
window.ui = ui;
};
</script>
</body>
</html>

View File

@ -0,0 +1,79 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};
isValid = qp.state === sentState;
if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
swagger/swagger-ui.css Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
swagger/swagger-ui.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

368
swagger/swagger.json Normal file
View File

@ -0,0 +1,368 @@
{
"swagger": "2.0",
"info": {
"title": "oc-datacenter",
"description": "Monitor owned datacenter activity\n",
"version": "1.0.0",
"termsOfService": "http://cloud.o-forge.io/",
"contact": {
"email": "admin@o-cloud.io"
},
"license": {
"name": "AGPL",
"url": "https://www.gnu.org/licenses/agpl-3.0.html"
}
},
"basePath": "/oc/",
"paths": {
"/": {
"get": {
"tags": [
"oc-datacenter/controllersDatacenterController"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "DatacenterController.GetAll",
"parameters": [
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
}
},
"/booking/": {
"get": {
"tags": [
"booking"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "BookingController.GetAll",
"parameters": [
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
},
"post": {
"tags": [
"booking"
],
"description": "create booking\n\u003cbr\u003e",
"operationId": "BookingController.Post.",
"parameters": [
{
"in": "body",
"name": "booking",
"description": "the booking you want to post",
"required": true,
"schema": {
"type": "string"
},
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/models.object"
}
}
}
}
},
"/booking/check/{id}/{start_date}/{end_date}": {
"get": {
"tags": [
"booking"
],
"description": "check booking\n\u003cbr\u003e",
"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/execution/{id}": {
"get": {
"tags": [
"booking"
],
"description": "search bookings by execution\n\u003cbr\u003e",
"operationId": "BookingController.Search",
"parameters": [
{
"in": "path",
"name": "id",
"description": "id execution",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
}
},
"/booking/search/{start_date}/{end_date}": {
"get": {
"tags": [
"booking"
],
"description": "search bookings\n\u003cbr\u003e",
"operationId": "BookingController.Search",
"parameters": [
{
"in": "path",
"name": "start_date",
"description": "the word search you want to get",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "end_date",
"description": "the word search you want to get",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{workspace} models.workspace"
}
}
}
},
"/booking/{id}": {
"get": {
"tags": [
"booking"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "BookingController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
},
"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}": {
"get": {
"tags": [
"session"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "SessionController.GetToken",
"parameters": [
{
"in": "path",
"name": "id",
"description": "id of the datacenter",
"type": "string"
},
{
"in": "path",
"name": "duration",
"description": "duration of the token",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
}
},
"/version/": {
"get": {
"tags": [
"version"
],
"description": "get version\n\u003cbr\u003e",
"operationId": "VersionController.GetAll",
"responses": {
"200": {
"description": ""
}
}
}
},
"/version/status": {
"get": {
"tags": [
"version"
],
"description": "get status\n\u003cbr\u003e",
"operationId": "VersionController.Status",
"responses": {
"200": {
"description": ""
}
}
}
},
"/{id}": {
"get": {
"tags": [
"oc-datacenter/controllersDatacenterController"
],
"description": "find booking by id\n\u003cbr\u003e",
"operationId": "DatacenterController.Get",
"parameters": [
{
"in": "path",
"name": "id",
"description": "the id you want to get",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "is_draft",
"description": "draft wished",
"type": "string"
}
],
"responses": {
"200": {
"description": "{booking} models.booking"
}
}
}
}
},
"definitions": {
"models.compute": {
"title": "compute",
"type": "object"
},
"models.object": {
"title": "object",
"type": "object"
}
},
"tags": [
{
"name": "oc-datacenter/controllersDatacenterController",
"description": "Operations about workspace\n"
},
{
"name": "booking",
"description": "Operations about workspace\n"
},
{
"name": "version",
"description": "VersionController operations for Version\n"
}
]
}

268
swagger/swagger.yml Normal file
View File

@ -0,0 +1,268 @@
swagger: "2.0"
info:
title: oc-datacenter
description: |
Monitor owned datacenter activity
version: 1.0.0
termsOfService: http://cloud.o-forge.io/
contact:
email: admin@o-cloud.io
license:
name: AGPL
url: https://www.gnu.org/licenses/agpl-3.0.html
basePath: /oc/
paths:
/:
get:
tags:
- oc-datacenter/controllersDatacenterController
description: |-
find booking by id
<br>
operationId: DatacenterController.GetAll
parameters:
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{booking} models.booking'
/{id}:
get:
tags:
- oc-datacenter/controllersDatacenterController
description: |-
find booking by id
<br>
operationId: DatacenterController.Get
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{booking} models.booking'
/booking/:
get:
tags:
- booking
description: |-
find booking by id
<br>
operationId: BookingController.GetAll
parameters:
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{booking} models.booking'
post:
tags:
- booking
description: |-
create booking
<br>
operationId: BookingController.Post.
parameters:
- in: body
name: booking
description: the booking you want to post
required: true
schema:
type: string
type: string
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: ""
schema:
$ref: '#/definitions/models.object'
/booking/{id}:
get:
tags:
- booking
description: |-
find booking by id
<br>
operationId: BookingController.Get
parameters:
- in: path
name: id
description: the id you want to get
required: true
type: string
responses:
"200":
description: '{booking} models.booking'
put:
tags:
- booking
description: |-
create computes
<br>
operationId: BookingController.Update
parameters:
- in: path
name: id
description: the compute id you want to get
required: true
type: string
- in: body
name: body
description: The compute content
required: true
schema:
$ref: '#/definitions/models.compute'
responses:
"200":
description: '{compute} models.compute'
/booking/check/{id}/{start_date}/{end_date}:
get:
tags:
- booking
description: |-
check booking
<br>
operationId: BookingController.Check
parameters:
- in: path
name: id
description: id of the datacenter
type: string
- in: path
name: start_date
description: 2006-01-02T15:04:05
type: string
default: the booking start date
- in: path
name: end_date
description: 2006-01-02T15:04:05
type: string
default: the booking end date
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: ""
schema:
$ref: '#/definitions/models.object'
/booking/search/{start_date}/{end_date}:
get:
tags:
- booking
description: |-
search bookings
<br>
operationId: BookingController.Search
parameters:
- in: path
name: start_date
description: the word search you want to get
required: true
type: string
- in: path
name: end_date
description: the word search you want to get
required: true
type: string
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{workspace} models.workspace'
/booking/search/execution/{id}:
get:
tags:
- booking
description: |-
search bookings by execution
<br>
operationId: BookingController.Search
parameters:
- in: path
name: id
description: id execution
required: true
type: string
- in: query
name: is_draft
description: draft wished
type: string
responses:
"200":
description: '{workspace} models.workspace'
/session/token/{id}/{duration}:
get:
tags:
- session
description: |-
find booking by id
<br>
operationId: SessionController.GetToken
parameters:
- in: path
name: id
description: id of the datacenter
type: string
- in: path
name: duration
description: duration of the token
type: string
responses:
"200":
description: '{booking} models.booking'
/version/:
get:
tags:
- version
description: |-
get version
<br>
operationId: VersionController.GetAll
responses:
"200":
description: ""
/version/status:
get:
tags:
- version
description: |-
get status
<br>
operationId: VersionController.Status
responses:
"200":
description: ""
definitions:
models.compute:
title: compute
type: object
models.object:
title: object
type: object
tags:
- name: oc-datacenter/controllersDatacenterController
description: |
Operations about workspace
- name: booking
description: |
Operations about workspace
- name: version
description: |
VersionController operations for Version